42969fa97fc7994b2f3f5aaba9c310b58b471f8c
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192          skip_children = false;
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 
214                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
215                 // and are not displayed -this causes this to use up the wrong element when matching.
216                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
217                 
218                 
219                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
220                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
221                   
222                   
223                   
224                     cn.el = echild;
225                   //  Roo.log("GOT");
226                     //echild.dom.removeAttribute('xtype');
227                 } else {
228                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229                     Roo.log(self_cntr_el);
230                     Roo.log(echild);
231                     Roo.log(cn);
232                 }
233             }
234            
235             
236            
237             // if object has flexy:if - then it may or may not be rendered.
238             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
239                 // skip a flexy if element.
240                 Roo.log('skipping render');
241                 Roo.log(tree);
242                 if (!cn.el) {
243                     Roo.log('skipping all children');
244                     skip_children = true;
245                 }
246                 
247              } else {
248                  
249                 // actually if flexy:foreach is found, we really want to create 
250                 // multiple copies here...
251                 //Roo.log('render');
252                 //Roo.log(this[cntr]());
253                 cn.render(this[cntr](true));
254              }
255             // then add the element..
256         }
257         
258         
259         // handle the kids..
260         
261         var nitems = [];
262         /*
263         if (typeof (tree.menu) != 'undefined') {
264             tree.menu.parentType = cn.xtype;
265             tree.menu.triggerEl = cn.el;
266             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
267             
268         }
269         */
270         if (!tree.items || !tree.items.length) {
271             cn.items = nitems;
272             return cn;
273         }
274         var items = tree.items;
275         delete tree.items;
276         
277         //Roo.log(items.length);
278             // add the items..
279         if (!skip_children) {    
280             for(var i =0;i < items.length;i++) {
281                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
282             }
283         }
284         
285         cn.items = nitems;
286         
287         return cn;
288     }
289     
290     
291     
292     
293 });
294
295  /*
296  * - LGPL
297  *
298  * Body
299  * 
300  */
301
302 /**
303  * @class Roo.bootstrap.Body
304  * @extends Roo.bootstrap.Component
305  * Bootstrap Body class
306  * 
307  * @constructor
308  * Create a new body
309  * @param {Object} config The config object
310  */
311
312 Roo.bootstrap.Body = function(config){
313     Roo.bootstrap.Body.superclass.constructor.call(this, config);
314     this.el = Roo.get(document.body);
315     if (this.cls && this.cls.length) {
316         Roo.get(document.body).addClass(this.cls);
317     }
318 };
319
320 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
321       
322         autoCreate : {
323         cls: 'container'
324     },
325     onRender : function(ct, position)
326     {
327        /* Roo.log("Roo.bootstrap.Body - onRender");
328         if (this.cls && this.cls.length) {
329             Roo.get(document.body).addClass(this.cls);
330         }
331         // style??? xttr???
332         */
333     }
334     
335     
336  
337    
338 });
339
340  /*
341  * - LGPL
342  *
343  * button group
344  * 
345  */
346
347
348 /**
349  * @class Roo.bootstrap.ButtonGroup
350  * @extends Roo.bootstrap.Component
351  * Bootstrap ButtonGroup class
352  * @cfg {String} size lg | sm | xs (default empty normal)
353  * @cfg {String} align vertical | justified  (default none)
354  * @cfg {String} direction up | down (default down)
355  * @cfg {Boolean} toolbar false | true
356  * @cfg {Boolean} btn true | false
357  * 
358  * 
359  * @constructor
360  * Create a new Input
361  * @param {Object} config The config object
362  */
363
364 Roo.bootstrap.ButtonGroup = function(config){
365     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
366 };
367
368 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
369     
370     size: '',
371     align: '',
372     direction: '',
373     toolbar: false,
374     btn: true,
375
376     getAutoCreate : function(){
377         var cfg = {
378             cls: 'btn-group',
379             html : null
380         }
381         
382         cfg.html = this.html || cfg.html;
383         
384         if (this.toolbar) {
385             cfg = {
386                 cls: 'btn-toolbar',
387                 html: null
388             }
389             
390             return cfg;
391         }
392         
393         if (['vertical','justified'].indexOf(this.align)!==-1) {
394             cfg.cls = 'btn-group-' + this.align;
395             
396             if (this.align == 'justified') {
397                 console.log(this.items);
398             }
399         }
400         
401         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
402             cfg.cls += ' btn-group-' + this.size;
403         }
404         
405         if (this.direction == 'up') {
406             cfg.cls += ' dropup' ;
407         }
408         
409         return cfg;
410     }
411    
412 });
413
414  /*
415  * - LGPL
416  *
417  * button
418  * 
419  */
420
421 /**
422  * @class Roo.bootstrap.Button
423  * @extends Roo.bootstrap.Component
424  * Bootstrap Button class
425  * @cfg {String} html The button content
426  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
427  * @cfg {String} size empty | lg | sm | xs
428  * @cfg {String} tag empty | a | input | submit
429  * @cfg {String} href empty or href
430  * @cfg {Boolean} disabled false | true
431  * @cfg {Boolean} isClose false | true
432  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
433  * @cfg {String} badge text for badge
434  * @cfg {String} theme default (or empty) | glow
435  * @cfg {Boolean} inverse false | true
436  * @cfg {Boolean} toggle false | true
437  * @cfg {String} ontext text for on toggle state
438  * @cfg {String} offtext text for off toggle state
439  * @cfg {Boolean} defaulton true | false
440  * @cfg {Boolean} preventDefault (true | false) default true
441  * @cfg {Boolean} removeClass true | false remove the standard class..
442  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
443  * 
444  * @constructor
445  * Create a new button
446  * @param {Object} config The config object
447  */
448
449
450 Roo.bootstrap.Button = function(config){
451     Roo.bootstrap.Button.superclass.constructor.call(this, config);
452     this.addEvents({
453         // raw events
454         /**
455          * @event click
456          * When a butotn is pressed
457          * @param {Roo.EventObject} e
458          */
459         "click" : true,
460          /**
461          * @event toggle
462          * After the button has been toggles
463          * @param {Roo.EventObject} e
464          * @param {boolean} pressed (also available as button.pressed)
465          */
466         "toggle" : true
467     });
468 };
469
470 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
471     html: false,
472     active: false,
473     weight: '',
474     size: '',
475     tag: 'button',
476     href: '',
477     disabled: false,
478     isClose: false,
479     glyphicon: '',
480     badge: '',
481     theme: 'default',
482     inverse: false,
483     
484     toggle: false,
485     ontext: 'ON',
486     offtext: 'OFF',
487     defaulton: true,
488     preventDefault: true,
489     removeClass: false,
490     name: false,
491     target: false,
492     
493     
494     pressed : null,
495      
496     
497     getAutoCreate : function(){
498         
499         var cfg = {
500             tag : 'button',
501             cls : 'roo-button',
502             html: ''
503         };
504         
505         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
506             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
507             this.tag = 'button';
508         } else {
509             cfg.tag = this.tag;
510         }
511         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
512         
513         if (this.toggle == true) {
514             cfg={
515                 tag: 'div',
516                 cls: 'slider-frame roo-button',
517                 cn: [
518                     {
519                         tag: 'span',
520                         'data-on-text':'ON',
521                         'data-off-text':'OFF',
522                         cls: 'slider-button',
523                         html: this.offtext
524                     }
525                 ]
526             };
527             
528             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
529                 cfg.cls += ' '+this.weight;
530             }
531             
532             return cfg;
533         }
534         
535         if (this.isClose) {
536             cfg.cls += ' close';
537             
538             cfg["aria-hidden"] = true;
539             
540             cfg.html = "&times;";
541             
542             return cfg;
543         }
544         
545          
546         if (this.theme==='default') {
547             cfg.cls = 'btn roo-button';
548             
549             //if (this.parentType != 'Navbar') {
550             this.weight = this.weight.length ?  this.weight : 'default';
551             //}
552             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
553                 
554                 cfg.cls += ' btn-' + this.weight;
555             }
556         } else if (this.theme==='glow') {
557             
558             cfg.tag = 'a';
559             cfg.cls = 'btn-glow roo-button';
560             
561             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
562                 
563                 cfg.cls += ' ' + this.weight;
564             }
565         }
566    
567         
568         if (this.inverse) {
569             this.cls += ' inverse';
570         }
571         
572         
573         if (this.active) {
574             cfg.cls += ' active';
575         }
576         
577         if (this.disabled) {
578             cfg.disabled = 'disabled';
579         }
580         
581         if (this.items) {
582             Roo.log('changing to ul' );
583             cfg.tag = 'ul';
584             this.glyphicon = 'caret';
585         }
586         
587         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
588          
589         //gsRoo.log(this.parentType);
590         if (this.parentType === 'Navbar' && !this.parent().bar) {
591             Roo.log('changing to li?');
592             
593             cfg.tag = 'li';
594             
595             cfg.cls = '';
596             cfg.cn =  [{
597                 tag : 'a',
598                 cls : 'roo-button',
599                 html : this.html,
600                 href : this.href || '#'
601             }];
602             if (this.menu) {
603                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
604                 cfg.cls += ' dropdown';
605             }   
606             
607             delete cfg.html;
608             
609         }
610         
611        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
612         
613         if (this.glyphicon) {
614             cfg.html = ' ' + cfg.html;
615             
616             cfg.cn = [
617                 {
618                     tag: 'span',
619                     cls: 'glyphicon glyphicon-' + this.glyphicon
620                 }
621             ];
622         }
623         
624         if (this.badge) {
625             cfg.html += ' ';
626             
627             cfg.tag = 'a';
628             
629 //            cfg.cls='btn roo-button';
630             
631             cfg.href=this.href;
632             
633             var value = cfg.html;
634             
635             if(this.glyphicon){
636                 value = {
637                             tag: 'span',
638                             cls: 'glyphicon glyphicon-' + this.glyphicon,
639                             html: this.html
640                         };
641                 
642             }
643             
644             cfg.cn = [
645                 value,
646                 {
647                     tag: 'span',
648                     cls: 'badge',
649                     html: this.badge
650                 }
651             ];
652             
653             cfg.html='';
654         }
655         
656         if (this.menu) {
657             cfg.cls += ' dropdown';
658             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
659         }
660         
661         if (cfg.tag !== 'a' && this.href !== '') {
662             throw "Tag must be a to set href.";
663         } else if (this.href.length > 0) {
664             cfg.href = this.href;
665         }
666         
667         if(this.removeClass){
668             cfg.cls = '';
669         }
670         
671         if(this.target){
672             cfg.target = this.target;
673         }
674         
675         return cfg;
676     },
677     initEvents: function() {
678        // Roo.log('init events?');
679 //        Roo.log(this.el.dom);
680         // add the menu...
681         
682         if (typeof (this.menu) != 'undefined') {
683             this.menu.parentType = this.xtype;
684             this.menu.triggerEl = this.el;
685             this.addxtype(Roo.apply({}, this.menu));
686         }
687
688
689        if (this.el.hasClass('roo-button')) {
690             this.el.on('click', this.onClick, this);
691        } else {
692             this.el.select('.roo-button').on('click', this.onClick, this);
693        }
694        
695        if(this.removeClass){
696            this.el.on('click', this.onClick, this);
697        }
698        
699        this.el.enableDisplayMode();
700         
701     },
702     onClick : function(e)
703     {
704         if (this.disabled) {
705             return;
706         }
707         
708         Roo.log('button on click ');
709         if(this.preventDefault){
710             e.preventDefault();
711         }
712         if (this.pressed === true || this.pressed === false) {
713             this.pressed = !this.pressed;
714             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
715             this.fireEvent('toggle', this, e, this.pressed);
716         }
717         
718         
719         this.fireEvent('click', this, e);
720     },
721     
722     /**
723      * Enables this button
724      */
725     enable : function()
726     {
727         this.disabled = false;
728         this.el.removeClass('disabled');
729     },
730     
731     /**
732      * Disable this button
733      */
734     disable : function()
735     {
736         this.disabled = true;
737         this.el.addClass('disabled');
738     },
739      /**
740      * sets the active state on/off, 
741      * @param {Boolean} state (optional) Force a particular state
742      */
743     setActive : function(v) {
744         
745         this.el[v ? 'addClass' : 'removeClass']('active');
746     },
747      /**
748      * toggles the current active state 
749      */
750     toggleActive : function()
751     {
752        var active = this.el.hasClass('active');
753        this.setActive(!active);
754        
755         
756     },
757     setText : function(str)
758     {
759         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
760     },
761     getText : function()
762     {
763         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
764     },
765     hide: function() {
766        
767      
768         this.el.hide();   
769     },
770     show: function() {
771        
772         this.el.show();   
773     }
774     
775     
776 });
777
778  /*
779  * - LGPL
780  *
781  * column
782  * 
783  */
784
785 /**
786  * @class Roo.bootstrap.Column
787  * @extends Roo.bootstrap.Component
788  * Bootstrap Column class
789  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
790  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
791  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
792  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
793  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
794  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
795  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
796  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
797  *
798  * 
799  * @cfg {Boolean} hidden (true|false) hide the element
800  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
801  * @cfg {String} fa (ban|check|...) font awesome icon
802  * @cfg {Number} fasize (1|2|....) font awsome size
803
804  * @cfg {String} icon (info-sign|check|...) glyphicon name
805
806  * @cfg {String} html content of column.
807  * 
808  * @constructor
809  * Create a new Column
810  * @param {Object} config The config object
811  */
812
813 Roo.bootstrap.Column = function(config){
814     Roo.bootstrap.Column.superclass.constructor.call(this, config);
815 };
816
817 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
818     
819     xs: false,
820     sm: false,
821     md: false,
822     lg: false,
823     xsoff: false,
824     smoff: false,
825     mdoff: false,
826     lgoff: false,
827     html: '',
828     offset: 0,
829     alert: false,
830     fa: false,
831     icon : false,
832     hidden : false,
833     fasize : 1,
834     
835     getAutoCreate : function(){
836         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
837         
838         cfg = {
839             tag: 'div',
840             cls: 'column'
841         };
842         
843         var settings=this;
844         ['xs','sm','md','lg'].map(function(size){
845             //Roo.log( size + ':' + settings[size]);
846             
847             if (settings[size+'off'] !== false) {
848                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
849             }
850             
851             if (settings[size] === false) {
852                 return;
853             }
854             Roo.log(settings[size]);
855             if (!settings[size]) { // 0 = hidden
856                 cfg.cls += ' hidden-' + size;
857                 return;
858             }
859             cfg.cls += ' col-' + size + '-' + settings[size];
860             
861         });
862         
863         if (this.hidden) {
864             cfg.cls += ' hidden';
865         }
866         
867         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
868             cfg.cls +=' alert alert-' + this.alert;
869         }
870         
871         
872         if (this.html.length) {
873             cfg.html = this.html;
874         }
875         if (this.fa) {
876             var fasize = '';
877             if (this.fasize > 1) {
878                 fasize = ' fa-' + this.fasize + 'x';
879             }
880             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
881             
882             
883         }
884         if (this.icon) {
885             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
886         }
887         
888         return cfg;
889     }
890    
891 });
892
893  
894
895  /*
896  * - LGPL
897  *
898  * page container.
899  * 
900  */
901
902
903 /**
904  * @class Roo.bootstrap.Container
905  * @extends Roo.bootstrap.Component
906  * Bootstrap Container class
907  * @cfg {Boolean} jumbotron is it a jumbotron element
908  * @cfg {String} html content of element
909  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
910  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
911  * @cfg {String} header content of header (for panel)
912  * @cfg {String} footer content of footer (for panel)
913  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
914  * @cfg {String} tag (header|aside|section) type of HTML tag.
915  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
916  * @cfg {String} fa (ban|check|...) font awesome icon
917  * @cfg {String} icon (info-sign|check|...) glyphicon name
918  * @cfg {Boolean} hidden (true|false) hide the element
919
920  *     
921  * @constructor
922  * Create a new Container
923  * @param {Object} config The config object
924  */
925
926 Roo.bootstrap.Container = function(config){
927     Roo.bootstrap.Container.superclass.constructor.call(this, config);
928 };
929
930 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
931     
932     jumbotron : false,
933     well: '',
934     panel : '',
935     header: '',
936     footer : '',
937     sticky: '',
938     tag : false,
939     alert : false,
940     fa: false,
941     icon : false,
942   
943      
944     getChildContainer : function() {
945         
946         if(!this.el){
947             return false;
948         }
949         
950         if (this.panel.length) {
951             return this.el.select('.panel-body',true).first();
952         }
953         
954         return this.el;
955     },
956     
957     
958     getAutoCreate : function(){
959         
960         var cfg = {
961             tag : this.tag || 'div',
962             html : '',
963             cls : ''
964         };
965         if (this.jumbotron) {
966             cfg.cls = 'jumbotron';
967         }
968         
969         
970         
971         // - this is applied by the parent..
972         //if (this.cls) {
973         //    cfg.cls = this.cls + '';
974         //}
975         
976         if (this.sticky.length) {
977             
978             var bd = Roo.get(document.body);
979             if (!bd.hasClass('bootstrap-sticky')) {
980                 bd.addClass('bootstrap-sticky');
981                 Roo.select('html',true).setStyle('height', '100%');
982             }
983              
984             cfg.cls += 'bootstrap-sticky-' + this.sticky;
985         }
986         
987         
988         if (this.well.length) {
989             switch (this.well) {
990                 case 'lg':
991                 case 'sm':
992                     cfg.cls +=' well well-' +this.well;
993                     break;
994                 default:
995                     cfg.cls +=' well';
996                     break;
997             }
998         }
999         
1000         if (this.hidden) {
1001             cfg.cls += ' hidden';
1002         }
1003         
1004         
1005         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1006             cfg.cls +=' alert alert-' + this.alert;
1007         }
1008         
1009         var body = cfg;
1010         
1011         if (this.panel.length) {
1012             cfg.cls += ' panel panel-' + this.panel;
1013             cfg.cn = [];
1014             if (this.header.length) {
1015                 cfg.cn.push({
1016                     
1017                     cls : 'panel-heading',
1018                     cn : [{
1019                         tag: 'h3',
1020                         cls : 'panel-title',
1021                         html : this.header
1022                     }]
1023                     
1024                 });
1025             }
1026             body = false;
1027             cfg.cn.push({
1028                 cls : 'panel-body',
1029                 html : this.html
1030             });
1031             
1032             
1033             if (this.footer.length) {
1034                 cfg.cn.push({
1035                     cls : 'panel-footer',
1036                     html : this.footer
1037                     
1038                 });
1039             }
1040             
1041         }
1042         
1043         if (body) {
1044             body.html = this.html || cfg.html;
1045             // prefix with the icons..
1046             if (this.fa) {
1047                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1048             }
1049             if (this.icon) {
1050                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1051             }
1052             
1053             
1054         }
1055         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1056             cfg.cls =  'container';
1057         }
1058         
1059         return cfg;
1060     },
1061     
1062     titleEl : function()
1063     {
1064         if(!this.el || !this.panel.length || !this.header.length){
1065             return;
1066         }
1067         
1068         return this.el.select('.panel-title',true).first();
1069     },
1070     
1071     setTitle : function(v)
1072     {
1073         var titleEl = this.titleEl();
1074         
1075         if(!titleEl){
1076             return;
1077         }
1078         
1079         titleEl.dom.innerHTML = v;
1080     },
1081     
1082     getTitle : function()
1083     {
1084         
1085         var titleEl = this.titleEl();
1086         
1087         if(!titleEl){
1088             return '';
1089         }
1090         
1091         return titleEl.dom.innerHTML;
1092     }
1093    
1094 });
1095
1096  /*
1097  * - LGPL
1098  *
1099  * image
1100  * 
1101  */
1102
1103
1104 /**
1105  * @class Roo.bootstrap.Img
1106  * @extends Roo.bootstrap.Component
1107  * Bootstrap Img class
1108  * @cfg {Boolean} imgResponsive false | true
1109  * @cfg {String} border rounded | circle | thumbnail
1110  * @cfg {String} src image source
1111  * @cfg {String} alt image alternative text
1112  * @cfg {String} href a tag href
1113  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1114  * 
1115  * @constructor
1116  * Create a new Input
1117  * @param {Object} config The config object
1118  */
1119
1120 Roo.bootstrap.Img = function(config){
1121     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1122     
1123     this.addEvents({
1124         // img events
1125         /**
1126          * @event click
1127          * The img click event for the img.
1128          * @param {Roo.EventObject} e
1129          */
1130         "click" : true
1131     });
1132 };
1133
1134 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1135     
1136     imgResponsive: true,
1137     border: '',
1138     src: '',
1139     href: false,
1140     target: false,
1141
1142     getAutoCreate : function(){
1143         
1144         var cfg = {
1145             tag: 'img',
1146             cls: (this.imgResponsive) ? 'img-responsive' : '',
1147             html : null
1148         }
1149         
1150         cfg.html = this.html || cfg.html;
1151         
1152         cfg.src = this.src || cfg.src;
1153         
1154         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1155             cfg.cls += ' img-' + this.border;
1156         }
1157         
1158         if(this.alt){
1159             cfg.alt = this.alt;
1160         }
1161         
1162         if(this.href){
1163             var a = {
1164                 tag: 'a',
1165                 href: this.href,
1166                 cn: [
1167                     cfg
1168                 ]
1169             }
1170             
1171             if(this.target){
1172                 a.target = this.target;
1173             }
1174             
1175         }
1176         
1177         
1178         return (this.href) ? a : cfg;
1179     },
1180     
1181     initEvents: function() {
1182         
1183         if(!this.href){
1184             this.el.on('click', this.onClick, this);
1185         }
1186     },
1187     
1188     onClick : function(e)
1189     {
1190         Roo.log('img onclick');
1191         this.fireEvent('click', this, e);
1192     }
1193    
1194 });
1195
1196  /*
1197  * - LGPL
1198  *
1199  * image
1200  * 
1201  */
1202
1203
1204 /**
1205  * @class Roo.bootstrap.Link
1206  * @extends Roo.bootstrap.Component
1207  * Bootstrap Link Class
1208  * @cfg {String} alt image alternative text
1209  * @cfg {String} href a tag href
1210  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1211  * @cfg {String} html the content of the link.
1212  * @cfg {String} anchor name for the anchor link
1213
1214  * @cfg {Boolean} preventDefault (true | false) default false
1215
1216  * 
1217  * @constructor
1218  * Create a new Input
1219  * @param {Object} config The config object
1220  */
1221
1222 Roo.bootstrap.Link = function(config){
1223     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1224     
1225     this.addEvents({
1226         // img events
1227         /**
1228          * @event click
1229          * The img click event for the img.
1230          * @param {Roo.EventObject} e
1231          */
1232         "click" : true
1233     });
1234 };
1235
1236 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1237     
1238     href: false,
1239     target: false,
1240     preventDefault: false,
1241     anchor : false,
1242     alt : false,
1243
1244     getAutoCreate : function()
1245     {
1246         
1247         var cfg = {
1248             tag: 'a'
1249         };
1250         // anchor's do not require html/href...
1251         if (this.anchor === false) {
1252             cfg.html = this.html || 'html-missing';
1253             cfg.href = this.href || '#';
1254         } else {
1255             cfg.name = this.anchor;
1256             if (this.html !== false) {
1257                 cfg.html = this.html;
1258             }
1259             if (this.href !== false) {
1260                 cfg.href = this.href;
1261             }
1262         }
1263         
1264         if(this.alt !== false){
1265             cfg.alt = this.alt;
1266         }
1267         
1268         
1269         if(this.target !== false) {
1270             cfg.target = this.target;
1271         }
1272         
1273         return cfg;
1274     },
1275     
1276     initEvents: function() {
1277         
1278         if(!this.href || this.preventDefault){
1279             this.el.on('click', this.onClick, this);
1280         }
1281     },
1282     
1283     onClick : function(e)
1284     {
1285         if(this.preventDefault){
1286             e.preventDefault();
1287         }
1288         //Roo.log('img onclick');
1289         this.fireEvent('click', this, e);
1290     }
1291    
1292 });
1293
1294  /*
1295  * - LGPL
1296  *
1297  * header
1298  * 
1299  */
1300
1301 /**
1302  * @class Roo.bootstrap.Header
1303  * @extends Roo.bootstrap.Component
1304  * Bootstrap Header class
1305  * @cfg {String} html content of header
1306  * @cfg {Number} level (1|2|3|4|5|6) default 1
1307  * 
1308  * @constructor
1309  * Create a new Header
1310  * @param {Object} config The config object
1311  */
1312
1313
1314 Roo.bootstrap.Header  = function(config){
1315     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1316 };
1317
1318 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1319     
1320     //href : false,
1321     html : false,
1322     level : 1,
1323     
1324     
1325     
1326     getAutoCreate : function(){
1327         
1328         var cfg = {
1329             tag: 'h' + (1 *this.level),
1330             html: this.html || 'fill in html'
1331         } ;
1332         
1333         return cfg;
1334     }
1335    
1336 });
1337
1338  
1339
1340  /*
1341  * Based on:
1342  * Ext JS Library 1.1.1
1343  * Copyright(c) 2006-2007, Ext JS, LLC.
1344  *
1345  * Originally Released Under LGPL - original licence link has changed is not relivant.
1346  *
1347  * Fork - LGPL
1348  * <script type="text/javascript">
1349  */
1350  
1351 /**
1352  * @class Roo.bootstrap.MenuMgr
1353  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1354  * @singleton
1355  */
1356 Roo.bootstrap.MenuMgr = function(){
1357    var menus, active, groups = {}, attached = false, lastShow = new Date();
1358
1359    // private - called when first menu is created
1360    function init(){
1361        menus = {};
1362        active = new Roo.util.MixedCollection();
1363        Roo.get(document).addKeyListener(27, function(){
1364            if(active.length > 0){
1365                hideAll();
1366            }
1367        });
1368    }
1369
1370    // private
1371    function hideAll(){
1372        if(active && active.length > 0){
1373            var c = active.clone();
1374            c.each(function(m){
1375                m.hide();
1376            });
1377        }
1378    }
1379
1380    // private
1381    function onHide(m){
1382        active.remove(m);
1383        if(active.length < 1){
1384            Roo.get(document).un("mouseup", onMouseDown);
1385             
1386            attached = false;
1387        }
1388    }
1389
1390    // private
1391    function onShow(m){
1392        var last = active.last();
1393        lastShow = new Date();
1394        active.add(m);
1395        if(!attached){
1396           Roo.get(document).on("mouseup", onMouseDown);
1397            
1398            attached = true;
1399        }
1400        if(m.parentMenu){
1401           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1402           m.parentMenu.activeChild = m;
1403        }else if(last && last.isVisible()){
1404           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1405        }
1406    }
1407
1408    // private
1409    function onBeforeHide(m){
1410        if(m.activeChild){
1411            m.activeChild.hide();
1412        }
1413        if(m.autoHideTimer){
1414            clearTimeout(m.autoHideTimer);
1415            delete m.autoHideTimer;
1416        }
1417    }
1418
1419    // private
1420    function onBeforeShow(m){
1421        var pm = m.parentMenu;
1422        if(!pm && !m.allowOtherMenus){
1423            hideAll();
1424        }else if(pm && pm.activeChild && active != m){
1425            pm.activeChild.hide();
1426        }
1427    }
1428
1429    // private
1430    function onMouseDown(e){
1431         Roo.log("on MouseDown");
1432         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1433            hideAll();
1434         }
1435         
1436         
1437    }
1438
1439    // private
1440    function onBeforeCheck(mi, state){
1441        if(state){
1442            var g = groups[mi.group];
1443            for(var i = 0, l = g.length; i < l; i++){
1444                if(g[i] != mi){
1445                    g[i].setChecked(false);
1446                }
1447            }
1448        }
1449    }
1450
1451    return {
1452
1453        /**
1454         * Hides all menus that are currently visible
1455         */
1456        hideAll : function(){
1457             hideAll();  
1458        },
1459
1460        // private
1461        register : function(menu){
1462            if(!menus){
1463                init();
1464            }
1465            menus[menu.id] = menu;
1466            menu.on("beforehide", onBeforeHide);
1467            menu.on("hide", onHide);
1468            menu.on("beforeshow", onBeforeShow);
1469            menu.on("show", onShow);
1470            var g = menu.group;
1471            if(g && menu.events["checkchange"]){
1472                if(!groups[g]){
1473                    groups[g] = [];
1474                }
1475                groups[g].push(menu);
1476                menu.on("checkchange", onCheck);
1477            }
1478        },
1479
1480         /**
1481          * Returns a {@link Roo.menu.Menu} object
1482          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1483          * be used to generate and return a new Menu instance.
1484          */
1485        get : function(menu){
1486            if(typeof menu == "string"){ // menu id
1487                return menus[menu];
1488            }else if(menu.events){  // menu instance
1489                return menu;
1490            }
1491            /*else if(typeof menu.length == 'number'){ // array of menu items?
1492                return new Roo.bootstrap.Menu({items:menu});
1493            }else{ // otherwise, must be a config
1494                return new Roo.bootstrap.Menu(menu);
1495            }
1496            */
1497            return false;
1498        },
1499
1500        // private
1501        unregister : function(menu){
1502            delete menus[menu.id];
1503            menu.un("beforehide", onBeforeHide);
1504            menu.un("hide", onHide);
1505            menu.un("beforeshow", onBeforeShow);
1506            menu.un("show", onShow);
1507            var g = menu.group;
1508            if(g && menu.events["checkchange"]){
1509                groups[g].remove(menu);
1510                menu.un("checkchange", onCheck);
1511            }
1512        },
1513
1514        // private
1515        registerCheckable : function(menuItem){
1516            var g = menuItem.group;
1517            if(g){
1518                if(!groups[g]){
1519                    groups[g] = [];
1520                }
1521                groups[g].push(menuItem);
1522                menuItem.on("beforecheckchange", onBeforeCheck);
1523            }
1524        },
1525
1526        // private
1527        unregisterCheckable : function(menuItem){
1528            var g = menuItem.group;
1529            if(g){
1530                groups[g].remove(menuItem);
1531                menuItem.un("beforecheckchange", onBeforeCheck);
1532            }
1533        }
1534    };
1535 }();/*
1536  * - LGPL
1537  *
1538  * menu
1539  * 
1540  */
1541
1542 /**
1543  * @class Roo.bootstrap.Menu
1544  * @extends Roo.bootstrap.Component
1545  * Bootstrap Menu class - container for MenuItems
1546  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1547  * 
1548  * @constructor
1549  * Create a new Menu
1550  * @param {Object} config The config object
1551  */
1552
1553
1554 Roo.bootstrap.Menu = function(config){
1555     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1556     if (this.registerMenu) {
1557         Roo.bootstrap.MenuMgr.register(this);
1558     }
1559     this.addEvents({
1560         /**
1561          * @event beforeshow
1562          * Fires before this menu is displayed
1563          * @param {Roo.menu.Menu} this
1564          */
1565         beforeshow : true,
1566         /**
1567          * @event beforehide
1568          * Fires before this menu is hidden
1569          * @param {Roo.menu.Menu} this
1570          */
1571         beforehide : true,
1572         /**
1573          * @event show
1574          * Fires after this menu is displayed
1575          * @param {Roo.menu.Menu} this
1576          */
1577         show : true,
1578         /**
1579          * @event hide
1580          * Fires after this menu is hidden
1581          * @param {Roo.menu.Menu} this
1582          */
1583         hide : true,
1584         /**
1585          * @event click
1586          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1587          * @param {Roo.menu.Menu} this
1588          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1589          * @param {Roo.EventObject} e
1590          */
1591         click : true,
1592         /**
1593          * @event mouseover
1594          * Fires when the mouse is hovering over this menu
1595          * @param {Roo.menu.Menu} this
1596          * @param {Roo.EventObject} e
1597          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1598          */
1599         mouseover : true,
1600         /**
1601          * @event mouseout
1602          * Fires when the mouse exits this menu
1603          * @param {Roo.menu.Menu} this
1604          * @param {Roo.EventObject} e
1605          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1606          */
1607         mouseout : true,
1608         /**
1609          * @event itemclick
1610          * Fires when a menu item contained in this menu is clicked
1611          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1612          * @param {Roo.EventObject} e
1613          */
1614         itemclick: true
1615     });
1616     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1617 };
1618
1619 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1620     
1621    /// html : false,
1622     //align : '',
1623     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1624     type: false,
1625     /**
1626      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1627      */
1628     registerMenu : true,
1629     
1630     menuItems :false, // stores the menu items..
1631     
1632     hidden:true,
1633     
1634     parentMenu : false,
1635     
1636     getChildContainer : function() {
1637         return this.el;  
1638     },
1639     
1640     getAutoCreate : function(){
1641          
1642         //if (['right'].indexOf(this.align)!==-1) {
1643         //    cfg.cn[1].cls += ' pull-right'
1644         //}
1645         
1646         
1647         var cfg = {
1648             tag : 'ul',
1649             cls : 'dropdown-menu' ,
1650             style : 'z-index:1000'
1651             
1652         }
1653         
1654         if (this.type === 'submenu') {
1655             cfg.cls = 'submenu active';
1656         }
1657         if (this.type === 'treeview') {
1658             cfg.cls = 'treeview-menu';
1659         }
1660         
1661         return cfg;
1662     },
1663     initEvents : function() {
1664         
1665        // Roo.log("ADD event");
1666        // Roo.log(this.triggerEl.dom);
1667         this.triggerEl.on('click', this.onTriggerPress, this);
1668         this.triggerEl.addClass('dropdown-toggle');
1669         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1670
1671         this.el.on("mouseover", this.onMouseOver, this);
1672         this.el.on("mouseout", this.onMouseOut, this);
1673         
1674         
1675     },
1676     findTargetItem : function(e){
1677         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1678         if(!t){
1679             return false;
1680         }
1681         //Roo.log(t);         Roo.log(t.id);
1682         if(t && t.id){
1683             //Roo.log(this.menuitems);
1684             return this.menuitems.get(t.id);
1685             
1686             //return this.items.get(t.menuItemId);
1687         }
1688         
1689         return false;
1690     },
1691     onClick : function(e){
1692         Roo.log("menu.onClick");
1693         var t = this.findTargetItem(e);
1694         if(!t){
1695             return;
1696         }
1697         Roo.log(e);
1698         /*
1699         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1700             if(t == this.activeItem && t.shouldDeactivate(e)){
1701                 this.activeItem.deactivate();
1702                 delete this.activeItem;
1703                 return;
1704             }
1705             if(t.canActivate){
1706                 this.setActiveItem(t, true);
1707             }
1708             return;
1709             
1710             
1711         }
1712         */
1713         Roo.log('pass click event');
1714         
1715         t.onClick(e);
1716         
1717         this.fireEvent("click", this, t, e);
1718         
1719         this.hide();
1720     },
1721      onMouseOver : function(e){
1722         var t  = this.findTargetItem(e);
1723         //Roo.log(t);
1724         //if(t){
1725         //    if(t.canActivate && !t.disabled){
1726         //        this.setActiveItem(t, true);
1727         //    }
1728         //}
1729         
1730         this.fireEvent("mouseover", this, e, t);
1731     },
1732     isVisible : function(){
1733         return !this.hidden;
1734     },
1735      onMouseOut : function(e){
1736         var t  = this.findTargetItem(e);
1737         
1738         //if(t ){
1739         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1740         //        this.activeItem.deactivate();
1741         //        delete this.activeItem;
1742         //    }
1743         //}
1744         this.fireEvent("mouseout", this, e, t);
1745     },
1746     
1747     
1748     /**
1749      * Displays this menu relative to another element
1750      * @param {String/HTMLElement/Roo.Element} element The element to align to
1751      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1752      * the element (defaults to this.defaultAlign)
1753      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1754      */
1755     show : function(el, pos, parentMenu){
1756         this.parentMenu = parentMenu;
1757         if(!this.el){
1758             this.render();
1759         }
1760         this.fireEvent("beforeshow", this);
1761         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1762     },
1763      /**
1764      * Displays this menu at a specific xy position
1765      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1766      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1767      */
1768     showAt : function(xy, parentMenu, /* private: */_e){
1769         this.parentMenu = parentMenu;
1770         if(!this.el){
1771             this.render();
1772         }
1773         if(_e !== false){
1774             this.fireEvent("beforeshow", this);
1775             
1776             //xy = this.el.adjustForConstraints(xy);
1777         }
1778         //this.el.setXY(xy);
1779         //this.el.show();
1780         this.hideMenuItems();
1781         this.hidden = false;
1782         this.triggerEl.addClass('open');
1783         this.focus();
1784         this.fireEvent("show", this);
1785     },
1786     
1787     focus : function(){
1788         return;
1789         if(!this.hidden){
1790             this.doFocus.defer(50, this);
1791         }
1792     },
1793
1794     doFocus : function(){
1795         if(!this.hidden){
1796             this.focusEl.focus();
1797         }
1798     },
1799
1800     /**
1801      * Hides this menu and optionally all parent menus
1802      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1803      */
1804     hide : function(deep){
1805         
1806         this.hideMenuItems();
1807         if(this.el && this.isVisible()){
1808             this.fireEvent("beforehide", this);
1809             if(this.activeItem){
1810                 this.activeItem.deactivate();
1811                 this.activeItem = null;
1812             }
1813             this.triggerEl.removeClass('open');;
1814             this.hidden = true;
1815             this.fireEvent("hide", this);
1816         }
1817         if(deep === true && this.parentMenu){
1818             this.parentMenu.hide(true);
1819         }
1820     },
1821     
1822     onTriggerPress  : function(e)
1823     {
1824         
1825         Roo.log('trigger press');
1826         //Roo.log(e.getTarget());
1827        // Roo.log(this.triggerEl.dom);
1828         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1829             return;
1830         }
1831         if (this.isVisible()) {
1832             Roo.log('hide');
1833             this.hide();
1834         } else {
1835             this.show(this.triggerEl, false, false);
1836         }
1837         
1838         
1839     },
1840     
1841          
1842        
1843     
1844     hideMenuItems : function()
1845     {
1846         //$(backdrop).remove()
1847         Roo.select('.open',true).each(function(aa) {
1848             
1849             aa.removeClass('open');
1850           //var parent = getParent($(this))
1851           //var relatedTarget = { relatedTarget: this }
1852           
1853            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1854           //if (e.isDefaultPrevented()) return
1855            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1856         })
1857     },
1858     addxtypeChild : function (tree, cntr) {
1859         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1860           
1861         this.menuitems.add(comp);
1862         return comp;
1863
1864     },
1865     getEl : function()
1866     {
1867         Roo.log(this.el);
1868         return this.el;
1869     }
1870 });
1871
1872  
1873  /*
1874  * - LGPL
1875  *
1876  * menu item
1877  * 
1878  */
1879
1880
1881 /**
1882  * @class Roo.bootstrap.MenuItem
1883  * @extends Roo.bootstrap.Component
1884  * Bootstrap MenuItem class
1885  * @cfg {String} html the menu label
1886  * @cfg {String} href the link
1887  * @cfg {Boolean} preventDefault (true | false) default true
1888  * 
1889  * 
1890  * @constructor
1891  * Create a new MenuItem
1892  * @param {Object} config The config object
1893  */
1894
1895
1896 Roo.bootstrap.MenuItem = function(config){
1897     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1898     this.addEvents({
1899         // raw events
1900         /**
1901          * @event click
1902          * The raw click event for the entire grid.
1903          * @param {Roo.EventObject} e
1904          */
1905         "click" : true
1906     });
1907 };
1908
1909 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1910     
1911     href : false,
1912     html : false,
1913     preventDefault: true,
1914     
1915     getAutoCreate : function(){
1916         var cfg= {
1917             tag: 'li',
1918             cls: 'dropdown-menu-item',
1919             cn: [
1920                     {
1921                         tag : 'a',
1922                         href : '#',
1923                         html : 'Link'
1924                     }
1925                 ]
1926         };
1927         if (this.parent().type == 'treeview') {
1928             cfg.cls = 'treeview-menu';
1929         }
1930         
1931         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1932         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1933         return cfg;
1934     },
1935     
1936     initEvents: function() {
1937         
1938         //this.el.select('a').on('click', this.onClick, this);
1939         
1940     },
1941     onClick : function(e)
1942     {
1943         Roo.log('item on click ');
1944         //if(this.preventDefault){
1945         //    e.preventDefault();
1946         //}
1947         //this.parent().hideMenuItems();
1948         
1949         this.fireEvent('click', this, e);
1950     },
1951     getEl : function()
1952     {
1953         return this.el;
1954     }
1955 });
1956
1957  
1958
1959  /*
1960  * - LGPL
1961  *
1962  * menu separator
1963  * 
1964  */
1965
1966
1967 /**
1968  * @class Roo.bootstrap.MenuSeparator
1969  * @extends Roo.bootstrap.Component
1970  * Bootstrap MenuSeparator class
1971  * 
1972  * @constructor
1973  * Create a new MenuItem
1974  * @param {Object} config The config object
1975  */
1976
1977
1978 Roo.bootstrap.MenuSeparator = function(config){
1979     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1980 };
1981
1982 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1983     
1984     getAutoCreate : function(){
1985         var cfg = {
1986             cls: 'divider',
1987             tag : 'li'
1988         };
1989         
1990         return cfg;
1991     }
1992    
1993 });
1994
1995  
1996
1997  
1998 /*
1999 <div class="modal fade">
2000   <div class="modal-dialog">
2001     <div class="modal-content">
2002       <div class="modal-header">
2003         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2004         <h4 class="modal-title">Modal title</h4>
2005       </div>
2006       <div class="modal-body">
2007         <p>One fine body&hellip;</p>
2008       </div>
2009       <div class="modal-footer">
2010         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2011         <button type="button" class="btn btn-primary">Save changes</button>
2012       </div>
2013     </div><!-- /.modal-content -->
2014   </div><!-- /.modal-dialog -->
2015 </div><!-- /.modal -->
2016 */
2017 /*
2018  * - LGPL
2019  *
2020  * page contgainer.
2021  * 
2022  */
2023
2024 /**
2025  * @class Roo.bootstrap.Modal
2026  * @extends Roo.bootstrap.Component
2027  * Bootstrap Modal class
2028  * @cfg {String} title Title of dialog
2029  * @cfg {Boolean} specificTitle (true|false) default false
2030  * @cfg {Array} buttons Array of buttons or standard button set..
2031  * @cfg {String} buttonPosition (left|right|center) default right
2032  * @cfg {Boolean} animate (true | false) default true
2033  * 
2034  * @constructor
2035  * Create a new Modal Dialog
2036  * @param {Object} config The config object
2037  */
2038
2039 Roo.bootstrap.Modal = function(config){
2040     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2041     this.addEvents({
2042         // raw events
2043         /**
2044          * @event btnclick
2045          * The raw btnclick event for the button
2046          * @param {Roo.EventObject} e
2047          */
2048         "btnclick" : true
2049     });
2050     this.buttons = this.buttons || [];
2051 };
2052
2053 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2054     
2055     title : 'test dialog',
2056    
2057     buttons : false,
2058     
2059     // set on load...
2060     body:  false,
2061     
2062     specificTitle: false,
2063     
2064     buttonPosition: 'right',
2065     
2066     animate : true,
2067     
2068     onRender : function(ct, position)
2069     {
2070         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2071      
2072         if(!this.el){
2073             var cfg = Roo.apply({},  this.getAutoCreate());
2074             cfg.id = Roo.id();
2075             //if(!cfg.name){
2076             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2077             //}
2078             //if (!cfg.name.length) {
2079             //    delete cfg.name;
2080            // }
2081             if (this.cls) {
2082                 cfg.cls += ' ' + this.cls;
2083             }
2084             if (this.style) {
2085                 cfg.style = this.style;
2086             }
2087             this.el = Roo.get(document.body).createChild(cfg, position);
2088         }
2089         //var type = this.el.dom.type;
2090         
2091         if(this.tabIndex !== undefined){
2092             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2093         }
2094         
2095         
2096         
2097         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2098         this.maskEl.enableDisplayMode("block");
2099         this.maskEl.hide();
2100         //this.el.addClass("x-dlg-modal");
2101     
2102         if (this.buttons.length) {
2103             Roo.each(this.buttons, function(bb) {
2104                 b = Roo.apply({}, bb);
2105                 b.xns = b.xns || Roo.bootstrap;
2106                 b.xtype = b.xtype || 'Button';
2107                 if (typeof(b.listeners) == 'undefined') {
2108                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2109                 }
2110                 
2111                 var btn = Roo.factory(b);
2112                 
2113                 btn.onRender(this.el.select('.modal-footer div').first());
2114                 
2115             },this);
2116         }
2117         // render the children.
2118         var nitems = [];
2119         
2120         if(typeof(this.items) != 'undefined'){
2121             var items = this.items;
2122             delete this.items;
2123
2124             for(var i =0;i < items.length;i++) {
2125                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2126             }
2127         }
2128         
2129         this.items = nitems;
2130         
2131         this.body = this.el.select('.modal-body',true).first();
2132         this.close = this.el.select('.modal-header .close', true).first();
2133         this.footer = this.el.select('.modal-footer',true).first();
2134         this.initEvents();
2135         //this.el.addClass([this.fieldClass, this.cls]);
2136         
2137     },
2138     getAutoCreate : function(){
2139         
2140         
2141         var bdy = {
2142                 cls : 'modal-body',
2143                 html : this.html || ''
2144         };
2145         
2146         var title = {
2147             tag: 'h4',
2148             cls : 'modal-title',
2149             html : this.title
2150         };
2151         
2152         if(this.specificTitle){
2153             title = this.title;
2154         };
2155         
2156         var modal = {
2157             cls: "modal",
2158             style : 'display: none',
2159             cn : [
2160                 {
2161                     cls: "modal-dialog",
2162                     cn : [
2163                         {
2164                             cls : "modal-content",
2165                             cn : [
2166                                 {
2167                                     cls : 'modal-header',
2168                                     cn : [
2169                                         {
2170                                             tag: 'button',
2171                                             cls : 'close',
2172                                             html : '&times'
2173                                         },
2174                                         title
2175                                     ]
2176                                 },
2177                                 bdy,
2178                                 {
2179                                     cls : 'modal-footer',
2180                                     cn : [
2181                                         {
2182                                             tag: 'div',
2183                                             cls: 'btn-' + this.buttonPosition
2184                                         }
2185                                     ]
2186                                     
2187                                 }
2188                                 
2189                                 
2190                             ]
2191                             
2192                         }
2193                     ]
2194                         
2195                 }
2196             ]
2197         };
2198         
2199         if(this.animate){
2200             modal.cls += ' fade';
2201         }
2202         
2203         return modal;
2204           
2205     },
2206     getChildContainer : function() {
2207          
2208          return this.el.select('.modal-body',true).first();
2209         
2210     },
2211     getButtonContainer : function() {
2212          return this.el.select('.modal-footer div',true).first();
2213         
2214     },
2215     initEvents : function()
2216     {
2217         this.el.select('.modal-header .close').on('click', this.hide, this);
2218 //        
2219 //        this.addxtype(this);
2220     },
2221     show : function() {
2222         
2223         if (!this.rendered) {
2224             this.render();
2225         }
2226         
2227         this.el.setStyle('display', 'block');
2228         
2229         if(this.animate){
2230             var _this = this;
2231             (function(){ _this.el.addClass('in'); }).defer(50);
2232         }else{
2233             this.el.addClass('in');
2234         }
2235         
2236         Roo.get(document.body).addClass("x-body-masked");
2237         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2238         this.maskEl.show();
2239         this.el.setStyle('zIndex', '10001');
2240         this.fireEvent('show', this);
2241         
2242         
2243     },
2244     hide : function()
2245     {
2246         Roo.log('Modal hide?!');
2247         this.maskEl.hide();
2248         Roo.get(document.body).removeClass("x-body-masked");
2249         this.el.removeClass('in');
2250         
2251         if(this.animate){
2252             var _this = this;
2253             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2254         }else{
2255             this.el.setStyle('display', 'none');
2256         }
2257         
2258         this.fireEvent('hide', this);
2259     },
2260     
2261     addButton : function(str, cb)
2262     {
2263          
2264         
2265         var b = Roo.apply({}, { html : str } );
2266         b.xns = b.xns || Roo.bootstrap;
2267         b.xtype = b.xtype || 'Button';
2268         if (typeof(b.listeners) == 'undefined') {
2269             b.listeners = { click : cb.createDelegate(this)  };
2270         }
2271         
2272         var btn = Roo.factory(b);
2273            
2274         btn.onRender(this.el.select('.modal-footer div').first());
2275         
2276         return btn;   
2277        
2278     },
2279     
2280     setDefaultButton : function(btn)
2281     {
2282         //this.el.select('.modal-footer').()
2283     },
2284     resizeTo: function(w,h)
2285     {
2286         // skip..
2287     },
2288     setContentSize  : function(w, h)
2289     {
2290         
2291     },
2292     onButtonClick: function(btn,e)
2293     {
2294         //Roo.log([a,b,c]);
2295         this.fireEvent('btnclick', btn.name, e);
2296     },
2297     setTitle: function(str) {
2298         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2299         
2300     }
2301 });
2302
2303
2304 Roo.apply(Roo.bootstrap.Modal,  {
2305     /**
2306          * Button config that displays a single OK button
2307          * @type Object
2308          */
2309         OK :  [{
2310             name : 'ok',
2311             weight : 'primary',
2312             html : 'OK'
2313         }], 
2314         /**
2315          * Button config that displays Yes and No buttons
2316          * @type Object
2317          */
2318         YESNO : [
2319             {
2320                 name  : 'no',
2321                 html : 'No'
2322             },
2323             {
2324                 name  :'yes',
2325                 weight : 'primary',
2326                 html : 'Yes'
2327             }
2328         ],
2329         
2330         /**
2331          * Button config that displays OK and Cancel buttons
2332          * @type Object
2333          */
2334         OKCANCEL : [
2335             {
2336                name : 'cancel',
2337                 html : 'Cancel'
2338             },
2339             {
2340                 name : 'ok',
2341                 weight : 'primary',
2342                 html : 'OK'
2343             }
2344         ],
2345         /**
2346          * Button config that displays Yes, No and Cancel buttons
2347          * @type Object
2348          */
2349         YESNOCANCEL : [
2350             {
2351                 name : 'yes',
2352                 weight : 'primary',
2353                 html : 'Yes'
2354             },
2355             {
2356                 name : 'no',
2357                 html : 'No'
2358             },
2359             {
2360                 name : 'cancel',
2361                 html : 'Cancel'
2362             }
2363         ]
2364 });
2365  /*
2366  * - LGPL
2367  *
2368  * messagebox - can be used as a replace
2369  * 
2370  */
2371 /**
2372  * @class Roo.MessageBox
2373  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2374  * Example usage:
2375  *<pre><code>
2376 // Basic alert:
2377 Roo.Msg.alert('Status', 'Changes saved successfully.');
2378
2379 // Prompt for user data:
2380 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2381     if (btn == 'ok'){
2382         // process text value...
2383     }
2384 });
2385
2386 // Show a dialog using config options:
2387 Roo.Msg.show({
2388    title:'Save Changes?',
2389    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2390    buttons: Roo.Msg.YESNOCANCEL,
2391    fn: processResult,
2392    animEl: 'elId'
2393 });
2394 </code></pre>
2395  * @singleton
2396  */
2397 Roo.bootstrap.MessageBox = function(){
2398     var dlg, opt, mask, waitTimer;
2399     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2400     var buttons, activeTextEl, bwidth;
2401
2402     
2403     // private
2404     var handleButton = function(button){
2405         dlg.hide();
2406         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2407     };
2408
2409     // private
2410     var handleHide = function(){
2411         if(opt && opt.cls){
2412             dlg.el.removeClass(opt.cls);
2413         }
2414         //if(waitTimer){
2415         //    Roo.TaskMgr.stop(waitTimer);
2416         //    waitTimer = null;
2417         //}
2418     };
2419
2420     // private
2421     var updateButtons = function(b){
2422         var width = 0;
2423         if(!b){
2424             buttons["ok"].hide();
2425             buttons["cancel"].hide();
2426             buttons["yes"].hide();
2427             buttons["no"].hide();
2428             //dlg.footer.dom.style.display = 'none';
2429             return width;
2430         }
2431         dlg.footer.dom.style.display = '';
2432         for(var k in buttons){
2433             if(typeof buttons[k] != "function"){
2434                 if(b[k]){
2435                     buttons[k].show();
2436                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2437                     width += buttons[k].el.getWidth()+15;
2438                 }else{
2439                     buttons[k].hide();
2440                 }
2441             }
2442         }
2443         return width;
2444     };
2445
2446     // private
2447     var handleEsc = function(d, k, e){
2448         if(opt && opt.closable !== false){
2449             dlg.hide();
2450         }
2451         if(e){
2452             e.stopEvent();
2453         }
2454     };
2455
2456     return {
2457         /**
2458          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2459          * @return {Roo.BasicDialog} The BasicDialog element
2460          */
2461         getDialog : function(){
2462            if(!dlg){
2463                 dlg = new Roo.bootstrap.Modal( {
2464                     //draggable: true,
2465                     //resizable:false,
2466                     //constraintoviewport:false,
2467                     //fixedcenter:true,
2468                     //collapsible : false,
2469                     //shim:true,
2470                     //modal: true,
2471                   //  width:400,
2472                   //  height:100,
2473                     //buttonAlign:"center",
2474                     closeClick : function(){
2475                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2476                             handleButton("no");
2477                         }else{
2478                             handleButton("cancel");
2479                         }
2480                     }
2481                 });
2482                 dlg.render();
2483                 dlg.on("hide", handleHide);
2484                 mask = dlg.mask;
2485                 //dlg.addKeyListener(27, handleEsc);
2486                 buttons = {};
2487                 this.buttons = buttons;
2488                 var bt = this.buttonText;
2489                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2490                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2491                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2492                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2493                 Roo.log(buttons)
2494                 bodyEl = dlg.body.createChild({
2495
2496                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2497                         '<textarea class="roo-mb-textarea"></textarea>' +
2498                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2499                 });
2500                 msgEl = bodyEl.dom.firstChild;
2501                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2502                 textboxEl.enableDisplayMode();
2503                 textboxEl.addKeyListener([10,13], function(){
2504                     if(dlg.isVisible() && opt && opt.buttons){
2505                         if(opt.buttons.ok){
2506                             handleButton("ok");
2507                         }else if(opt.buttons.yes){
2508                             handleButton("yes");
2509                         }
2510                     }
2511                 });
2512                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2513                 textareaEl.enableDisplayMode();
2514                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2515                 progressEl.enableDisplayMode();
2516                 var pf = progressEl.dom.firstChild;
2517                 if (pf) {
2518                     pp = Roo.get(pf.firstChild);
2519                     pp.setHeight(pf.offsetHeight);
2520                 }
2521                 
2522             }
2523             return dlg;
2524         },
2525
2526         /**
2527          * Updates the message box body text
2528          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2529          * the XHTML-compliant non-breaking space character '&amp;#160;')
2530          * @return {Roo.MessageBox} This message box
2531          */
2532         updateText : function(text){
2533             if(!dlg.isVisible() && !opt.width){
2534                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2535             }
2536             msgEl.innerHTML = text || '&#160;';
2537       
2538             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2539             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2540             var w = Math.max(
2541                     Math.min(opt.width || cw , this.maxWidth), 
2542                     Math.max(opt.minWidth || this.minWidth, bwidth)
2543             );
2544             if(opt.prompt){
2545                 activeTextEl.setWidth(w);
2546             }
2547             if(dlg.isVisible()){
2548                 dlg.fixedcenter = false;
2549             }
2550             // to big, make it scroll. = But as usual stupid IE does not support
2551             // !important..
2552             
2553             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2554                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2555                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2556             } else {
2557                 bodyEl.dom.style.height = '';
2558                 bodyEl.dom.style.overflowY = '';
2559             }
2560             if (cw > w) {
2561                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2562             } else {
2563                 bodyEl.dom.style.overflowX = '';
2564             }
2565             
2566             dlg.setContentSize(w, bodyEl.getHeight());
2567             if(dlg.isVisible()){
2568                 dlg.fixedcenter = true;
2569             }
2570             return this;
2571         },
2572
2573         /**
2574          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2575          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2576          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2577          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2578          * @return {Roo.MessageBox} This message box
2579          */
2580         updateProgress : function(value, text){
2581             if(text){
2582                 this.updateText(text);
2583             }
2584             if (pp) { // weird bug on my firefox - for some reason this is not defined
2585                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2586             }
2587             return this;
2588         },        
2589
2590         /**
2591          * Returns true if the message box is currently displayed
2592          * @return {Boolean} True if the message box is visible, else false
2593          */
2594         isVisible : function(){
2595             return dlg && dlg.isVisible();  
2596         },
2597
2598         /**
2599          * Hides the message box if it is displayed
2600          */
2601         hide : function(){
2602             if(this.isVisible()){
2603                 dlg.hide();
2604             }  
2605         },
2606
2607         /**
2608          * Displays a new message box, or reinitializes an existing message box, based on the config options
2609          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2610          * The following config object properties are supported:
2611          * <pre>
2612 Property    Type             Description
2613 ----------  ---------------  ------------------------------------------------------------------------------------
2614 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2615                                    closes (defaults to undefined)
2616 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2617                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2618 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2619                                    progress and wait dialogs will ignore this property and always hide the
2620                                    close button as they can only be closed programmatically.
2621 cls               String           A custom CSS class to apply to the message box element
2622 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2623                                    displayed (defaults to 75)
2624 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2625                                    function will be btn (the name of the button that was clicked, if applicable,
2626                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2627                                    Progress and wait dialogs will ignore this option since they do not respond to
2628                                    user actions and can only be closed programmatically, so any required function
2629                                    should be called by the same code after it closes the dialog.
2630 icon              String           A CSS class that provides a background image to be used as an icon for
2631                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2632 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2633 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2634 modal             Boolean          False to allow user interaction with the page while the message box is
2635                                    displayed (defaults to true)
2636 msg               String           A string that will replace the existing message box body text (defaults
2637                                    to the XHTML-compliant non-breaking space character '&#160;')
2638 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2639 progress          Boolean          True to display a progress bar (defaults to false)
2640 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2641 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2642 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2643 title             String           The title text
2644 value             String           The string value to set into the active textbox element if displayed
2645 wait              Boolean          True to display a progress bar (defaults to false)
2646 width             Number           The width of the dialog in pixels
2647 </pre>
2648          *
2649          * Example usage:
2650          * <pre><code>
2651 Roo.Msg.show({
2652    title: 'Address',
2653    msg: 'Please enter your address:',
2654    width: 300,
2655    buttons: Roo.MessageBox.OKCANCEL,
2656    multiline: true,
2657    fn: saveAddress,
2658    animEl: 'addAddressBtn'
2659 });
2660 </code></pre>
2661          * @param {Object} config Configuration options
2662          * @return {Roo.MessageBox} This message box
2663          */
2664         show : function(options)
2665         {
2666             
2667             // this causes nightmares if you show one dialog after another
2668             // especially on callbacks..
2669              
2670             if(this.isVisible()){
2671                 
2672                 this.hide();
2673                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2674                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2675                 Roo.log("New Dialog Message:" +  options.msg )
2676                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2677                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2678                 
2679             }
2680             var d = this.getDialog();
2681             opt = options;
2682             d.setTitle(opt.title || "&#160;");
2683             d.close.setDisplayed(opt.closable !== false);
2684             activeTextEl = textboxEl;
2685             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2686             if(opt.prompt){
2687                 if(opt.multiline){
2688                     textboxEl.hide();
2689                     textareaEl.show();
2690                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2691                         opt.multiline : this.defaultTextHeight);
2692                     activeTextEl = textareaEl;
2693                 }else{
2694                     textboxEl.show();
2695                     textareaEl.hide();
2696                 }
2697             }else{
2698                 textboxEl.hide();
2699                 textareaEl.hide();
2700             }
2701             progressEl.setDisplayed(opt.progress === true);
2702             this.updateProgress(0);
2703             activeTextEl.dom.value = opt.value || "";
2704             if(opt.prompt){
2705                 dlg.setDefaultButton(activeTextEl);
2706             }else{
2707                 var bs = opt.buttons;
2708                 var db = null;
2709                 if(bs && bs.ok){
2710                     db = buttons["ok"];
2711                 }else if(bs && bs.yes){
2712                     db = buttons["yes"];
2713                 }
2714                 dlg.setDefaultButton(db);
2715             }
2716             bwidth = updateButtons(opt.buttons);
2717             this.updateText(opt.msg);
2718             if(opt.cls){
2719                 d.el.addClass(opt.cls);
2720             }
2721             d.proxyDrag = opt.proxyDrag === true;
2722             d.modal = opt.modal !== false;
2723             d.mask = opt.modal !== false ? mask : false;
2724             if(!d.isVisible()){
2725                 // force it to the end of the z-index stack so it gets a cursor in FF
2726                 document.body.appendChild(dlg.el.dom);
2727                 d.animateTarget = null;
2728                 d.show(options.animEl);
2729             }
2730             return this;
2731         },
2732
2733         /**
2734          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2735          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2736          * and closing the message box when the process is complete.
2737          * @param {String} title The title bar text
2738          * @param {String} msg The message box body text
2739          * @return {Roo.MessageBox} This message box
2740          */
2741         progress : function(title, msg){
2742             this.show({
2743                 title : title,
2744                 msg : msg,
2745                 buttons: false,
2746                 progress:true,
2747                 closable:false,
2748                 minWidth: this.minProgressWidth,
2749                 modal : true
2750             });
2751             return this;
2752         },
2753
2754         /**
2755          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2756          * If a callback function is passed it will be called after the user clicks the button, and the
2757          * id of the button that was clicked will be passed as the only parameter to the callback
2758          * (could also be the top-right close button).
2759          * @param {String} title The title bar text
2760          * @param {String} msg The message box body text
2761          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2762          * @param {Object} scope (optional) The scope of the callback function
2763          * @return {Roo.MessageBox} This message box
2764          */
2765         alert : function(title, msg, fn, scope){
2766             this.show({
2767                 title : title,
2768                 msg : msg,
2769                 buttons: this.OK,
2770                 fn: fn,
2771                 scope : scope,
2772                 modal : true
2773             });
2774             return this;
2775         },
2776
2777         /**
2778          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2779          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2780          * You are responsible for closing the message box when the process is complete.
2781          * @param {String} msg The message box body text
2782          * @param {String} title (optional) The title bar text
2783          * @return {Roo.MessageBox} This message box
2784          */
2785         wait : function(msg, title){
2786             this.show({
2787                 title : title,
2788                 msg : msg,
2789                 buttons: false,
2790                 closable:false,
2791                 progress:true,
2792                 modal:true,
2793                 width:300,
2794                 wait:true
2795             });
2796             waitTimer = Roo.TaskMgr.start({
2797                 run: function(i){
2798                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2799                 },
2800                 interval: 1000
2801             });
2802             return this;
2803         },
2804
2805         /**
2806          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2807          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2808          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2809          * @param {String} title The title bar text
2810          * @param {String} msg The message box body text
2811          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2812          * @param {Object} scope (optional) The scope of the callback function
2813          * @return {Roo.MessageBox} This message box
2814          */
2815         confirm : function(title, msg, fn, scope){
2816             this.show({
2817                 title : title,
2818                 msg : msg,
2819                 buttons: this.YESNO,
2820                 fn: fn,
2821                 scope : scope,
2822                 modal : true
2823             });
2824             return this;
2825         },
2826
2827         /**
2828          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2829          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2830          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2831          * (could also be the top-right close button) and the text that was entered will be passed as the two
2832          * parameters to the callback.
2833          * @param {String} title The title bar text
2834          * @param {String} msg The message box body text
2835          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2836          * @param {Object} scope (optional) The scope of the callback function
2837          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2838          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2839          * @return {Roo.MessageBox} This message box
2840          */
2841         prompt : function(title, msg, fn, scope, multiline){
2842             this.show({
2843                 title : title,
2844                 msg : msg,
2845                 buttons: this.OKCANCEL,
2846                 fn: fn,
2847                 minWidth:250,
2848                 scope : scope,
2849                 prompt:true,
2850                 multiline: multiline,
2851                 modal : true
2852             });
2853             return this;
2854         },
2855
2856         /**
2857          * Button config that displays a single OK button
2858          * @type Object
2859          */
2860         OK : {ok:true},
2861         /**
2862          * Button config that displays Yes and No buttons
2863          * @type Object
2864          */
2865         YESNO : {yes:true, no:true},
2866         /**
2867          * Button config that displays OK and Cancel buttons
2868          * @type Object
2869          */
2870         OKCANCEL : {ok:true, cancel:true},
2871         /**
2872          * Button config that displays Yes, No and Cancel buttons
2873          * @type Object
2874          */
2875         YESNOCANCEL : {yes:true, no:true, cancel:true},
2876
2877         /**
2878          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2879          * @type Number
2880          */
2881         defaultTextHeight : 75,
2882         /**
2883          * The maximum width in pixels of the message box (defaults to 600)
2884          * @type Number
2885          */
2886         maxWidth : 600,
2887         /**
2888          * The minimum width in pixels of the message box (defaults to 100)
2889          * @type Number
2890          */
2891         minWidth : 100,
2892         /**
2893          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2894          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2895          * @type Number
2896          */
2897         minProgressWidth : 250,
2898         /**
2899          * An object containing the default button text strings that can be overriden for localized language support.
2900          * Supported properties are: ok, cancel, yes and no.
2901          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2902          * @type Object
2903          */
2904         buttonText : {
2905             ok : "OK",
2906             cancel : "Cancel",
2907             yes : "Yes",
2908             no : "No"
2909         }
2910     };
2911 }();
2912
2913 /**
2914  * Shorthand for {@link Roo.MessageBox}
2915  */
2916 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2917 Roo.Msg = Roo.Msg || Roo.MessageBox;
2918 /*
2919  * - LGPL
2920  *
2921  * navbar
2922  * 
2923  */
2924
2925 /**
2926  * @class Roo.bootstrap.Navbar
2927  * @extends Roo.bootstrap.Component
2928  * Bootstrap Navbar class
2929
2930  * @constructor
2931  * Create a new Navbar
2932  * @param {Object} config The config object
2933  */
2934
2935
2936 Roo.bootstrap.Navbar = function(config){
2937     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2938     
2939 };
2940
2941 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2942     
2943     
2944    
2945     // private
2946     navItems : false,
2947     loadMask : false,
2948     
2949     
2950     getAutoCreate : function(){
2951         
2952         
2953         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2954         
2955     },
2956     
2957     initEvents :function ()
2958     {
2959         //Roo.log(this.el.select('.navbar-toggle',true));
2960         this.el.select('.navbar-toggle',true).on('click', function() {
2961            // Roo.log('click');
2962             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2963         }, this);
2964         
2965         var mark = {
2966             tag: "div",
2967             cls:"x-dlg-mask"
2968         }
2969         
2970         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2971         
2972         var size = this.el.getSize();
2973         this.maskEl.setSize(size.width, size.height);
2974         this.maskEl.enableDisplayMode("block");
2975         this.maskEl.hide();
2976         
2977         if(this.loadMask){
2978             this.maskEl.show();
2979         }
2980     },
2981     
2982     
2983     getChildContainer : function()
2984     {
2985         if (this.el.select('.collapse').getCount()) {
2986             return this.el.select('.collapse',true).first();
2987         }
2988         
2989         return this.el;
2990     },
2991     
2992     mask : function()
2993     {
2994         this.maskEl.show();
2995     },
2996     
2997     unmask : function()
2998     {
2999         this.maskEl.hide();
3000     } 
3001     
3002     
3003     
3004     
3005 });
3006
3007
3008
3009  
3010
3011  /*
3012  * - LGPL
3013  *
3014  * navbar
3015  * 
3016  */
3017
3018 /**
3019  * @class Roo.bootstrap.NavSimplebar
3020  * @extends Roo.bootstrap.Navbar
3021  * Bootstrap Sidebar class
3022  *
3023  * @cfg {Boolean} inverse is inverted color
3024  * 
3025  * @cfg {String} type (nav | pills | tabs)
3026  * @cfg {Boolean} arrangement stacked | justified
3027  * @cfg {String} align (left | right) alignment
3028  * 
3029  * @cfg {Boolean} main (true|false) main nav bar? default false
3030  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3031  * 
3032  * @cfg {String} tag (header|footer|nav|div) default is nav 
3033
3034  * 
3035  * 
3036  * 
3037  * @constructor
3038  * Create a new Sidebar
3039  * @param {Object} config The config object
3040  */
3041
3042
3043 Roo.bootstrap.NavSimplebar = function(config){
3044     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3045 };
3046
3047 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3048     
3049     inverse: false,
3050     
3051     type: false,
3052     arrangement: '',
3053     align : false,
3054     
3055     
3056     
3057     main : false,
3058     
3059     
3060     tag : false,
3061     
3062     
3063     getAutoCreate : function(){
3064         
3065         
3066         var cfg = {
3067             tag : this.tag || 'div',
3068             cls : 'navbar'
3069         };
3070           
3071         
3072         cfg.cn = [
3073             {
3074                 cls: 'nav',
3075                 tag : 'ul'
3076             }
3077         ];
3078         
3079          
3080         this.type = this.type || 'nav';
3081         if (['tabs','pills'].indexOf(this.type)!==-1) {
3082             cfg.cn[0].cls += ' nav-' + this.type
3083         
3084         
3085         } else {
3086             if (this.type!=='nav') {
3087                 Roo.log('nav type must be nav/tabs/pills')
3088             }
3089             cfg.cn[0].cls += ' navbar-nav'
3090         }
3091         
3092         
3093         
3094         
3095         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3096             cfg.cn[0].cls += ' nav-' + this.arrangement;
3097         }
3098         
3099         
3100         if (this.align === 'right') {
3101             cfg.cn[0].cls += ' navbar-right';
3102         }
3103         
3104         if (this.inverse) {
3105             cfg.cls += ' navbar-inverse';
3106             
3107         }
3108         
3109         
3110         return cfg;
3111     
3112         
3113     }
3114     
3115     
3116     
3117 });
3118
3119
3120
3121  
3122
3123  
3124        /*
3125  * - LGPL
3126  *
3127  * navbar
3128  * 
3129  */
3130
3131 /**
3132  * @class Roo.bootstrap.NavHeaderbar
3133  * @extends Roo.bootstrap.NavSimplebar
3134  * Bootstrap Sidebar class
3135  *
3136  * @cfg {String} brand what is brand
3137  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3138  * @cfg {String} brand_href href of the brand
3139  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3140  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3141  * 
3142  * @constructor
3143  * Create a new Sidebar
3144  * @param {Object} config The config object
3145  */
3146
3147
3148 Roo.bootstrap.NavHeaderbar = function(config){
3149     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3150 };
3151
3152 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3153     
3154     position: '',
3155     brand: '',
3156     brand_href: false,
3157     srButton : true,
3158     autohide : false,
3159     
3160     getAutoCreate : function(){
3161         
3162         var   cfg = {
3163             tag: this.nav || 'nav',
3164             cls: 'navbar',
3165             role: 'navigation',
3166             cn: []
3167         };
3168         
3169         if(this.srButton){
3170             cfg.cn.push({
3171                 tag: 'div',
3172                 cls: 'navbar-header',
3173                 cn: [
3174                     {
3175                         tag: 'button',
3176                         type: 'button',
3177                         cls: 'navbar-toggle',
3178                         'data-toggle': 'collapse',
3179                         cn: [
3180                             {
3181                                 tag: 'span',
3182                                 cls: 'sr-only',
3183                                 html: 'Toggle navigation'
3184                             },
3185                             {
3186                                 tag: 'span',
3187                                 cls: 'icon-bar'
3188                             },
3189                             {
3190                                 tag: 'span',
3191                                 cls: 'icon-bar'
3192                             },
3193                             {
3194                                 tag: 'span',
3195                                 cls: 'icon-bar'
3196                             }
3197                         ]
3198                     }
3199                 ]
3200             });
3201         }
3202         
3203         cfg.cn.push({
3204             tag: 'div',
3205             cls: 'collapse navbar-collapse',
3206             cn : []
3207         });
3208         
3209         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3210         
3211         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3212             cfg.cls += ' navbar-' + this.position;
3213             
3214             // tag can override this..
3215             
3216             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3217         }
3218         
3219         if (this.brand !== '') {
3220             cfg.cn[0].cn.push({
3221                 tag: 'a',
3222                 href: this.brand_href ? this.brand_href : '#',
3223                 cls: 'navbar-brand',
3224                 cn: [
3225                 this.brand
3226                 ]
3227             });
3228         }
3229         
3230         if(this.main){
3231             cfg.cls += ' main-nav';
3232         }
3233         
3234         
3235         return cfg;
3236
3237         
3238     },
3239     initEvents : function()
3240     {
3241         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3242         
3243         if (this.autohide) {
3244             
3245             var prevScroll = 0;
3246             var ft = this.el;
3247             
3248             Roo.get(document).on('scroll',function(e) {
3249                 var ns = Roo.get(document).getScroll().top;
3250                 var os = prevScroll;
3251                 prevScroll = ns;
3252                 
3253                 if(ns > os){
3254                     ft.removeClass('slideDown');
3255                     ft.addClass('slideUp');
3256                     return;
3257                 }
3258                 ft.removeClass('slideUp');
3259                 ft.addClass('slideDown');
3260                  
3261               
3262           },this);
3263         }
3264     }    
3265           
3266       
3267     
3268     
3269 });
3270
3271
3272
3273  
3274
3275  /*
3276  * - LGPL
3277  *
3278  * navbar
3279  * 
3280  */
3281
3282 /**
3283  * @class Roo.bootstrap.NavSidebar
3284  * @extends Roo.bootstrap.Navbar
3285  * Bootstrap Sidebar class
3286  * 
3287  * @constructor
3288  * Create a new Sidebar
3289  * @param {Object} config The config object
3290  */
3291
3292
3293 Roo.bootstrap.NavSidebar = function(config){
3294     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3295 };
3296
3297 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3298     
3299     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3300     
3301     getAutoCreate : function(){
3302         
3303         
3304         return  {
3305             tag: 'div',
3306             cls: 'sidebar sidebar-nav'
3307         };
3308     
3309         
3310     }
3311     
3312     
3313     
3314 });
3315
3316
3317
3318  
3319
3320  /*
3321  * - LGPL
3322  *
3323  * nav group
3324  * 
3325  */
3326
3327 /**
3328  * @class Roo.bootstrap.NavGroup
3329  * @extends Roo.bootstrap.Component
3330  * Bootstrap NavGroup class
3331  * @cfg {String} align left | right
3332  * @cfg {Boolean} inverse false | true
3333  * @cfg {String} type (nav|pills|tab) default nav
3334  * @cfg {String} navId - reference Id for navbar.
3335
3336  * 
3337  * @constructor
3338  * Create a new nav group
3339  * @param {Object} config The config object
3340  */
3341
3342 Roo.bootstrap.NavGroup = function(config){
3343     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3344     this.navItems = [];
3345    
3346     Roo.bootstrap.NavGroup.register(this);
3347      this.addEvents({
3348         /**
3349              * @event changed
3350              * Fires when the active item changes
3351              * @param {Roo.bootstrap.NavGroup} this
3352              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3353              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3354          */
3355         'changed': true
3356      });
3357     
3358 };
3359
3360 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3361     
3362     align: '',
3363     inverse: false,
3364     form: false,
3365     type: 'nav',
3366     navId : '',
3367     // private
3368     
3369     navItems : false, 
3370     
3371     getAutoCreate : function()
3372     {
3373         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3374         
3375         cfg = {
3376             tag : 'ul',
3377             cls: 'nav' 
3378         }
3379         
3380         if (['tabs','pills'].indexOf(this.type)!==-1) {
3381             cfg.cls += ' nav-' + this.type
3382         } else {
3383             if (this.type!=='nav') {
3384                 Roo.log('nav type must be nav/tabs/pills')
3385             }
3386             cfg.cls += ' navbar-nav'
3387         }
3388         
3389         if (this.parent().sidebar) {
3390             cfg = {
3391                 tag: 'ul',
3392                 cls: 'dashboard-menu sidebar-menu'
3393             }
3394             
3395             return cfg;
3396         }
3397         
3398         if (this.form === true) {
3399             cfg = {
3400                 tag: 'form',
3401                 cls: 'navbar-form'
3402             }
3403             
3404             if (this.align === 'right') {
3405                 cfg.cls += ' navbar-right';
3406             } else {
3407                 cfg.cls += ' navbar-left';
3408             }
3409         }
3410         
3411         if (this.align === 'right') {
3412             cfg.cls += ' navbar-right';
3413         }
3414         
3415         if (this.inverse) {
3416             cfg.cls += ' navbar-inverse';
3417             
3418         }
3419         
3420         
3421         return cfg;
3422     },
3423     /**
3424     * sets the active Navigation item
3425     * @param {Roo.bootstrap.NavItem} the new current navitem
3426     */
3427     setActiveItem : function(item)
3428     {
3429         var prev = false;
3430         Roo.each(this.navItems, function(v){
3431             if (v == item) {
3432                 return ;
3433             }
3434             if (v.isActive()) {
3435                 v.setActive(false, true);
3436                 prev = v;
3437                 
3438             }
3439             
3440         });
3441
3442         item.setActive(true, true);
3443         this.fireEvent('changed', this, item, prev);
3444         
3445         
3446     },
3447     /**
3448     * gets the active Navigation item
3449     * @return {Roo.bootstrap.NavItem} the current navitem
3450     */
3451     getActive : function()
3452     {
3453         
3454         var prev = false;
3455         Roo.each(this.navItems, function(v){
3456             
3457             if (v.isActive()) {
3458                 prev = v;
3459                 
3460             }
3461             
3462         });
3463         return prev;
3464     },
3465     
3466     indexOfNav : function()
3467     {
3468         
3469         var prev = false;
3470         Roo.each(this.navItems, function(v,i){
3471             
3472             if (v.isActive()) {
3473                 prev = i;
3474                 
3475             }
3476             
3477         });
3478         return prev;
3479     },
3480     /**
3481     * adds a Navigation item
3482     * @param {Roo.bootstrap.NavItem} the navitem to add
3483     */
3484     addItem : function(cfg)
3485     {
3486         var cn = new Roo.bootstrap.NavItem(cfg);
3487         this.register(cn);
3488         cn.parentId = this.id;
3489         cn.onRender(this.el, null);
3490         return cn;
3491     },
3492     /**
3493     * register a Navigation item
3494     * @param {Roo.bootstrap.NavItem} the navitem to add
3495     */
3496     register : function(item)
3497     {
3498         this.navItems.push( item);
3499         item.navId = this.navId;
3500     
3501     },
3502     
3503     /**
3504     * clear all the Navigation item
3505     */
3506    
3507     clearAll : function()
3508     {
3509         this.navItems = [];
3510         this.el.dom.innerHTML = '';
3511     },
3512     
3513     getNavItem: function(tabId)
3514     {
3515         var ret = false;
3516         Roo.each(this.navItems, function(e) {
3517             if (e.tabId == tabId) {
3518                ret =  e;
3519                return false;
3520             }
3521             return true;
3522             
3523         });
3524         return ret;
3525     },
3526     
3527     setActiveNext : function()
3528     {
3529         var i = this.indexOfNav(this.getActive());
3530         if (i > this.navItems.length) {
3531             return;
3532         }
3533         this.setActiveItem(this.navItems[i+1]);
3534     },
3535     setActivePrev : function()
3536     {
3537         var i = this.indexOfNav(this.getActive());
3538         if (i  < 1) {
3539             return;
3540         }
3541         this.setActiveItem(this.navItems[i-1]);
3542     },
3543     clearWasActive : function(except) {
3544         Roo.each(this.navItems, function(e) {
3545             if (e.tabId != except.tabId && e.was_active) {
3546                e.was_active = false;
3547                return false;
3548             }
3549             return true;
3550             
3551         });
3552     },
3553     getWasActive : function ()
3554     {
3555         var r = false;
3556         Roo.each(this.navItems, function(e) {
3557             if (e.was_active) {
3558                r = e;
3559                return false;
3560             }
3561             return true;
3562             
3563         });
3564         return r;
3565     }
3566     
3567     
3568 });
3569
3570  
3571 Roo.apply(Roo.bootstrap.NavGroup, {
3572     
3573     groups: {},
3574      /**
3575     * register a Navigation Group
3576     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3577     */
3578     register : function(navgrp)
3579     {
3580         this.groups[navgrp.navId] = navgrp;
3581         
3582     },
3583     /**
3584     * fetch a Navigation Group based on the navigation ID
3585     * @param {string} the navgroup to add
3586     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3587     */
3588     get: function(navId) {
3589         if (typeof(this.groups[navId]) == 'undefined') {
3590             return false;
3591             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3592         }
3593         return this.groups[navId] ;
3594     }
3595     
3596     
3597     
3598 });
3599
3600  /*
3601  * - LGPL
3602  *
3603  * row
3604  * 
3605  */
3606
3607 /**
3608  * @class Roo.bootstrap.NavItem
3609  * @extends Roo.bootstrap.Component
3610  * Bootstrap Navbar.NavItem class
3611  * @cfg {String} href  link to
3612  * @cfg {String} html content of button
3613  * @cfg {String} badge text inside badge
3614  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3615  * @cfg {String} glyphicon name of glyphicon
3616  * @cfg {String} icon name of font awesome icon
3617  * @cfg {Boolean} active Is item active
3618  * @cfg {Boolean} disabled Is item disabled
3619  
3620  * @cfg {Boolean} preventDefault (true | false) default false
3621  * @cfg {String} tabId the tab that this item activates.
3622  * @cfg {String} tagtype (a|span) render as a href or span?
3623   
3624  * @constructor
3625  * Create a new Navbar Item
3626  * @param {Object} config The config object
3627  */
3628 Roo.bootstrap.NavItem = function(config){
3629     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3630     this.addEvents({
3631         // raw events
3632         /**
3633          * @event click
3634          * The raw click event for the entire grid.
3635          * @param {Roo.EventObject} e
3636          */
3637         "click" : true,
3638          /**
3639             * @event changed
3640             * Fires when the active item active state changes
3641             * @param {Roo.bootstrap.NavItem} this
3642             * @param {boolean} state the new state
3643              
3644          */
3645         'changed': true
3646     });
3647    
3648 };
3649
3650 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3651     
3652     href: false,
3653     html: '',
3654     badge: '',
3655     icon: false,
3656     glyphicon: false,
3657     active: false,
3658     preventDefault : false,
3659     tabId : false,
3660     tagtype : 'a',
3661     disabled : false,
3662     
3663     was_active : false,
3664     
3665     getAutoCreate : function(){
3666          
3667         var cfg = {
3668             tag: 'li',
3669             cls: 'nav-item'
3670             
3671         }
3672         if (this.active) {
3673             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3674         }
3675         if (this.disabled) {
3676             cfg.cls += ' disabled';
3677         }
3678         
3679         if (this.href || this.html || this.glyphicon || this.icon) {
3680             cfg.cn = [
3681                 {
3682                     tag: this.tagtype,
3683                     href : this.href || "#",
3684                     html: this.html || ''
3685                 }
3686             ];
3687             
3688             if (this.icon) {
3689                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3690             }
3691
3692             if(this.glyphicon) {
3693                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3694             }
3695             
3696             if (this.menu) {
3697                 
3698                 cfg.cn[0].html += " <span class='caret'></span>";
3699              
3700             }
3701             
3702             if (this.badge !== '') {
3703                  
3704                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3705             }
3706         }
3707         
3708         
3709         
3710         return cfg;
3711     },
3712     initEvents: function() {
3713        // Roo.log('init events?');
3714        // Roo.log(this.el.dom);
3715         if (typeof (this.menu) != 'undefined') {
3716             this.menu.parentType = this.xtype;
3717             this.menu.triggerEl = this.el;
3718             this.addxtype(Roo.apply({}, this.menu));
3719         }
3720
3721        
3722         this.el.select('a',true).on('click', this.onClick, this);
3723         // at this point parent should be available..
3724         this.parent().register(this);
3725     },
3726     
3727     onClick : function(e)
3728     {
3729          
3730         if(this.preventDefault){
3731             e.preventDefault();
3732         }
3733         if (this.disabled) {
3734             return;
3735         }
3736         
3737         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3738         if (tg && tg.transition) {
3739             Roo.log("waiting for the transitionend");
3740             return;
3741         }
3742         
3743         Roo.log("fire event clicked");
3744         if(this.fireEvent('click', this, e) === false){
3745             return;
3746         };
3747         var p = this.parent();
3748         if (['tabs','pills'].indexOf(p.type)!==-1) {
3749             if (typeof(p.setActiveItem) !== 'undefined') {
3750                 p.setActiveItem(this);
3751             }
3752         }
3753         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3754         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3755             // remove the collapsed menu expand...
3756             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3757         }
3758         
3759     },
3760     
3761     isActive: function () {
3762         return this.active
3763     },
3764     setActive : function(state, fire, is_was_active)
3765     {
3766         if (this.active && !state & this.navId) {
3767             this.was_active = true;
3768             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3769             if (nv) {
3770                 nv.clearWasActive(this);
3771             }
3772             
3773         }
3774         this.active = state;
3775         
3776         if (!state ) {
3777             this.el.removeClass('active');
3778         } else if (!this.el.hasClass('active')) {
3779             this.el.addClass('active');
3780         }
3781         if (fire) {
3782             this.fireEvent('changed', this, state);
3783         }
3784         
3785         // show a panel if it's registered and related..
3786         
3787         if (!this.navId || !this.tabId || !state || is_was_active) {
3788             return;
3789         }
3790         
3791         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3792         if (!tg) {
3793             return;
3794         }
3795         var pan = tg.getPanelByName(this.tabId);
3796         if (!pan) {
3797             return;
3798         }
3799         // if we can not flip to new panel - go back to old nav highlight..
3800         if (false == tg.showPanel(pan)) {
3801             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3802             if (nv) {
3803                 var onav = nv.getWasActive();
3804                 if (onav) {
3805                     onav.setActive(true, false, true);
3806                 }
3807             }
3808             
3809         }
3810         
3811         
3812         
3813     },
3814      // this should not be here...
3815     setDisabled : function(state)
3816     {
3817         this.disabled = state;
3818         if (!state ) {
3819             this.el.removeClass('disabled');
3820         } else if (!this.el.hasClass('disabled')) {
3821             this.el.addClass('disabled');
3822         }
3823         
3824     }
3825 });
3826  
3827
3828  /*
3829  * - LGPL
3830  *
3831  * sidebar item
3832  *
3833  *  li
3834  *    <span> icon </span>
3835  *    <span> text </span>
3836  *    <span>badge </span>
3837  */
3838
3839 /**
3840  * @class Roo.bootstrap.NavSidebarItem
3841  * @extends Roo.bootstrap.NavItem
3842  * Bootstrap Navbar.NavSidebarItem class
3843  * @constructor
3844  * Create a new Navbar Button
3845  * @param {Object} config The config object
3846  */
3847 Roo.bootstrap.NavSidebarItem = function(config){
3848     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3849     this.addEvents({
3850         // raw events
3851         /**
3852          * @event click
3853          * The raw click event for the entire grid.
3854          * @param {Roo.EventObject} e
3855          */
3856         "click" : true,
3857          /**
3858             * @event changed
3859             * Fires when the active item active state changes
3860             * @param {Roo.bootstrap.NavSidebarItem} this
3861             * @param {boolean} state the new state
3862              
3863          */
3864         'changed': true
3865     });
3866    
3867 };
3868
3869 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3870     
3871     
3872     getAutoCreate : function(){
3873         
3874         
3875         var a = {
3876                 tag: 'a',
3877                 href : this.href || '#',
3878                 cls: '',
3879                 html : '',
3880                 cn : []
3881         };
3882         var cfg = {
3883             tag: 'li',
3884             cls: '',
3885             cn: [ a ]
3886         }
3887         var span = {
3888             tag: 'span',
3889             html : this.html || ''
3890         }
3891         
3892         
3893         if (this.active) {
3894             cfg.cls += ' active';
3895         }
3896         
3897         // left icon..
3898         if (this.glyphicon || this.icon) {
3899             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3900             a.cn.push({ tag : 'i', cls : c }) ;
3901         }
3902         // html..
3903         a.cn.push(span);
3904         // then badge..
3905         if (this.badge !== '') {
3906             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3907         }
3908         // fi
3909         if (this.menu) {
3910             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3911             a.cls += 'dropdown-toggle treeview' ;
3912             
3913         }
3914         
3915         
3916         
3917         return cfg;
3918          
3919            
3920     }
3921    
3922      
3923  
3924 });
3925  
3926
3927  /*
3928  * - LGPL
3929  *
3930  * row
3931  * 
3932  */
3933
3934 /**
3935  * @class Roo.bootstrap.Row
3936  * @extends Roo.bootstrap.Component
3937  * Bootstrap Row class (contains columns...)
3938  * 
3939  * @constructor
3940  * Create a new Row
3941  * @param {Object} config The config object
3942  */
3943
3944 Roo.bootstrap.Row = function(config){
3945     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3946 };
3947
3948 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3949     
3950     getAutoCreate : function(){
3951        return {
3952             cls: 'row clearfix'
3953        };
3954     }
3955     
3956     
3957 });
3958
3959  
3960
3961  /*
3962  * - LGPL
3963  *
3964  * element
3965  * 
3966  */
3967
3968 /**
3969  * @class Roo.bootstrap.Element
3970  * @extends Roo.bootstrap.Component
3971  * Bootstrap Element class
3972  * @cfg {String} html contents of the element
3973  * @cfg {String} tag tag of the element
3974  * @cfg {String} cls class of the element
3975  * 
3976  * @constructor
3977  * Create a new Element
3978  * @param {Object} config The config object
3979  */
3980
3981 Roo.bootstrap.Element = function(config){
3982     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3983 };
3984
3985 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3986     
3987     tag: 'div',
3988     cls: '',
3989     html: '',
3990      
3991     
3992     getAutoCreate : function(){
3993         
3994         var cfg = {
3995             tag: this.tag,
3996             cls: this.cls,
3997             html: this.html
3998         }
3999         
4000         
4001         
4002         return cfg;
4003     }
4004    
4005 });
4006
4007  
4008
4009  /*
4010  * - LGPL
4011  *
4012  * pagination
4013  * 
4014  */
4015
4016 /**
4017  * @class Roo.bootstrap.Pagination
4018  * @extends Roo.bootstrap.Component
4019  * Bootstrap Pagination class
4020  * @cfg {String} size xs | sm | md | lg
4021  * @cfg {Boolean} inverse false | true
4022  * 
4023  * @constructor
4024  * Create a new Pagination
4025  * @param {Object} config The config object
4026  */
4027
4028 Roo.bootstrap.Pagination = function(config){
4029     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4030 };
4031
4032 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4033     
4034     cls: false,
4035     size: false,
4036     inverse: false,
4037     
4038     getAutoCreate : function(){
4039         var cfg = {
4040             tag: 'ul',
4041                 cls: 'pagination'
4042         };
4043         if (this.inverse) {
4044             cfg.cls += ' inverse';
4045         }
4046         if (this.html) {
4047             cfg.html=this.html;
4048         }
4049         if (this.cls) {
4050             cfg.cls += " " + this.cls;
4051         }
4052         return cfg;
4053     }
4054    
4055 });
4056
4057  
4058
4059  /*
4060  * - LGPL
4061  *
4062  * Pagination item
4063  * 
4064  */
4065
4066
4067 /**
4068  * @class Roo.bootstrap.PaginationItem
4069  * @extends Roo.bootstrap.Component
4070  * Bootstrap PaginationItem class
4071  * @cfg {String} html text
4072  * @cfg {String} href the link
4073  * @cfg {Boolean} preventDefault (true | false) default true
4074  * @cfg {Boolean} active (true | false) default false
4075  * 
4076  * 
4077  * @constructor
4078  * Create a new PaginationItem
4079  * @param {Object} config The config object
4080  */
4081
4082
4083 Roo.bootstrap.PaginationItem = function(config){
4084     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4085     this.addEvents({
4086         // raw events
4087         /**
4088          * @event click
4089          * The raw click event for the entire grid.
4090          * @param {Roo.EventObject} e
4091          */
4092         "click" : true
4093     });
4094 };
4095
4096 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4097     
4098     href : false,
4099     html : false,
4100     preventDefault: true,
4101     active : false,
4102     cls : false,
4103     
4104     getAutoCreate : function(){
4105         var cfg= {
4106             tag: 'li',
4107             cn: [
4108                 {
4109                     tag : 'a',
4110                     href : this.href ? this.href : '#',
4111                     html : this.html ? this.html : ''
4112                 }
4113             ]
4114         };
4115         
4116         if(this.cls){
4117             cfg.cls = this.cls;
4118         }
4119         
4120         if(this.active){
4121             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4122         }
4123         
4124         return cfg;
4125     },
4126     
4127     initEvents: function() {
4128         
4129         this.el.on('click', this.onClick, this);
4130         
4131     },
4132     onClick : function(e)
4133     {
4134         Roo.log('PaginationItem on click ');
4135         if(this.preventDefault){
4136             e.preventDefault();
4137         }
4138         
4139         this.fireEvent('click', this, e);
4140     }
4141    
4142 });
4143
4144  
4145
4146  /*
4147  * - LGPL
4148  *
4149  * slider
4150  * 
4151  */
4152
4153
4154 /**
4155  * @class Roo.bootstrap.Slider
4156  * @extends Roo.bootstrap.Component
4157  * Bootstrap Slider class
4158  *    
4159  * @constructor
4160  * Create a new Slider
4161  * @param {Object} config The config object
4162  */
4163
4164 Roo.bootstrap.Slider = function(config){
4165     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4166 };
4167
4168 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4169     
4170     getAutoCreate : function(){
4171         
4172         var cfg = {
4173             tag: 'div',
4174             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4175             cn: [
4176                 {
4177                     tag: 'a',
4178                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4179                 }
4180             ]
4181         }
4182         
4183         return cfg;
4184     }
4185    
4186 });
4187
4188  /*
4189  * Based on:
4190  * Ext JS Library 1.1.1
4191  * Copyright(c) 2006-2007, Ext JS, LLC.
4192  *
4193  * Originally Released Under LGPL - original licence link has changed is not relivant.
4194  *
4195  * Fork - LGPL
4196  * <script type="text/javascript">
4197  */
4198  
4199
4200 /**
4201  * @class Roo.grid.ColumnModel
4202  * @extends Roo.util.Observable
4203  * This is the default implementation of a ColumnModel used by the Grid. It defines
4204  * the columns in the grid.
4205  * <br>Usage:<br>
4206  <pre><code>
4207  var colModel = new Roo.grid.ColumnModel([
4208         {header: "Ticker", width: 60, sortable: true, locked: true},
4209         {header: "Company Name", width: 150, sortable: true},
4210         {header: "Market Cap.", width: 100, sortable: true},
4211         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4212         {header: "Employees", width: 100, sortable: true, resizable: false}
4213  ]);
4214  </code></pre>
4215  * <p>
4216  
4217  * The config options listed for this class are options which may appear in each
4218  * individual column definition.
4219  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4220  * @constructor
4221  * @param {Object} config An Array of column config objects. See this class's
4222  * config objects for details.
4223 */
4224 Roo.grid.ColumnModel = function(config){
4225         /**
4226      * The config passed into the constructor
4227      */
4228     this.config = config;
4229     this.lookup = {};
4230
4231     // if no id, create one
4232     // if the column does not have a dataIndex mapping,
4233     // map it to the order it is in the config
4234     for(var i = 0, len = config.length; i < len; i++){
4235         var c = config[i];
4236         if(typeof c.dataIndex == "undefined"){
4237             c.dataIndex = i;
4238         }
4239         if(typeof c.renderer == "string"){
4240             c.renderer = Roo.util.Format[c.renderer];
4241         }
4242         if(typeof c.id == "undefined"){
4243             c.id = Roo.id();
4244         }
4245         if(c.editor && c.editor.xtype){
4246             c.editor  = Roo.factory(c.editor, Roo.grid);
4247         }
4248         if(c.editor && c.editor.isFormField){
4249             c.editor = new Roo.grid.GridEditor(c.editor);
4250         }
4251         this.lookup[c.id] = c;
4252     }
4253
4254     /**
4255      * The width of columns which have no width specified (defaults to 100)
4256      * @type Number
4257      */
4258     this.defaultWidth = 100;
4259
4260     /**
4261      * Default sortable of columns which have no sortable specified (defaults to false)
4262      * @type Boolean
4263      */
4264     this.defaultSortable = false;
4265
4266     this.addEvents({
4267         /**
4268              * @event widthchange
4269              * Fires when the width of a column changes.
4270              * @param {ColumnModel} this
4271              * @param {Number} columnIndex The column index
4272              * @param {Number} newWidth The new width
4273              */
4274             "widthchange": true,
4275         /**
4276              * @event headerchange
4277              * Fires when the text of a header changes.
4278              * @param {ColumnModel} this
4279              * @param {Number} columnIndex The column index
4280              * @param {Number} newText The new header text
4281              */
4282             "headerchange": true,
4283         /**
4284              * @event hiddenchange
4285              * Fires when a column is hidden or "unhidden".
4286              * @param {ColumnModel} this
4287              * @param {Number} columnIndex The column index
4288              * @param {Boolean} hidden true if hidden, false otherwise
4289              */
4290             "hiddenchange": true,
4291             /**
4292          * @event columnmoved
4293          * Fires when a column is moved.
4294          * @param {ColumnModel} this
4295          * @param {Number} oldIndex
4296          * @param {Number} newIndex
4297          */
4298         "columnmoved" : true,
4299         /**
4300          * @event columlockchange
4301          * Fires when a column's locked state is changed
4302          * @param {ColumnModel} this
4303          * @param {Number} colIndex
4304          * @param {Boolean} locked true if locked
4305          */
4306         "columnlockchange" : true
4307     });
4308     Roo.grid.ColumnModel.superclass.constructor.call(this);
4309 };
4310 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4311     /**
4312      * @cfg {String} header The header text to display in the Grid view.
4313      */
4314     /**
4315      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4316      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4317      * specified, the column's index is used as an index into the Record's data Array.
4318      */
4319     /**
4320      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4321      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4322      */
4323     /**
4324      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4325      * Defaults to the value of the {@link #defaultSortable} property.
4326      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4327      */
4328     /**
4329      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4330      */
4331     /**
4332      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4333      */
4334     /**
4335      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4336      */
4337     /**
4338      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4339      */
4340     /**
4341      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4342      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4343      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4344      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4345      */
4346        /**
4347      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4348      */
4349     /**
4350      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4351      */
4352
4353     /**
4354      * Returns the id of the column at the specified index.
4355      * @param {Number} index The column index
4356      * @return {String} the id
4357      */
4358     getColumnId : function(index){
4359         return this.config[index].id;
4360     },
4361
4362     /**
4363      * Returns the column for a specified id.
4364      * @param {String} id The column id
4365      * @return {Object} the column
4366      */
4367     getColumnById : function(id){
4368         return this.lookup[id];
4369     },
4370
4371     
4372     /**
4373      * Returns the column for a specified dataIndex.
4374      * @param {String} dataIndex The column dataIndex
4375      * @return {Object|Boolean} the column or false if not found
4376      */
4377     getColumnByDataIndex: function(dataIndex){
4378         var index = this.findColumnIndex(dataIndex);
4379         return index > -1 ? this.config[index] : false;
4380     },
4381     
4382     /**
4383      * Returns the index for a specified column id.
4384      * @param {String} id The column id
4385      * @return {Number} the index, or -1 if not found
4386      */
4387     getIndexById : function(id){
4388         for(var i = 0, len = this.config.length; i < len; i++){
4389             if(this.config[i].id == id){
4390                 return i;
4391             }
4392         }
4393         return -1;
4394     },
4395     
4396     /**
4397      * Returns the index for a specified column dataIndex.
4398      * @param {String} dataIndex The column dataIndex
4399      * @return {Number} the index, or -1 if not found
4400      */
4401     
4402     findColumnIndex : function(dataIndex){
4403         for(var i = 0, len = this.config.length; i < len; i++){
4404             if(this.config[i].dataIndex == dataIndex){
4405                 return i;
4406             }
4407         }
4408         return -1;
4409     },
4410     
4411     
4412     moveColumn : function(oldIndex, newIndex){
4413         var c = this.config[oldIndex];
4414         this.config.splice(oldIndex, 1);
4415         this.config.splice(newIndex, 0, c);
4416         this.dataMap = null;
4417         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4418     },
4419
4420     isLocked : function(colIndex){
4421         return this.config[colIndex].locked === true;
4422     },
4423
4424     setLocked : function(colIndex, value, suppressEvent){
4425         if(this.isLocked(colIndex) == value){
4426             return;
4427         }
4428         this.config[colIndex].locked = value;
4429         if(!suppressEvent){
4430             this.fireEvent("columnlockchange", this, colIndex, value);
4431         }
4432     },
4433
4434     getTotalLockedWidth : function(){
4435         var totalWidth = 0;
4436         for(var i = 0; i < this.config.length; i++){
4437             if(this.isLocked(i) && !this.isHidden(i)){
4438                 this.totalWidth += this.getColumnWidth(i);
4439             }
4440         }
4441         return totalWidth;
4442     },
4443
4444     getLockedCount : function(){
4445         for(var i = 0, len = this.config.length; i < len; i++){
4446             if(!this.isLocked(i)){
4447                 return i;
4448             }
4449         }
4450     },
4451
4452     /**
4453      * Returns the number of columns.
4454      * @return {Number}
4455      */
4456     getColumnCount : function(visibleOnly){
4457         if(visibleOnly === true){
4458             var c = 0;
4459             for(var i = 0, len = this.config.length; i < len; i++){
4460                 if(!this.isHidden(i)){
4461                     c++;
4462                 }
4463             }
4464             return c;
4465         }
4466         return this.config.length;
4467     },
4468
4469     /**
4470      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4471      * @param {Function} fn
4472      * @param {Object} scope (optional)
4473      * @return {Array} result
4474      */
4475     getColumnsBy : function(fn, scope){
4476         var r = [];
4477         for(var i = 0, len = this.config.length; i < len; i++){
4478             var c = this.config[i];
4479             if(fn.call(scope||this, c, i) === true){
4480                 r[r.length] = c;
4481             }
4482         }
4483         return r;
4484     },
4485
4486     /**
4487      * Returns true if the specified column is sortable.
4488      * @param {Number} col The column index
4489      * @return {Boolean}
4490      */
4491     isSortable : function(col){
4492         if(typeof this.config[col].sortable == "undefined"){
4493             return this.defaultSortable;
4494         }
4495         return this.config[col].sortable;
4496     },
4497
4498     /**
4499      * Returns the rendering (formatting) function defined for the column.
4500      * @param {Number} col The column index.
4501      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4502      */
4503     getRenderer : function(col){
4504         if(!this.config[col].renderer){
4505             return Roo.grid.ColumnModel.defaultRenderer;
4506         }
4507         return this.config[col].renderer;
4508     },
4509
4510     /**
4511      * Sets the rendering (formatting) function for a column.
4512      * @param {Number} col The column index
4513      * @param {Function} fn The function to use to process the cell's raw data
4514      * to return HTML markup for the grid view. The render function is called with
4515      * the following parameters:<ul>
4516      * <li>Data value.</li>
4517      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4518      * <li>css A CSS style string to apply to the table cell.</li>
4519      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4520      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4521      * <li>Row index</li>
4522      * <li>Column index</li>
4523      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4524      */
4525     setRenderer : function(col, fn){
4526         this.config[col].renderer = fn;
4527     },
4528
4529     /**
4530      * Returns the width for the specified column.
4531      * @param {Number} col The column index
4532      * @return {Number}
4533      */
4534     getColumnWidth : function(col){
4535         return this.config[col].width * 1 || this.defaultWidth;
4536     },
4537
4538     /**
4539      * Sets the width for a column.
4540      * @param {Number} col The column index
4541      * @param {Number} width The new width
4542      */
4543     setColumnWidth : function(col, width, suppressEvent){
4544         this.config[col].width = width;
4545         this.totalWidth = null;
4546         if(!suppressEvent){
4547              this.fireEvent("widthchange", this, col, width);
4548         }
4549     },
4550
4551     /**
4552      * Returns the total width of all columns.
4553      * @param {Boolean} includeHidden True to include hidden column widths
4554      * @return {Number}
4555      */
4556     getTotalWidth : function(includeHidden){
4557         if(!this.totalWidth){
4558             this.totalWidth = 0;
4559             for(var i = 0, len = this.config.length; i < len; i++){
4560                 if(includeHidden || !this.isHidden(i)){
4561                     this.totalWidth += this.getColumnWidth(i);
4562                 }
4563             }
4564         }
4565         return this.totalWidth;
4566     },
4567
4568     /**
4569      * Returns the header for the specified column.
4570      * @param {Number} col The column index
4571      * @return {String}
4572      */
4573     getColumnHeader : function(col){
4574         return this.config[col].header;
4575     },
4576
4577     /**
4578      * Sets the header for a column.
4579      * @param {Number} col The column index
4580      * @param {String} header The new header
4581      */
4582     setColumnHeader : function(col, header){
4583         this.config[col].header = header;
4584         this.fireEvent("headerchange", this, col, header);
4585     },
4586
4587     /**
4588      * Returns the tooltip for the specified column.
4589      * @param {Number} col The column index
4590      * @return {String}
4591      */
4592     getColumnTooltip : function(col){
4593             return this.config[col].tooltip;
4594     },
4595     /**
4596      * Sets the tooltip for a column.
4597      * @param {Number} col The column index
4598      * @param {String} tooltip The new tooltip
4599      */
4600     setColumnTooltip : function(col, tooltip){
4601             this.config[col].tooltip = tooltip;
4602     },
4603
4604     /**
4605      * Returns the dataIndex for the specified column.
4606      * @param {Number} col The column index
4607      * @return {Number}
4608      */
4609     getDataIndex : function(col){
4610         return this.config[col].dataIndex;
4611     },
4612
4613     /**
4614      * Sets the dataIndex for a column.
4615      * @param {Number} col The column index
4616      * @param {Number} dataIndex The new dataIndex
4617      */
4618     setDataIndex : function(col, dataIndex){
4619         this.config[col].dataIndex = dataIndex;
4620     },
4621
4622     
4623     
4624     /**
4625      * Returns true if the cell is editable.
4626      * @param {Number} colIndex The column index
4627      * @param {Number} rowIndex The row index
4628      * @return {Boolean}
4629      */
4630     isCellEditable : function(colIndex, rowIndex){
4631         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4632     },
4633
4634     /**
4635      * Returns the editor defined for the cell/column.
4636      * return false or null to disable editing.
4637      * @param {Number} colIndex The column index
4638      * @param {Number} rowIndex The row index
4639      * @return {Object}
4640      */
4641     getCellEditor : function(colIndex, rowIndex){
4642         return this.config[colIndex].editor;
4643     },
4644
4645     /**
4646      * Sets if a column is editable.
4647      * @param {Number} col The column index
4648      * @param {Boolean} editable True if the column is editable
4649      */
4650     setEditable : function(col, editable){
4651         this.config[col].editable = editable;
4652     },
4653
4654
4655     /**
4656      * Returns true if the column is hidden.
4657      * @param {Number} colIndex The column index
4658      * @return {Boolean}
4659      */
4660     isHidden : function(colIndex){
4661         return this.config[colIndex].hidden;
4662     },
4663
4664
4665     /**
4666      * Returns true if the column width cannot be changed
4667      */
4668     isFixed : function(colIndex){
4669         return this.config[colIndex].fixed;
4670     },
4671
4672     /**
4673      * Returns true if the column can be resized
4674      * @return {Boolean}
4675      */
4676     isResizable : function(colIndex){
4677         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4678     },
4679     /**
4680      * Sets if a column is hidden.
4681      * @param {Number} colIndex The column index
4682      * @param {Boolean} hidden True if the column is hidden
4683      */
4684     setHidden : function(colIndex, hidden){
4685         this.config[colIndex].hidden = hidden;
4686         this.totalWidth = null;
4687         this.fireEvent("hiddenchange", this, colIndex, hidden);
4688     },
4689
4690     /**
4691      * Sets the editor for a column.
4692      * @param {Number} col The column index
4693      * @param {Object} editor The editor object
4694      */
4695     setEditor : function(col, editor){
4696         this.config[col].editor = editor;
4697     }
4698 });
4699
4700 Roo.grid.ColumnModel.defaultRenderer = function(value){
4701         if(typeof value == "string" && value.length < 1){
4702             return "&#160;";
4703         }
4704         return value;
4705 };
4706
4707 // Alias for backwards compatibility
4708 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4709 /*
4710  * Based on:
4711  * Ext JS Library 1.1.1
4712  * Copyright(c) 2006-2007, Ext JS, LLC.
4713  *
4714  * Originally Released Under LGPL - original licence link has changed is not relivant.
4715  *
4716  * Fork - LGPL
4717  * <script type="text/javascript">
4718  */
4719  
4720 /**
4721  * @class Roo.LoadMask
4722  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4723  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4724  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4725  * element's UpdateManager load indicator and will be destroyed after the initial load.
4726  * @constructor
4727  * Create a new LoadMask
4728  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4729  * @param {Object} config The config object
4730  */
4731 Roo.LoadMask = function(el, config){
4732     this.el = Roo.get(el);
4733     Roo.apply(this, config);
4734     if(this.store){
4735         this.store.on('beforeload', this.onBeforeLoad, this);
4736         this.store.on('load', this.onLoad, this);
4737         this.store.on('loadexception', this.onLoadException, this);
4738         this.removeMask = false;
4739     }else{
4740         var um = this.el.getUpdateManager();
4741         um.showLoadIndicator = false; // disable the default indicator
4742         um.on('beforeupdate', this.onBeforeLoad, this);
4743         um.on('update', this.onLoad, this);
4744         um.on('failure', this.onLoad, this);
4745         this.removeMask = true;
4746     }
4747 };
4748
4749 Roo.LoadMask.prototype = {
4750     /**
4751      * @cfg {Boolean} removeMask
4752      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4753      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4754      */
4755     /**
4756      * @cfg {String} msg
4757      * The text to display in a centered loading message box (defaults to 'Loading...')
4758      */
4759     msg : 'Loading...',
4760     /**
4761      * @cfg {String} msgCls
4762      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4763      */
4764     msgCls : 'x-mask-loading',
4765
4766     /**
4767      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4768      * @type Boolean
4769      */
4770     disabled: false,
4771
4772     /**
4773      * Disables the mask to prevent it from being displayed
4774      */
4775     disable : function(){
4776        this.disabled = true;
4777     },
4778
4779     /**
4780      * Enables the mask so that it can be displayed
4781      */
4782     enable : function(){
4783         this.disabled = false;
4784     },
4785     
4786     onLoadException : function()
4787     {
4788         Roo.log(arguments);
4789         
4790         if (typeof(arguments[3]) != 'undefined') {
4791             Roo.MessageBox.alert("Error loading",arguments[3]);
4792         } 
4793         /*
4794         try {
4795             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4796                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4797             }   
4798         } catch(e) {
4799             
4800         }
4801         */
4802     
4803         
4804         
4805         this.el.unmask(this.removeMask);
4806     },
4807     // private
4808     onLoad : function()
4809     {
4810         this.el.unmask(this.removeMask);
4811     },
4812
4813     // private
4814     onBeforeLoad : function(){
4815         if(!this.disabled){
4816             this.el.mask(this.msg, this.msgCls);
4817         }
4818     },
4819
4820     // private
4821     destroy : function(){
4822         if(this.store){
4823             this.store.un('beforeload', this.onBeforeLoad, this);
4824             this.store.un('load', this.onLoad, this);
4825             this.store.un('loadexception', this.onLoadException, this);
4826         }else{
4827             var um = this.el.getUpdateManager();
4828             um.un('beforeupdate', this.onBeforeLoad, this);
4829             um.un('update', this.onLoad, this);
4830             um.un('failure', this.onLoad, this);
4831         }
4832     }
4833 };/*
4834  * - LGPL
4835  *
4836  * table
4837  * 
4838  */
4839
4840 /**
4841  * @class Roo.bootstrap.Table
4842  * @extends Roo.bootstrap.Component
4843  * Bootstrap Table class
4844  * @cfg {String} cls table class
4845  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4846  * @cfg {String} bgcolor Specifies the background color for a table
4847  * @cfg {Number} border Specifies whether the table cells should have borders or not
4848  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4849  * @cfg {Number} cellspacing Specifies the space between cells
4850  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4851  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4852  * @cfg {String} sortable Specifies that the table should be sortable
4853  * @cfg {String} summary Specifies a summary of the content of a table
4854  * @cfg {Number} width Specifies the width of a table
4855  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4856  * 
4857  * @cfg {boolean} striped Should the rows be alternative striped
4858  * @cfg {boolean} bordered Add borders to the table
4859  * @cfg {boolean} hover Add hover highlighting
4860  * @cfg {boolean} condensed Format condensed
4861  * @cfg {boolean} responsive Format condensed
4862  * @cfg {Boolean} loadMask (true|false) default false
4863  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4864  * @cfg {Boolean} thead (true|false) generate thead, default true
4865  * @cfg {Boolean} RowSelection (true|false) default false
4866  * @cfg {Boolean} CellSelection (true|false) default false
4867  *
4868  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4869  
4870  * 
4871  * @constructor
4872  * Create a new Table
4873  * @param {Object} config The config object
4874  */
4875
4876 Roo.bootstrap.Table = function(config){
4877     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4878     
4879     if (this.sm) {
4880         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4881         this.sm = this.selModel;
4882         this.sm.xmodule = this.xmodule || false;
4883     }
4884     if (this.cm && typeof(this.cm.config) == 'undefined') {
4885         this.colModel = new Roo.grid.ColumnModel(this.cm);
4886         this.cm = this.colModel;
4887         this.cm.xmodule = this.xmodule || false;
4888     }
4889     if (this.store) {
4890         this.store= Roo.factory(this.store, Roo.data);
4891         this.ds = this.store;
4892         this.ds.xmodule = this.xmodule || false;
4893          
4894     }
4895     if (this.footer && this.store) {
4896         this.footer.dataSource = this.ds;
4897         this.footer = Roo.factory(this.footer);
4898     }
4899     
4900     /** @private */
4901     this.addEvents({
4902         /**
4903          * @event cellclick
4904          * Fires when a cell is clicked
4905          * @param {Roo.bootstrap.Table} this
4906          * @param {Roo.Element} el
4907          * @param {Number} rowIndex
4908          * @param {Number} columnIndex
4909          * @param {Roo.EventObject} e
4910          */
4911         "cellclick" : true,
4912         /**
4913          * @event celldblclick
4914          * Fires when a cell is double clicked
4915          * @param {Roo.bootstrap.Table} this
4916          * @param {Roo.Element} el
4917          * @param {Number} rowIndex
4918          * @param {Number} columnIndex
4919          * @param {Roo.EventObject} e
4920          */
4921         "celldblclick" : true,
4922         /**
4923          * @event rowclick
4924          * Fires when a row is clicked
4925          * @param {Roo.bootstrap.Table} this
4926          * @param {Roo.Element} el
4927          * @param {Number} rowIndex
4928          * @param {Roo.EventObject} e
4929          */
4930         "rowclick" : true,
4931         /**
4932          * @event rowdblclick
4933          * Fires when a row is double clicked
4934          * @param {Roo.bootstrap.Table} this
4935          * @param {Roo.Element} el
4936          * @param {Number} rowIndex
4937          * @param {Roo.EventObject} e
4938          */
4939         "rowdblclick" : true,
4940         /**
4941          * @event mouseover
4942          * Fires when a mouseover occur
4943          * @param {Roo.bootstrap.Table} this
4944          * @param {Roo.Element} el
4945          * @param {Number} rowIndex
4946          * @param {Number} columnIndex
4947          * @param {Roo.EventObject} e
4948          */
4949         "mouseover" : true,
4950         /**
4951          * @event mouseout
4952          * Fires when a mouseout occur
4953          * @param {Roo.bootstrap.Table} this
4954          * @param {Roo.Element} el
4955          * @param {Number} rowIndex
4956          * @param {Number} columnIndex
4957          * @param {Roo.EventObject} e
4958          */
4959         "mouseout" : true,
4960         /**
4961          * @event rowclass
4962          * Fires when a row is rendered, so you can change add a style to it.
4963          * @param {Roo.bootstrap.Table} this
4964          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4965          */
4966         'rowclass' : true
4967         
4968     });
4969 };
4970
4971 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4972     
4973     cls: false,
4974     align: false,
4975     bgcolor: false,
4976     border: false,
4977     cellpadding: false,
4978     cellspacing: false,
4979     frame: false,
4980     rules: false,
4981     sortable: false,
4982     summary: false,
4983     width: false,
4984     striped : false,
4985     bordered: false,
4986     hover:  false,
4987     condensed : false,
4988     responsive : false,
4989     sm : false,
4990     cm : false,
4991     store : false,
4992     loadMask : false,
4993     tfoot : true,
4994     thead : true,
4995     RowSelection : false,
4996     CellSelection : false,
4997     layout : false,
4998     
4999     // Roo.Element - the tbody
5000     mainBody: false, 
5001     
5002     getAutoCreate : function(){
5003         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5004         
5005         cfg = {
5006             tag: 'table',
5007             cls : 'table',
5008             cn : []
5009         }
5010             
5011         if (this.striped) {
5012             cfg.cls += ' table-striped';
5013         }
5014         
5015         if (this.hover) {
5016             cfg.cls += ' table-hover';
5017         }
5018         if (this.bordered) {
5019             cfg.cls += ' table-bordered';
5020         }
5021         if (this.condensed) {
5022             cfg.cls += ' table-condensed';
5023         }
5024         if (this.responsive) {
5025             cfg.cls += ' table-responsive';
5026         }
5027         
5028         if (this.cls) {
5029             cfg.cls+=  ' ' +this.cls;
5030         }
5031         
5032         // this lot should be simplifed...
5033         
5034         if (this.align) {
5035             cfg.align=this.align;
5036         }
5037         if (this.bgcolor) {
5038             cfg.bgcolor=this.bgcolor;
5039         }
5040         if (this.border) {
5041             cfg.border=this.border;
5042         }
5043         if (this.cellpadding) {
5044             cfg.cellpadding=this.cellpadding;
5045         }
5046         if (this.cellspacing) {
5047             cfg.cellspacing=this.cellspacing;
5048         }
5049         if (this.frame) {
5050             cfg.frame=this.frame;
5051         }
5052         if (this.rules) {
5053             cfg.rules=this.rules;
5054         }
5055         if (this.sortable) {
5056             cfg.sortable=this.sortable;
5057         }
5058         if (this.summary) {
5059             cfg.summary=this.summary;
5060         }
5061         if (this.width) {
5062             cfg.width=this.width;
5063         }
5064         if (this.layout) {
5065             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5066         }
5067         
5068         if(this.store || this.cm){
5069             if(this.thead){
5070                 cfg.cn.push(this.renderHeader());
5071             }
5072             
5073             cfg.cn.push(this.renderBody());
5074             
5075             if(this.tfoot){
5076                 cfg.cn.push(this.renderFooter());
5077             }
5078             
5079             cfg.cls+=  ' TableGrid';
5080         }
5081         
5082         return { cn : [ cfg ] };
5083     },
5084     
5085     initEvents : function()
5086     {   
5087         if(!this.store || !this.cm){
5088             return;
5089         }
5090         
5091         //Roo.log('initEvents with ds!!!!');
5092         
5093         this.mainBody = this.el.select('tbody', true).first();
5094         
5095         
5096         var _this = this;
5097         
5098         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5099             e.on('click', _this.sort, _this);
5100         });
5101         
5102         this.el.on("click", this.onClick, this);
5103         this.el.on("dblclick", this.onDblClick, this);
5104         
5105         this.parent().el.setStyle('position', 'relative');
5106         if (this.footer) {
5107             this.footer.parentId = this.id;
5108             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5109         }
5110         
5111         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5112         
5113         this.store.on('load', this.onLoad, this);
5114         this.store.on('beforeload', this.onBeforeLoad, this);
5115         this.store.on('update', this.onUpdate, this);
5116         
5117     },
5118     
5119     onMouseover : function(e, el)
5120     {
5121         var cell = Roo.get(el);
5122         
5123         if(!cell){
5124             return;
5125         }
5126         
5127         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5128             cell = cell.findParent('td', false, true);
5129         }
5130         
5131         var row = cell.findParent('tr', false, true);
5132         var cellIndex = cell.dom.cellIndex;
5133         var rowIndex = row.dom.rowIndex - 1; // start from 0
5134         
5135         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5136         
5137     },
5138     
5139     onMouseout : function(e, el)
5140     {
5141         var cell = Roo.get(el);
5142         
5143         if(!cell){
5144             return;
5145         }
5146         
5147         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5148             cell = cell.findParent('td', false, true);
5149         }
5150         
5151         var row = cell.findParent('tr', false, true);
5152         var cellIndex = cell.dom.cellIndex;
5153         var rowIndex = row.dom.rowIndex - 1; // start from 0
5154         
5155         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5156         
5157     },
5158     
5159     onClick : function(e, el)
5160     {
5161         var cell = Roo.get(el);
5162         
5163         if(!cell || (!this.CellSelection && !this.RowSelection)){
5164             return;
5165         }
5166         
5167         
5168         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5169             cell = cell.findParent('td', false, true);
5170         }
5171         
5172         var row = cell.findParent('tr', false, true);
5173         var cellIndex = cell.dom.cellIndex;
5174         var rowIndex = row.dom.rowIndex - 1;
5175         
5176         if(this.CellSelection){
5177             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5178         }
5179         
5180         if(this.RowSelection){
5181             this.fireEvent('rowclick', this, row, rowIndex, e);
5182         }
5183         
5184         
5185     },
5186     
5187     onDblClick : function(e,el)
5188     {
5189         var cell = Roo.get(el);
5190         
5191         if(!cell || (!this.CellSelection && !this.RowSelection)){
5192             return;
5193         }
5194         
5195         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5196             cell = cell.findParent('td', false, true);
5197         }
5198         
5199         var row = cell.findParent('tr', false, true);
5200         var cellIndex = cell.dom.cellIndex;
5201         var rowIndex = row.dom.rowIndex - 1;
5202         
5203         if(this.CellSelection){
5204             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5205         }
5206         
5207         if(this.RowSelection){
5208             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5209         }
5210     },
5211     
5212     sort : function(e,el)
5213     {
5214         var col = Roo.get(el)
5215         
5216         if(!col.hasClass('sortable')){
5217             return;
5218         }
5219         
5220         var sort = col.attr('sort');
5221         var dir = 'ASC';
5222         
5223         if(col.hasClass('glyphicon-arrow-up')){
5224             dir = 'DESC';
5225         }
5226         
5227         this.store.sortInfo = {field : sort, direction : dir};
5228         
5229         if (this.footer) {
5230             Roo.log("calling footer first");
5231             this.footer.onClick('first');
5232         } else {
5233         
5234             this.store.load({ params : { start : 0 } });
5235         }
5236     },
5237     
5238     renderHeader : function()
5239     {
5240         var header = {
5241             tag: 'thead',
5242             cn : []
5243         };
5244         
5245         var cm = this.cm;
5246         
5247         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5248             
5249             var config = cm.config[i];
5250                     
5251             var c = {
5252                 tag: 'th',
5253                 style : '',
5254                 html: cm.getColumnHeader(i)
5255             };
5256             
5257             if(typeof(config.hidden) != 'undefined' && config.hidden){
5258                 c.style += ' display:none;';
5259             }
5260             
5261             if(typeof(config.dataIndex) != 'undefined'){
5262                 c.sort = config.dataIndex;
5263             }
5264             
5265             if(typeof(config.sortable) != 'undefined' && config.sortable){
5266                 c.cls = 'sortable';
5267             }
5268             
5269             if(typeof(config.align) != 'undefined' && config.align.length){
5270                 c.style += ' text-align:' + config.align + ';';
5271             }
5272             
5273             if(typeof(config.width) != 'undefined'){
5274                 c.style += ' width:' + config.width + 'px;';
5275             }
5276             
5277             header.cn.push(c)
5278         }
5279         
5280         return header;
5281     },
5282     
5283     renderBody : function()
5284     {
5285         var body = {
5286             tag: 'tbody',
5287             cn : [
5288                 {
5289                     tag: 'tr',
5290                     cn : [
5291                         {
5292                             tag : 'td',
5293                             colspan :  this.cm.getColumnCount()
5294                         }
5295                     ]
5296                 }
5297             ]
5298         };
5299         
5300         return body;
5301     },
5302     
5303     renderFooter : function()
5304     {
5305         var footer = {
5306             tag: 'tfoot',
5307             cn : [
5308                 {
5309                     tag: 'tr',
5310                     cn : [
5311                         {
5312                             tag : 'td',
5313                             colspan :  this.cm.getColumnCount()
5314                         }
5315                     ]
5316                 }
5317             ]
5318         };
5319         
5320         return footer;
5321     },
5322     
5323     
5324     
5325     onLoad : function()
5326     {
5327         Roo.log('ds onload');
5328         this.clear();
5329         
5330         var _this = this;
5331         var cm = this.cm;
5332         var ds = this.store;
5333         
5334         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5335             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5336             
5337             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5338                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5339             }
5340             
5341             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5342                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5343             }
5344         });
5345         
5346         var tbody =  this.mainBody;
5347               
5348         if(ds.getCount() > 0){
5349             ds.data.each(function(d,rowIndex){
5350                 var row =  this.renderRow(cm, ds, rowIndex);
5351                 
5352                 tbody.createChild(row);
5353                 
5354                 var _this = this;
5355                 
5356                 if(row.cellObjects.length){
5357                     Roo.each(row.cellObjects, function(r){
5358                         _this.renderCellObject(r);
5359                     })
5360                 }
5361                 
5362             }, this);
5363         }
5364         
5365         Roo.each(this.el.select('tbody td', true).elements, function(e){
5366             e.on('mouseover', _this.onMouseover, _this);
5367         });
5368         
5369         Roo.each(this.el.select('tbody td', true).elements, function(e){
5370             e.on('mouseout', _this.onMouseout, _this);
5371         });
5372
5373         //if(this.loadMask){
5374         //    this.maskEl.hide();
5375         //}
5376     },
5377     
5378     
5379     onUpdate : function(ds,record)
5380     {
5381         this.refreshRow(record);
5382     },
5383     onRemove : function(ds, record, index, isUpdate){
5384         if(isUpdate !== true){
5385             this.fireEvent("beforerowremoved", this, index, record);
5386         }
5387         var bt = this.mainBody.dom;
5388         if(bt.rows[index]){
5389             bt.removeChild(bt.rows[index]);
5390         }
5391         
5392         if(isUpdate !== true){
5393             //this.stripeRows(index);
5394             //this.syncRowHeights(index, index);
5395             //this.layout();
5396             this.fireEvent("rowremoved", this, index, record);
5397         }
5398     },
5399     
5400     
5401     refreshRow : function(record){
5402         var ds = this.store, index;
5403         if(typeof record == 'number'){
5404             index = record;
5405             record = ds.getAt(index);
5406         }else{
5407             index = ds.indexOf(record);
5408         }
5409         this.insertRow(ds, index, true);
5410         this.onRemove(ds, record, index+1, true);
5411         //this.syncRowHeights(index, index);
5412         //this.layout();
5413         this.fireEvent("rowupdated", this, index, record);
5414     },
5415     
5416     insertRow : function(dm, rowIndex, isUpdate){
5417         
5418         if(!isUpdate){
5419             this.fireEvent("beforerowsinserted", this, rowIndex);
5420         }
5421             //var s = this.getScrollState();
5422         var row = this.renderRow(this.cm, this.store, rowIndex);
5423         // insert before rowIndex..
5424         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5425         
5426         var _this = this;
5427                 
5428         if(row.cellObjects.length){
5429             Roo.each(row.cellObjects, function(r){
5430                 _this.renderCellObject(r);
5431             })
5432         }
5433             
5434         if(!isUpdate){
5435             this.fireEvent("rowsinserted", this, rowIndex);
5436             //this.syncRowHeights(firstRow, lastRow);
5437             //this.stripeRows(firstRow);
5438             //this.layout();
5439         }
5440         
5441     },
5442     
5443     
5444     getRowDom : function(rowIndex)
5445     {
5446         // not sure if I need to check this.. but let's do it anyway..
5447         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5448                 this.mainBody.dom.rows[rowIndex] : false
5449     },
5450     // returns the object tree for a tr..
5451   
5452     
5453     renderRow : function(cm, ds, rowIndex) {
5454         
5455         var d = ds.getAt(rowIndex);
5456         
5457         var row = {
5458             tag : 'tr',
5459             cn : []
5460         };
5461             
5462         var cellObjects = [];
5463         
5464         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5465             var config = cm.config[i];
5466             
5467             var renderer = cm.getRenderer(i);
5468             var value = '';
5469             var id = false;
5470             
5471             if(typeof(renderer) !== 'undefined'){
5472                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5473             }
5474             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5475             // and are rendered into the cells after the row is rendered - using the id for the element.
5476             
5477             if(typeof(value) === 'object'){
5478                 id = Roo.id();
5479                 cellObjects.push({
5480                     container : id,
5481                     cfg : value 
5482                 })
5483             }
5484             
5485             var rowcfg = {
5486                 record: d,
5487                 rowIndex : rowIndex,
5488                 colIndex : i,
5489                 rowClass : ''
5490             }
5491
5492             this.fireEvent('rowclass', this, rowcfg);
5493             
5494             var td = {
5495                 tag: 'td',
5496                 cls : rowcfg.rowClass,
5497                 style: '',
5498                 html: (typeof(value) === 'object') ? '' : value
5499             };
5500             
5501             if (id) {
5502                 td.id = id;
5503             }
5504             
5505             if(typeof(config.hidden) != 'undefined' && config.hidden){
5506                 td.style += ' display:none;';
5507             }
5508             
5509             if(typeof(config.align) != 'undefined' && config.align.length){
5510                 td.style += ' text-align:' + config.align + ';';
5511             }
5512             
5513             if(typeof(config.width) != 'undefined'){
5514                 td.style += ' width:' +  config.width + 'px;';
5515             }
5516              
5517             row.cn.push(td);
5518            
5519         }
5520         
5521         row.cellObjects = cellObjects;
5522         
5523         return row;
5524           
5525     },
5526     
5527     
5528     
5529     onBeforeLoad : function()
5530     {
5531         //Roo.log('ds onBeforeLoad');
5532         
5533         //this.clear();
5534         
5535         //if(this.loadMask){
5536         //    this.maskEl.show();
5537         //}
5538     },
5539     
5540     clear : function()
5541     {
5542         this.el.select('tbody', true).first().dom.innerHTML = '';
5543     },
5544     
5545     getSelectionModel : function(){
5546         if(!this.selModel){
5547             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5548         }
5549         return this.selModel;
5550     },
5551     /*
5552      * Render the Roo.bootstrap object from renderder
5553      */
5554     renderCellObject : function(r)
5555     {
5556         var _this = this;
5557         
5558         var t = r.cfg.render(r.container);
5559         
5560         if(r.cfg.cn){
5561             Roo.each(r.cfg.cn, function(c){
5562                 var child = {
5563                     container: t.getChildContainer(),
5564                     cfg: c
5565                 }
5566                 _this.renderCellObject(child);
5567             })
5568         }
5569     }
5570    
5571 });
5572
5573  
5574
5575  /*
5576  * - LGPL
5577  *
5578  * table cell
5579  * 
5580  */
5581
5582 /**
5583  * @class Roo.bootstrap.TableCell
5584  * @extends Roo.bootstrap.Component
5585  * Bootstrap TableCell class
5586  * @cfg {String} html cell contain text
5587  * @cfg {String} cls cell class
5588  * @cfg {String} tag cell tag (td|th) default td
5589  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5590  * @cfg {String} align Aligns the content in a cell
5591  * @cfg {String} axis Categorizes cells
5592  * @cfg {String} bgcolor Specifies the background color of a cell
5593  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5594  * @cfg {Number} colspan Specifies the number of columns a cell should span
5595  * @cfg {String} headers Specifies one or more header cells a cell is related to
5596  * @cfg {Number} height Sets the height of a cell
5597  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5598  * @cfg {Number} rowspan Sets the number of rows a cell should span
5599  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5600  * @cfg {String} valign Vertical aligns the content in a cell
5601  * @cfg {Number} width Specifies the width of a cell
5602  * 
5603  * @constructor
5604  * Create a new TableCell
5605  * @param {Object} config The config object
5606  */
5607
5608 Roo.bootstrap.TableCell = function(config){
5609     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5610 };
5611
5612 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5613     
5614     html: false,
5615     cls: false,
5616     tag: false,
5617     abbr: false,
5618     align: false,
5619     axis: false,
5620     bgcolor: false,
5621     charoff: false,
5622     colspan: false,
5623     headers: false,
5624     height: false,
5625     nowrap: false,
5626     rowspan: false,
5627     scope: false,
5628     valign: false,
5629     width: false,
5630     
5631     
5632     getAutoCreate : function(){
5633         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5634         
5635         cfg = {
5636             tag: 'td'
5637         }
5638         
5639         if(this.tag){
5640             cfg.tag = this.tag;
5641         }
5642         
5643         if (this.html) {
5644             cfg.html=this.html
5645         }
5646         if (this.cls) {
5647             cfg.cls=this.cls
5648         }
5649         if (this.abbr) {
5650             cfg.abbr=this.abbr
5651         }
5652         if (this.align) {
5653             cfg.align=this.align
5654         }
5655         if (this.axis) {
5656             cfg.axis=this.axis
5657         }
5658         if (this.bgcolor) {
5659             cfg.bgcolor=this.bgcolor
5660         }
5661         if (this.charoff) {
5662             cfg.charoff=this.charoff
5663         }
5664         if (this.colspan) {
5665             cfg.colspan=this.colspan
5666         }
5667         if (this.headers) {
5668             cfg.headers=this.headers
5669         }
5670         if (this.height) {
5671             cfg.height=this.height
5672         }
5673         if (this.nowrap) {
5674             cfg.nowrap=this.nowrap
5675         }
5676         if (this.rowspan) {
5677             cfg.rowspan=this.rowspan
5678         }
5679         if (this.scope) {
5680             cfg.scope=this.scope
5681         }
5682         if (this.valign) {
5683             cfg.valign=this.valign
5684         }
5685         if (this.width) {
5686             cfg.width=this.width
5687         }
5688         
5689         
5690         return cfg;
5691     }
5692    
5693 });
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * table row
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.TableRow
5706  * @extends Roo.bootstrap.Component
5707  * Bootstrap TableRow class
5708  * @cfg {String} cls row class
5709  * @cfg {String} align Aligns the content in a table row
5710  * @cfg {String} bgcolor Specifies a background color for a table row
5711  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5712  * @cfg {String} valign Vertical aligns the content in a table row
5713  * 
5714  * @constructor
5715  * Create a new TableRow
5716  * @param {Object} config The config object
5717  */
5718
5719 Roo.bootstrap.TableRow = function(config){
5720     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5721 };
5722
5723 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5724     
5725     cls: false,
5726     align: false,
5727     bgcolor: false,
5728     charoff: false,
5729     valign: false,
5730     
5731     getAutoCreate : function(){
5732         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5733         
5734         cfg = {
5735             tag: 'tr'
5736         }
5737             
5738         if(this.cls){
5739             cfg.cls = this.cls;
5740         }
5741         if(this.align){
5742             cfg.align = this.align;
5743         }
5744         if(this.bgcolor){
5745             cfg.bgcolor = this.bgcolor;
5746         }
5747         if(this.charoff){
5748             cfg.charoff = this.charoff;
5749         }
5750         if(this.valign){
5751             cfg.valign = this.valign;
5752         }
5753         
5754         return cfg;
5755     }
5756    
5757 });
5758
5759  
5760
5761  /*
5762  * - LGPL
5763  *
5764  * table body
5765  * 
5766  */
5767
5768 /**
5769  * @class Roo.bootstrap.TableBody
5770  * @extends Roo.bootstrap.Component
5771  * Bootstrap TableBody class
5772  * @cfg {String} cls element class
5773  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5774  * @cfg {String} align Aligns the content inside the element
5775  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5776  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5777  * 
5778  * @constructor
5779  * Create a new TableBody
5780  * @param {Object} config The config object
5781  */
5782
5783 Roo.bootstrap.TableBody = function(config){
5784     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5785 };
5786
5787 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5788     
5789     cls: false,
5790     tag: false,
5791     align: false,
5792     charoff: false,
5793     valign: false,
5794     
5795     getAutoCreate : function(){
5796         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5797         
5798         cfg = {
5799             tag: 'tbody'
5800         }
5801             
5802         if (this.cls) {
5803             cfg.cls=this.cls
5804         }
5805         if(this.tag){
5806             cfg.tag = this.tag;
5807         }
5808         
5809         if(this.align){
5810             cfg.align = this.align;
5811         }
5812         if(this.charoff){
5813             cfg.charoff = this.charoff;
5814         }
5815         if(this.valign){
5816             cfg.valign = this.valign;
5817         }
5818         
5819         return cfg;
5820     }
5821     
5822     
5823 //    initEvents : function()
5824 //    {
5825 //        
5826 //        if(!this.store){
5827 //            return;
5828 //        }
5829 //        
5830 //        this.store = Roo.factory(this.store, Roo.data);
5831 //        this.store.on('load', this.onLoad, this);
5832 //        
5833 //        this.store.load();
5834 //        
5835 //    },
5836 //    
5837 //    onLoad: function () 
5838 //    {   
5839 //        this.fireEvent('load', this);
5840 //    }
5841 //    
5842 //   
5843 });
5844
5845  
5846
5847  /*
5848  * Based on:
5849  * Ext JS Library 1.1.1
5850  * Copyright(c) 2006-2007, Ext JS, LLC.
5851  *
5852  * Originally Released Under LGPL - original licence link has changed is not relivant.
5853  *
5854  * Fork - LGPL
5855  * <script type="text/javascript">
5856  */
5857
5858 // as we use this in bootstrap.
5859 Roo.namespace('Roo.form');
5860  /**
5861  * @class Roo.form.Action
5862  * Internal Class used to handle form actions
5863  * @constructor
5864  * @param {Roo.form.BasicForm} el The form element or its id
5865  * @param {Object} config Configuration options
5866  */
5867
5868  
5869  
5870 // define the action interface
5871 Roo.form.Action = function(form, options){
5872     this.form = form;
5873     this.options = options || {};
5874 };
5875 /**
5876  * Client Validation Failed
5877  * @const 
5878  */
5879 Roo.form.Action.CLIENT_INVALID = 'client';
5880 /**
5881  * Server Validation Failed
5882  * @const 
5883  */
5884 Roo.form.Action.SERVER_INVALID = 'server';
5885  /**
5886  * Connect to Server Failed
5887  * @const 
5888  */
5889 Roo.form.Action.CONNECT_FAILURE = 'connect';
5890 /**
5891  * Reading Data from Server Failed
5892  * @const 
5893  */
5894 Roo.form.Action.LOAD_FAILURE = 'load';
5895
5896 Roo.form.Action.prototype = {
5897     type : 'default',
5898     failureType : undefined,
5899     response : undefined,
5900     result : undefined,
5901
5902     // interface method
5903     run : function(options){
5904
5905     },
5906
5907     // interface method
5908     success : function(response){
5909
5910     },
5911
5912     // interface method
5913     handleResponse : function(response){
5914
5915     },
5916
5917     // default connection failure
5918     failure : function(response){
5919         
5920         this.response = response;
5921         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5922         this.form.afterAction(this, false);
5923     },
5924
5925     processResponse : function(response){
5926         this.response = response;
5927         if(!response.responseText){
5928             return true;
5929         }
5930         this.result = this.handleResponse(response);
5931         return this.result;
5932     },
5933
5934     // utility functions used internally
5935     getUrl : function(appendParams){
5936         var url = this.options.url || this.form.url || this.form.el.dom.action;
5937         if(appendParams){
5938             var p = this.getParams();
5939             if(p){
5940                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5941             }
5942         }
5943         return url;
5944     },
5945
5946     getMethod : function(){
5947         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5948     },
5949
5950     getParams : function(){
5951         var bp = this.form.baseParams;
5952         var p = this.options.params;
5953         if(p){
5954             if(typeof p == "object"){
5955                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5956             }else if(typeof p == 'string' && bp){
5957                 p += '&' + Roo.urlEncode(bp);
5958             }
5959         }else if(bp){
5960             p = Roo.urlEncode(bp);
5961         }
5962         return p;
5963     },
5964
5965     createCallback : function(){
5966         return {
5967             success: this.success,
5968             failure: this.failure,
5969             scope: this,
5970             timeout: (this.form.timeout*1000),
5971             upload: this.form.fileUpload ? this.success : undefined
5972         };
5973     }
5974 };
5975
5976 Roo.form.Action.Submit = function(form, options){
5977     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5978 };
5979
5980 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5981     type : 'submit',
5982
5983     haveProgress : false,
5984     uploadComplete : false,
5985     
5986     // uploadProgress indicator.
5987     uploadProgress : function()
5988     {
5989         if (!this.form.progressUrl) {
5990             return;
5991         }
5992         
5993         if (!this.haveProgress) {
5994             Roo.MessageBox.progress("Uploading", "Uploading");
5995         }
5996         if (this.uploadComplete) {
5997            Roo.MessageBox.hide();
5998            return;
5999         }
6000         
6001         this.haveProgress = true;
6002    
6003         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6004         
6005         var c = new Roo.data.Connection();
6006         c.request({
6007             url : this.form.progressUrl,
6008             params: {
6009                 id : uid
6010             },
6011             method: 'GET',
6012             success : function(req){
6013                //console.log(data);
6014                 var rdata = false;
6015                 var edata;
6016                 try  {
6017                    rdata = Roo.decode(req.responseText)
6018                 } catch (e) {
6019                     Roo.log("Invalid data from server..");
6020                     Roo.log(edata);
6021                     return;
6022                 }
6023                 if (!rdata || !rdata.success) {
6024                     Roo.log(rdata);
6025                     Roo.MessageBox.alert(Roo.encode(rdata));
6026                     return;
6027                 }
6028                 var data = rdata.data;
6029                 
6030                 if (this.uploadComplete) {
6031                    Roo.MessageBox.hide();
6032                    return;
6033                 }
6034                    
6035                 if (data){
6036                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6037                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6038                     );
6039                 }
6040                 this.uploadProgress.defer(2000,this);
6041             },
6042        
6043             failure: function(data) {
6044                 Roo.log('progress url failed ');
6045                 Roo.log(data);
6046             },
6047             scope : this
6048         });
6049            
6050     },
6051     
6052     
6053     run : function()
6054     {
6055         // run get Values on the form, so it syncs any secondary forms.
6056         this.form.getValues();
6057         
6058         var o = this.options;
6059         var method = this.getMethod();
6060         var isPost = method == 'POST';
6061         if(o.clientValidation === false || this.form.isValid()){
6062             
6063             if (this.form.progressUrl) {
6064                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6065                     (new Date() * 1) + '' + Math.random());
6066                     
6067             } 
6068             
6069             
6070             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6071                 form:this.form.el.dom,
6072                 url:this.getUrl(!isPost),
6073                 method: method,
6074                 params:isPost ? this.getParams() : null,
6075                 isUpload: this.form.fileUpload
6076             }));
6077             
6078             this.uploadProgress();
6079
6080         }else if (o.clientValidation !== false){ // client validation failed
6081             this.failureType = Roo.form.Action.CLIENT_INVALID;
6082             this.form.afterAction(this, false);
6083         }
6084     },
6085
6086     success : function(response)
6087     {
6088         this.uploadComplete= true;
6089         if (this.haveProgress) {
6090             Roo.MessageBox.hide();
6091         }
6092         
6093         
6094         var result = this.processResponse(response);
6095         if(result === true || result.success){
6096             this.form.afterAction(this, true);
6097             return;
6098         }
6099         if(result.errors){
6100             this.form.markInvalid(result.errors);
6101             this.failureType = Roo.form.Action.SERVER_INVALID;
6102         }
6103         this.form.afterAction(this, false);
6104     },
6105     failure : function(response)
6106     {
6107         this.uploadComplete= true;
6108         if (this.haveProgress) {
6109             Roo.MessageBox.hide();
6110         }
6111         
6112         this.response = response;
6113         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6114         this.form.afterAction(this, false);
6115     },
6116     
6117     handleResponse : function(response){
6118         if(this.form.errorReader){
6119             var rs = this.form.errorReader.read(response);
6120             var errors = [];
6121             if(rs.records){
6122                 for(var i = 0, len = rs.records.length; i < len; i++) {
6123                     var r = rs.records[i];
6124                     errors[i] = r.data;
6125                 }
6126             }
6127             if(errors.length < 1){
6128                 errors = null;
6129             }
6130             return {
6131                 success : rs.success,
6132                 errors : errors
6133             };
6134         }
6135         var ret = false;
6136         try {
6137             ret = Roo.decode(response.responseText);
6138         } catch (e) {
6139             ret = {
6140                 success: false,
6141                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6142                 errors : []
6143             };
6144         }
6145         return ret;
6146         
6147     }
6148 });
6149
6150
6151 Roo.form.Action.Load = function(form, options){
6152     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6153     this.reader = this.form.reader;
6154 };
6155
6156 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6157     type : 'load',
6158
6159     run : function(){
6160         
6161         Roo.Ajax.request(Roo.apply(
6162                 this.createCallback(), {
6163                     method:this.getMethod(),
6164                     url:this.getUrl(false),
6165                     params:this.getParams()
6166         }));
6167     },
6168
6169     success : function(response){
6170         
6171         var result = this.processResponse(response);
6172         if(result === true || !result.success || !result.data){
6173             this.failureType = Roo.form.Action.LOAD_FAILURE;
6174             this.form.afterAction(this, false);
6175             return;
6176         }
6177         this.form.clearInvalid();
6178         this.form.setValues(result.data);
6179         this.form.afterAction(this, true);
6180     },
6181
6182     handleResponse : function(response){
6183         if(this.form.reader){
6184             var rs = this.form.reader.read(response);
6185             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6186             return {
6187                 success : rs.success,
6188                 data : data
6189             };
6190         }
6191         return Roo.decode(response.responseText);
6192     }
6193 });
6194
6195 Roo.form.Action.ACTION_TYPES = {
6196     'load' : Roo.form.Action.Load,
6197     'submit' : Roo.form.Action.Submit
6198 };/*
6199  * - LGPL
6200  *
6201  * form
6202  * 
6203  */
6204
6205 /**
6206  * @class Roo.bootstrap.Form
6207  * @extends Roo.bootstrap.Component
6208  * Bootstrap Form class
6209  * @cfg {String} method  GET | POST (default POST)
6210  * @cfg {String} labelAlign top | left (default top)
6211  * @cfg {String} align left  | right - for navbars
6212  * @cfg {Boolean} loadMask load mask when submit (default true)
6213
6214  * 
6215  * @constructor
6216  * Create a new Form
6217  * @param {Object} config The config object
6218  */
6219
6220
6221 Roo.bootstrap.Form = function(config){
6222     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6223     this.addEvents({
6224         /**
6225          * @event clientvalidation
6226          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6227          * @param {Form} this
6228          * @param {Boolean} valid true if the form has passed client-side validation
6229          */
6230         clientvalidation: true,
6231         /**
6232          * @event beforeaction
6233          * Fires before any action is performed. Return false to cancel the action.
6234          * @param {Form} this
6235          * @param {Action} action The action to be performed
6236          */
6237         beforeaction: true,
6238         /**
6239          * @event actionfailed
6240          * Fires when an action fails.
6241          * @param {Form} this
6242          * @param {Action} action The action that failed
6243          */
6244         actionfailed : true,
6245         /**
6246          * @event actioncomplete
6247          * Fires when an action is completed.
6248          * @param {Form} this
6249          * @param {Action} action The action that completed
6250          */
6251         actioncomplete : true
6252     });
6253     
6254 };
6255
6256 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6257       
6258      /**
6259      * @cfg {String} method
6260      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6261      */
6262     method : 'POST',
6263     /**
6264      * @cfg {String} url
6265      * The URL to use for form actions if one isn't supplied in the action options.
6266      */
6267     /**
6268      * @cfg {Boolean} fileUpload
6269      * Set to true if this form is a file upload.
6270      */
6271      
6272     /**
6273      * @cfg {Object} baseParams
6274      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6275      */
6276       
6277     /**
6278      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6279      */
6280     timeout: 30,
6281     /**
6282      * @cfg {Sting} align (left|right) for navbar forms
6283      */
6284     align : 'left',
6285
6286     // private
6287     activeAction : null,
6288  
6289     /**
6290      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6291      * element by passing it or its id or mask the form itself by passing in true.
6292      * @type Mixed
6293      */
6294     waitMsgTarget : false,
6295     
6296     loadMask : true,
6297     
6298     getAutoCreate : function(){
6299         
6300         var cfg = {
6301             tag: 'form',
6302             method : this.method || 'POST',
6303             id : this.id || Roo.id(),
6304             cls : ''
6305         }
6306         if (this.parent().xtype.match(/^Nav/)) {
6307             cfg.cls = 'navbar-form navbar-' + this.align;
6308             
6309         }
6310         
6311         if (this.labelAlign == 'left' ) {
6312             cfg.cls += ' form-horizontal';
6313         }
6314         
6315         
6316         return cfg;
6317     },
6318     initEvents : function()
6319     {
6320         this.el.on('submit', this.onSubmit, this);
6321         // this was added as random key presses on the form where triggering form submit.
6322         this.el.on('keypress', function(e) {
6323             if (e.getCharCode() != 13) {
6324                 return true;
6325             }
6326             // we might need to allow it for textareas.. and some other items.
6327             // check e.getTarget().
6328             
6329             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6330                 return true;
6331             }
6332         
6333             Roo.log("keypress blocked");
6334             
6335             e.preventDefault();
6336             return false;
6337         });
6338         
6339     },
6340     // private
6341     onSubmit : function(e){
6342         e.stopEvent();
6343     },
6344     
6345      /**
6346      * Returns true if client-side validation on the form is successful.
6347      * @return Boolean
6348      */
6349     isValid : function(){
6350         var items = this.getItems();
6351         var valid = true;
6352         items.each(function(f){
6353            if(!f.validate()){
6354                valid = false;
6355                
6356            }
6357         });
6358         return valid;
6359     },
6360     /**
6361      * Returns true if any fields in this form have changed since their original load.
6362      * @return Boolean
6363      */
6364     isDirty : function(){
6365         var dirty = false;
6366         var items = this.getItems();
6367         items.each(function(f){
6368            if(f.isDirty()){
6369                dirty = true;
6370                return false;
6371            }
6372            return true;
6373         });
6374         return dirty;
6375     },
6376      /**
6377      * Performs a predefined action (submit or load) or custom actions you define on this form.
6378      * @param {String} actionName The name of the action type
6379      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6380      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6381      * accept other config options):
6382      * <pre>
6383 Property          Type             Description
6384 ----------------  ---------------  ----------------------------------------------------------------------------------
6385 url               String           The url for the action (defaults to the form's url)
6386 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6387 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6388 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6389                                    validate the form on the client (defaults to false)
6390      * </pre>
6391      * @return {BasicForm} this
6392      */
6393     doAction : function(action, options){
6394         if(typeof action == 'string'){
6395             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6396         }
6397         if(this.fireEvent('beforeaction', this, action) !== false){
6398             this.beforeAction(action);
6399             action.run.defer(100, action);
6400         }
6401         return this;
6402     },
6403     
6404     // private
6405     beforeAction : function(action){
6406         var o = action.options;
6407         
6408         if(this.loadMask){
6409             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6410         }
6411         // not really supported yet.. ??
6412         
6413         //if(this.waitMsgTarget === true){
6414         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6415         //}else if(this.waitMsgTarget){
6416         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6417         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6418         //}else {
6419         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6420        // }
6421          
6422     },
6423
6424     // private
6425     afterAction : function(action, success){
6426         this.activeAction = null;
6427         var o = action.options;
6428         
6429         //if(this.waitMsgTarget === true){
6430             this.el.unmask();
6431         //}else if(this.waitMsgTarget){
6432         //    this.waitMsgTarget.unmask();
6433         //}else{
6434         //    Roo.MessageBox.updateProgress(1);
6435         //    Roo.MessageBox.hide();
6436        // }
6437         // 
6438         if(success){
6439             if(o.reset){
6440                 this.reset();
6441             }
6442             Roo.callback(o.success, o.scope, [this, action]);
6443             this.fireEvent('actioncomplete', this, action);
6444             
6445         }else{
6446             
6447             // failure condition..
6448             // we have a scenario where updates need confirming.
6449             // eg. if a locking scenario exists..
6450             // we look for { errors : { needs_confirm : true }} in the response.
6451             if (
6452                 (typeof(action.result) != 'undefined')  &&
6453                 (typeof(action.result.errors) != 'undefined')  &&
6454                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6455            ){
6456                 var _t = this;
6457                 Roo.log("not supported yet");
6458                  /*
6459                 
6460                 Roo.MessageBox.confirm(
6461                     "Change requires confirmation",
6462                     action.result.errorMsg,
6463                     function(r) {
6464                         if (r != 'yes') {
6465                             return;
6466                         }
6467                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6468                     }
6469                     
6470                 );
6471                 */
6472                 
6473                 
6474                 return;
6475             }
6476             
6477             Roo.callback(o.failure, o.scope, [this, action]);
6478             // show an error message if no failed handler is set..
6479             if (!this.hasListener('actionfailed')) {
6480                 Roo.log("need to add dialog support");
6481                 /*
6482                 Roo.MessageBox.alert("Error",
6483                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6484                         action.result.errorMsg :
6485                         "Saving Failed, please check your entries or try again"
6486                 );
6487                 */
6488             }
6489             
6490             this.fireEvent('actionfailed', this, action);
6491         }
6492         
6493     },
6494     /**
6495      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6496      * @param {String} id The value to search for
6497      * @return Field
6498      */
6499     findField : function(id){
6500         var items = this.getItems();
6501         var field = items.get(id);
6502         if(!field){
6503              items.each(function(f){
6504                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6505                     field = f;
6506                     return false;
6507                 }
6508                 return true;
6509             });
6510         }
6511         return field || null;
6512     },
6513      /**
6514      * Mark fields in this form invalid in bulk.
6515      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6516      * @return {BasicForm} this
6517      */
6518     markInvalid : function(errors){
6519         if(errors instanceof Array){
6520             for(var i = 0, len = errors.length; i < len; i++){
6521                 var fieldError = errors[i];
6522                 var f = this.findField(fieldError.id);
6523                 if(f){
6524                     f.markInvalid(fieldError.msg);
6525                 }
6526             }
6527         }else{
6528             var field, id;
6529             for(id in errors){
6530                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6531                     field.markInvalid(errors[id]);
6532                 }
6533             }
6534         }
6535         //Roo.each(this.childForms || [], function (f) {
6536         //    f.markInvalid(errors);
6537         //});
6538         
6539         return this;
6540     },
6541
6542     /**
6543      * Set values for fields in this form in bulk.
6544      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6545      * @return {BasicForm} this
6546      */
6547     setValues : function(values){
6548         if(values instanceof Array){ // array of objects
6549             for(var i = 0, len = values.length; i < len; i++){
6550                 var v = values[i];
6551                 var f = this.findField(v.id);
6552                 if(f){
6553                     f.setValue(v.value);
6554                     if(this.trackResetOnLoad){
6555                         f.originalValue = f.getValue();
6556                     }
6557                 }
6558             }
6559         }else{ // object hash
6560             var field, id;
6561             for(id in values){
6562                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6563                     
6564                     if (field.setFromData && 
6565                         field.valueField && 
6566                         field.displayField &&
6567                         // combos' with local stores can 
6568                         // be queried via setValue()
6569                         // to set their value..
6570                         (field.store && !field.store.isLocal)
6571                         ) {
6572                         // it's a combo
6573                         var sd = { };
6574                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6575                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6576                         field.setFromData(sd);
6577                         
6578                     } else {
6579                         field.setValue(values[id]);
6580                     }
6581                     
6582                     
6583                     if(this.trackResetOnLoad){
6584                         field.originalValue = field.getValue();
6585                     }
6586                 }
6587             }
6588         }
6589          
6590         //Roo.each(this.childForms || [], function (f) {
6591         //    f.setValues(values);
6592         //});
6593                 
6594         return this;
6595     },
6596
6597     /**
6598      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6599      * they are returned as an array.
6600      * @param {Boolean} asString
6601      * @return {Object}
6602      */
6603     getValues : function(asString){
6604         //if (this.childForms) {
6605             // copy values from the child forms
6606         //    Roo.each(this.childForms, function (f) {
6607         //        this.setValues(f.getValues());
6608         //    }, this);
6609         //}
6610         
6611         
6612         
6613         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6614         if(asString === true){
6615             return fs;
6616         }
6617         return Roo.urlDecode(fs);
6618     },
6619     
6620     /**
6621      * Returns the fields in this form as an object with key/value pairs. 
6622      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6623      * @return {Object}
6624      */
6625     getFieldValues : function(with_hidden)
6626     {
6627         var items = this.getItems();
6628         var ret = {};
6629         items.each(function(f){
6630             if (!f.getName()) {
6631                 return;
6632             }
6633             var v = f.getValue();
6634             if (f.inputType =='radio') {
6635                 if (typeof(ret[f.getName()]) == 'undefined') {
6636                     ret[f.getName()] = ''; // empty..
6637                 }
6638                 
6639                 if (!f.el.dom.checked) {
6640                     return;
6641                     
6642                 }
6643                 v = f.el.dom.value;
6644                 
6645             }
6646             
6647             // not sure if this supported any more..
6648             if ((typeof(v) == 'object') && f.getRawValue) {
6649                 v = f.getRawValue() ; // dates..
6650             }
6651             // combo boxes where name != hiddenName...
6652             if (f.name != f.getName()) {
6653                 ret[f.name] = f.getRawValue();
6654             }
6655             ret[f.getName()] = v;
6656         });
6657         
6658         return ret;
6659     },
6660
6661     /**
6662      * Clears all invalid messages in this form.
6663      * @return {BasicForm} this
6664      */
6665     clearInvalid : function(){
6666         var items = this.getItems();
6667         
6668         items.each(function(f){
6669            f.clearInvalid();
6670         });
6671         
6672         
6673         
6674         return this;
6675     },
6676
6677     /**
6678      * Resets this form.
6679      * @return {BasicForm} this
6680      */
6681     reset : function(){
6682         var items = this.getItems();
6683         items.each(function(f){
6684             f.reset();
6685         });
6686         
6687         Roo.each(this.childForms || [], function (f) {
6688             f.reset();
6689         });
6690        
6691         
6692         return this;
6693     },
6694     getItems : function()
6695     {
6696         var r=new Roo.util.MixedCollection(false, function(o){
6697             return o.id || (o.id = Roo.id());
6698         });
6699         var iter = function(el) {
6700             if (el.inputEl) {
6701                 r.add(el);
6702             }
6703             if (!el.items) {
6704                 return;
6705             }
6706             Roo.each(el.items,function(e) {
6707                 iter(e);
6708             });
6709             
6710             
6711         };
6712         iter(this);
6713         return r;
6714         
6715         
6716         
6717         
6718     }
6719     
6720 });
6721
6722  
6723 /*
6724  * Based on:
6725  * Ext JS Library 1.1.1
6726  * Copyright(c) 2006-2007, Ext JS, LLC.
6727  *
6728  * Originally Released Under LGPL - original licence link has changed is not relivant.
6729  *
6730  * Fork - LGPL
6731  * <script type="text/javascript">
6732  */
6733 /**
6734  * @class Roo.form.VTypes
6735  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6736  * @singleton
6737  */
6738 Roo.form.VTypes = function(){
6739     // closure these in so they are only created once.
6740     var alpha = /^[a-zA-Z_]+$/;
6741     var alphanum = /^[a-zA-Z0-9_]+$/;
6742     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6743     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6744
6745     // All these messages and functions are configurable
6746     return {
6747         /**
6748          * The function used to validate email addresses
6749          * @param {String} value The email address
6750          */
6751         'email' : function(v){
6752             return email.test(v);
6753         },
6754         /**
6755          * The error text to display when the email validation function returns false
6756          * @type String
6757          */
6758         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6759         /**
6760          * The keystroke filter mask to be applied on email input
6761          * @type RegExp
6762          */
6763         'emailMask' : /[a-z0-9_\.\-@]/i,
6764
6765         /**
6766          * The function used to validate URLs
6767          * @param {String} value The URL
6768          */
6769         'url' : function(v){
6770             return url.test(v);
6771         },
6772         /**
6773          * The error text to display when the url validation function returns false
6774          * @type String
6775          */
6776         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6777         
6778         /**
6779          * The function used to validate alpha values
6780          * @param {String} value The value
6781          */
6782         'alpha' : function(v){
6783             return alpha.test(v);
6784         },
6785         /**
6786          * The error text to display when the alpha validation function returns false
6787          * @type String
6788          */
6789         'alphaText' : 'This field should only contain letters and _',
6790         /**
6791          * The keystroke filter mask to be applied on alpha input
6792          * @type RegExp
6793          */
6794         'alphaMask' : /[a-z_]/i,
6795
6796         /**
6797          * The function used to validate alphanumeric values
6798          * @param {String} value The value
6799          */
6800         'alphanum' : function(v){
6801             return alphanum.test(v);
6802         },
6803         /**
6804          * The error text to display when the alphanumeric validation function returns false
6805          * @type String
6806          */
6807         'alphanumText' : 'This field should only contain letters, numbers and _',
6808         /**
6809          * The keystroke filter mask to be applied on alphanumeric input
6810          * @type RegExp
6811          */
6812         'alphanumMask' : /[a-z0-9_]/i
6813     };
6814 }();/*
6815  * - LGPL
6816  *
6817  * Input
6818  * 
6819  */
6820
6821 /**
6822  * @class Roo.bootstrap.Input
6823  * @extends Roo.bootstrap.Component
6824  * Bootstrap Input class
6825  * @cfg {Boolean} disabled is it disabled
6826  * @cfg {String} fieldLabel - the label associated
6827  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6828  * @cfg {String} name name of the input
6829  * @cfg {string} fieldLabel - the label associated
6830  * @cfg {string}  inputType - input / file submit ...
6831  * @cfg {string} placeholder - placeholder to put in text.
6832  * @cfg {string}  before - input group add on before
6833  * @cfg {string} after - input group add on after
6834  * @cfg {string} size - (lg|sm) or leave empty..
6835  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6836  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6837  * @cfg {Number} md colspan out of 12 for computer-sized screens
6838  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6839  * @cfg {string} value default value of the input
6840  * @cfg {Number} labelWidth set the width of label (0-12)
6841  * @cfg {String} labelAlign (top|left)
6842  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6843  * @cfg {String} align (left|center|right) Default left
6844  * 
6845  * 
6846  * @constructor
6847  * Create a new Input
6848  * @param {Object} config The config object
6849  */
6850
6851 Roo.bootstrap.Input = function(config){
6852     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6853    
6854         this.addEvents({
6855             /**
6856              * @event focus
6857              * Fires when this field receives input focus.
6858              * @param {Roo.form.Field} this
6859              */
6860             focus : true,
6861             /**
6862              * @event blur
6863              * Fires when this field loses input focus.
6864              * @param {Roo.form.Field} this
6865              */
6866             blur : true,
6867             /**
6868              * @event specialkey
6869              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6870              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6871              * @param {Roo.form.Field} this
6872              * @param {Roo.EventObject} e The event object
6873              */
6874             specialkey : true,
6875             /**
6876              * @event change
6877              * Fires just before the field blurs if the field value has changed.
6878              * @param {Roo.form.Field} this
6879              * @param {Mixed} newValue The new value
6880              * @param {Mixed} oldValue The original value
6881              */
6882             change : true,
6883             /**
6884              * @event invalid
6885              * Fires after the field has been marked as invalid.
6886              * @param {Roo.form.Field} this
6887              * @param {String} msg The validation message
6888              */
6889             invalid : true,
6890             /**
6891              * @event valid
6892              * Fires after the field has been validated with no errors.
6893              * @param {Roo.form.Field} this
6894              */
6895             valid : true,
6896              /**
6897              * @event keyup
6898              * Fires after the key up
6899              * @param {Roo.form.Field} this
6900              * @param {Roo.EventObject}  e The event Object
6901              */
6902             keyup : true
6903         });
6904 };
6905
6906 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6907      /**
6908      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6909       automatic validation (defaults to "keyup").
6910      */
6911     validationEvent : "keyup",
6912      /**
6913      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6914      */
6915     validateOnBlur : true,
6916     /**
6917      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6918      */
6919     validationDelay : 250,
6920      /**
6921      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6922      */
6923     focusClass : "x-form-focus",  // not needed???
6924     
6925        
6926     /**
6927      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6928      */
6929     invalidClass : "has-error",
6930     
6931     /**
6932      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6933      */
6934     selectOnFocus : false,
6935     
6936      /**
6937      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6938      */
6939     maskRe : null,
6940        /**
6941      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6942      */
6943     vtype : null,
6944     
6945       /**
6946      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6947      */
6948     disableKeyFilter : false,
6949     
6950        /**
6951      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6952      */
6953     disabled : false,
6954      /**
6955      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6956      */
6957     allowBlank : true,
6958     /**
6959      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6960      */
6961     blankText : "This field is required",
6962     
6963      /**
6964      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6965      */
6966     minLength : 0,
6967     /**
6968      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6969      */
6970     maxLength : Number.MAX_VALUE,
6971     /**
6972      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6973      */
6974     minLengthText : "The minimum length for this field is {0}",
6975     /**
6976      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6977      */
6978     maxLengthText : "The maximum length for this field is {0}",
6979   
6980     
6981     /**
6982      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6983      * If available, this function will be called only after the basic validators all return true, and will be passed the
6984      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6985      */
6986     validator : null,
6987     /**
6988      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6989      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6990      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6991      */
6992     regex : null,
6993     /**
6994      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6995      */
6996     regexText : "",
6997     
6998     
6999     
7000     fieldLabel : '',
7001     inputType : 'text',
7002     
7003     name : false,
7004     placeholder: false,
7005     before : false,
7006     after : false,
7007     size : false,
7008     // private
7009     hasFocus : false,
7010     preventMark: false,
7011     isFormField : true,
7012     value : '',
7013     labelWidth : 2,
7014     labelAlign : false,
7015     readOnly : false,
7016     align : false,
7017     formatedValue : false,
7018     
7019     parentLabelAlign : function()
7020     {
7021         var parent = this;
7022         while (parent.parent()) {
7023             parent = parent.parent();
7024             if (typeof(parent.labelAlign) !='undefined') {
7025                 return parent.labelAlign;
7026             }
7027         }
7028         return 'left';
7029         
7030     },
7031     
7032     getAutoCreate : function(){
7033         
7034         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7035         
7036         var id = Roo.id();
7037         
7038         var cfg = {};
7039         
7040         if(this.inputType != 'hidden'){
7041             cfg.cls = 'form-group' //input-group
7042         }
7043         
7044         var input =  {
7045             tag: 'input',
7046             id : id,
7047             type : this.inputType,
7048             value : this.value,
7049             cls : 'form-control',
7050             placeholder : this.placeholder || ''
7051             
7052         };
7053         
7054         if(this.align){
7055             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7056         }
7057         
7058         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7059             input.maxLength = this.maxLength;
7060         }
7061         
7062         if (this.disabled) {
7063             input.disabled=true;
7064         }
7065         
7066         if (this.readOnly) {
7067             input.readonly=true;
7068         }
7069         
7070         if (this.name) {
7071             input.name = this.name;
7072         }
7073         if (this.size) {
7074             input.cls += ' input-' + this.size;
7075         }
7076         var settings=this;
7077         ['xs','sm','md','lg'].map(function(size){
7078             if (settings[size]) {
7079                 cfg.cls += ' col-' + size + '-' + settings[size];
7080             }
7081         });
7082         
7083         var inputblock = input;
7084         
7085         if (this.before || this.after) {
7086             
7087             inputblock = {
7088                 cls : 'input-group',
7089                 cn :  [] 
7090             };
7091             if (this.before && typeof(this.before) == 'string') {
7092                 
7093                 inputblock.cn.push({
7094                     tag :'span',
7095                     cls : 'roo-input-before input-group-addon',
7096                     html : this.before
7097                 });
7098             }
7099             if (this.before && typeof(this.before) == 'object') {
7100                 this.before = Roo.factory(this.before);
7101                 Roo.log(this.before);
7102                 inputblock.cn.push({
7103                     tag :'span',
7104                     cls : 'roo-input-before input-group-' +
7105                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7106                 });
7107             }
7108             
7109             inputblock.cn.push(input);
7110             
7111             if (this.after && typeof(this.after) == 'string') {
7112                 inputblock.cn.push({
7113                     tag :'span',
7114                     cls : 'roo-input-after input-group-addon',
7115                     html : this.after
7116                 });
7117             }
7118             if (this.after && typeof(this.after) == 'object') {
7119                 this.after = Roo.factory(this.after);
7120                 Roo.log(this.after);
7121                 inputblock.cn.push({
7122                     tag :'span',
7123                     cls : 'roo-input-after input-group-' +
7124                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7125                 });
7126             }
7127         };
7128         
7129         if (align ==='left' && this.fieldLabel.length) {
7130                 Roo.log("left and has label");
7131                 cfg.cn = [
7132                     
7133                     {
7134                         tag: 'label',
7135                         'for' :  id,
7136                         cls : 'control-label col-sm-' + this.labelWidth,
7137                         html : this.fieldLabel
7138                         
7139                     },
7140                     {
7141                         cls : "col-sm-" + (12 - this.labelWidth), 
7142                         cn: [
7143                             inputblock
7144                         ]
7145                     }
7146                     
7147                 ];
7148         } else if ( this.fieldLabel.length) {
7149                 Roo.log(" label");
7150                  cfg.cn = [
7151                    
7152                     {
7153                         tag: 'label',
7154                         //cls : 'input-group-addon',
7155                         html : this.fieldLabel
7156                         
7157                     },
7158                     
7159                     inputblock
7160                     
7161                 ];
7162
7163         } else {
7164             
7165                 Roo.log(" no label && no align");
7166                 cfg.cn = [
7167                     
7168                         inputblock
7169                     
7170                 ];
7171                 
7172                 
7173         };
7174         Roo.log('input-parentType: ' + this.parentType);
7175         
7176         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7177            cfg.cls += ' navbar-form';
7178            Roo.log(cfg);
7179         }
7180         
7181         return cfg;
7182         
7183     },
7184     /**
7185      * return the real input element.
7186      */
7187     inputEl: function ()
7188     {
7189         return this.el.select('input.form-control',true).first();
7190     },
7191     setDisabled : function(v)
7192     {
7193         var i  = this.inputEl().dom;
7194         if (!v) {
7195             i.removeAttribute('disabled');
7196             return;
7197             
7198         }
7199         i.setAttribute('disabled','true');
7200     },
7201     initEvents : function()
7202     {
7203         
7204         this.inputEl().on("keydown" , this.fireKey,  this);
7205         this.inputEl().on("focus", this.onFocus,  this);
7206         this.inputEl().on("blur", this.onBlur,  this);
7207         
7208         this.inputEl().relayEvent('keyup', this);
7209
7210         // reference to original value for reset
7211         this.originalValue = this.getValue();
7212         //Roo.form.TextField.superclass.initEvents.call(this);
7213         if(this.validationEvent == 'keyup'){
7214             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7215             this.inputEl().on('keyup', this.filterValidation, this);
7216         }
7217         else if(this.validationEvent !== false){
7218             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7219         }
7220         
7221         if(this.selectOnFocus){
7222             this.on("focus", this.preFocus, this);
7223             
7224         }
7225         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7226             this.inputEl().on("keypress", this.filterKeys, this);
7227         }
7228        /* if(this.grow){
7229             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7230             this.el.on("click", this.autoSize,  this);
7231         }
7232         */
7233         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7234             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7235         }
7236         
7237         if (typeof(this.before) == 'object') {
7238             this.before.render(this.el.select('.roo-input-before',true).first());
7239         }
7240         if (typeof(this.after) == 'object') {
7241             this.after.render(this.el.select('.roo-input-after',true).first());
7242         }
7243         
7244         
7245     },
7246     filterValidation : function(e){
7247         if(!e.isNavKeyPress()){
7248             this.validationTask.delay(this.validationDelay);
7249         }
7250     },
7251      /**
7252      * Validates the field value
7253      * @return {Boolean} True if the value is valid, else false
7254      */
7255     validate : function(){
7256         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7257         if(this.disabled || this.validateValue(this.getRawValue())){
7258             this.clearInvalid();
7259             return true;
7260         }
7261         return false;
7262     },
7263     
7264     
7265     /**
7266      * Validates a value according to the field's validation rules and marks the field as invalid
7267      * if the validation fails
7268      * @param {Mixed} value The value to validate
7269      * @return {Boolean} True if the value is valid, else false
7270      */
7271     validateValue : function(value){
7272         if(value.length < 1)  { // if it's blank
7273              if(this.allowBlank){
7274                 this.clearInvalid();
7275                 return true;
7276              }else{
7277                 this.markInvalid(this.blankText);
7278                 return false;
7279              }
7280         }
7281         if(value.length < this.minLength){
7282             this.markInvalid(String.format(this.minLengthText, this.minLength));
7283             return false;
7284         }
7285         if(value.length > this.maxLength){
7286             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7287             return false;
7288         }
7289         if(this.vtype){
7290             var vt = Roo.form.VTypes;
7291             if(!vt[this.vtype](value, this)){
7292                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7293                 return false;
7294             }
7295         }
7296         if(typeof this.validator == "function"){
7297             var msg = this.validator(value);
7298             if(msg !== true){
7299                 this.markInvalid(msg);
7300                 return false;
7301             }
7302         }
7303         if(this.regex && !this.regex.test(value)){
7304             this.markInvalid(this.regexText);
7305             return false;
7306         }
7307         return true;
7308     },
7309
7310     
7311     
7312      // private
7313     fireKey : function(e){
7314         //Roo.log('field ' + e.getKey());
7315         if(e.isNavKeyPress()){
7316             this.fireEvent("specialkey", this, e);
7317         }
7318     },
7319     focus : function (selectText){
7320         if(this.rendered){
7321             this.inputEl().focus();
7322             if(selectText === true){
7323                 this.inputEl().dom.select();
7324             }
7325         }
7326         return this;
7327     } ,
7328     
7329     onFocus : function(){
7330         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7331            // this.el.addClass(this.focusClass);
7332         }
7333         if(!this.hasFocus){
7334             this.hasFocus = true;
7335             this.startValue = this.getValue();
7336             this.fireEvent("focus", this);
7337         }
7338     },
7339     
7340     beforeBlur : Roo.emptyFn,
7341
7342     
7343     // private
7344     onBlur : function(){
7345         this.beforeBlur();
7346         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7347             //this.el.removeClass(this.focusClass);
7348         }
7349         this.hasFocus = false;
7350         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7351             this.validate();
7352         }
7353         var v = this.getValue();
7354         if(String(v) !== String(this.startValue)){
7355             this.fireEvent('change', this, v, this.startValue);
7356         }
7357         this.fireEvent("blur", this);
7358     },
7359     
7360     /**
7361      * Resets the current field value to the originally loaded value and clears any validation messages
7362      */
7363     reset : function(){
7364         this.setValue(this.originalValue);
7365         this.clearInvalid();
7366     },
7367      /**
7368      * Returns the name of the field
7369      * @return {Mixed} name The name field
7370      */
7371     getName: function(){
7372         return this.name;
7373     },
7374      /**
7375      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7376      * @return {Mixed} value The field value
7377      */
7378     getValue : function(){
7379         
7380         var v = this.inputEl().getValue();
7381         
7382         return v;
7383     },
7384     /**
7385      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7386      * @return {Mixed} value The field value
7387      */
7388     getRawValue : function(){
7389         var v = this.inputEl().getValue();
7390         
7391         return v;
7392     },
7393     
7394     /**
7395      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7396      * @param {Mixed} value The value to set
7397      */
7398     setRawValue : function(v){
7399         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7400     },
7401     
7402     selectText : function(start, end){
7403         var v = this.getRawValue();
7404         if(v.length > 0){
7405             start = start === undefined ? 0 : start;
7406             end = end === undefined ? v.length : end;
7407             var d = this.inputEl().dom;
7408             if(d.setSelectionRange){
7409                 d.setSelectionRange(start, end);
7410             }else if(d.createTextRange){
7411                 var range = d.createTextRange();
7412                 range.moveStart("character", start);
7413                 range.moveEnd("character", v.length-end);
7414                 range.select();
7415             }
7416         }
7417     },
7418     
7419     /**
7420      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7421      * @param {Mixed} value The value to set
7422      */
7423     setValue : function(v){
7424         this.value = v;
7425         if(this.rendered){
7426             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7427             this.validate();
7428         }
7429     },
7430     
7431     /*
7432     processValue : function(value){
7433         if(this.stripCharsRe){
7434             var newValue = value.replace(this.stripCharsRe, '');
7435             if(newValue !== value){
7436                 this.setRawValue(newValue);
7437                 return newValue;
7438             }
7439         }
7440         return value;
7441     },
7442   */
7443     preFocus : function(){
7444         
7445         if(this.selectOnFocus){
7446             this.inputEl().dom.select();
7447         }
7448     },
7449     filterKeys : function(e){
7450         var k = e.getKey();
7451         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7452             return;
7453         }
7454         var c = e.getCharCode(), cc = String.fromCharCode(c);
7455         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7456             return;
7457         }
7458         if(!this.maskRe.test(cc)){
7459             e.stopEvent();
7460         }
7461     },
7462      /**
7463      * Clear any invalid styles/messages for this field
7464      */
7465     clearInvalid : function(){
7466         
7467         if(!this.el || this.preventMark){ // not rendered
7468             return;
7469         }
7470         this.el.removeClass(this.invalidClass);
7471         /*
7472         switch(this.msgTarget){
7473             case 'qtip':
7474                 this.el.dom.qtip = '';
7475                 break;
7476             case 'title':
7477                 this.el.dom.title = '';
7478                 break;
7479             case 'under':
7480                 if(this.errorEl){
7481                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7482                 }
7483                 break;
7484             case 'side':
7485                 if(this.errorIcon){
7486                     this.errorIcon.dom.qtip = '';
7487                     this.errorIcon.hide();
7488                     this.un('resize', this.alignErrorIcon, this);
7489                 }
7490                 break;
7491             default:
7492                 var t = Roo.getDom(this.msgTarget);
7493                 t.innerHTML = '';
7494                 t.style.display = 'none';
7495                 break;
7496         }
7497         */
7498         this.fireEvent('valid', this);
7499     },
7500      /**
7501      * Mark this field as invalid
7502      * @param {String} msg The validation message
7503      */
7504     markInvalid : function(msg){
7505         if(!this.el  || this.preventMark){ // not rendered
7506             return;
7507         }
7508         this.el.addClass(this.invalidClass);
7509         /*
7510         msg = msg || this.invalidText;
7511         switch(this.msgTarget){
7512             case 'qtip':
7513                 this.el.dom.qtip = msg;
7514                 this.el.dom.qclass = 'x-form-invalid-tip';
7515                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7516                     Roo.QuickTips.enable();
7517                 }
7518                 break;
7519             case 'title':
7520                 this.el.dom.title = msg;
7521                 break;
7522             case 'under':
7523                 if(!this.errorEl){
7524                     var elp = this.el.findParent('.x-form-element', 5, true);
7525                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7526                     this.errorEl.setWidth(elp.getWidth(true)-20);
7527                 }
7528                 this.errorEl.update(msg);
7529                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7530                 break;
7531             case 'side':
7532                 if(!this.errorIcon){
7533                     var elp = this.el.findParent('.x-form-element', 5, true);
7534                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7535                 }
7536                 this.alignErrorIcon();
7537                 this.errorIcon.dom.qtip = msg;
7538                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7539                 this.errorIcon.show();
7540                 this.on('resize', this.alignErrorIcon, this);
7541                 break;
7542             default:
7543                 var t = Roo.getDom(this.msgTarget);
7544                 t.innerHTML = msg;
7545                 t.style.display = this.msgDisplay;
7546                 break;
7547         }
7548         */
7549         this.fireEvent('invalid', this, msg);
7550     },
7551     // private
7552     SafariOnKeyDown : function(event)
7553     {
7554         // this is a workaround for a password hang bug on chrome/ webkit.
7555         
7556         var isSelectAll = false;
7557         
7558         if(this.inputEl().dom.selectionEnd > 0){
7559             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7560         }
7561         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7562             event.preventDefault();
7563             this.setValue('');
7564             return;
7565         }
7566         
7567         if(isSelectAll){ // backspace and delete key
7568             
7569             event.preventDefault();
7570             // this is very hacky as keydown always get's upper case.
7571             //
7572             var cc = String.fromCharCode(event.getCharCode());
7573             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7574             
7575         }
7576     },
7577     adjustWidth : function(tag, w){
7578         tag = tag.toLowerCase();
7579         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7580             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7581                 if(tag == 'input'){
7582                     return w + 2;
7583                 }
7584                 if(tag == 'textarea'){
7585                     return w-2;
7586                 }
7587             }else if(Roo.isOpera){
7588                 if(tag == 'input'){
7589                     return w + 2;
7590                 }
7591                 if(tag == 'textarea'){
7592                     return w-2;
7593                 }
7594             }
7595         }
7596         return w;
7597     }
7598     
7599 });
7600
7601  
7602 /*
7603  * - LGPL
7604  *
7605  * Input
7606  * 
7607  */
7608
7609 /**
7610  * @class Roo.bootstrap.TextArea
7611  * @extends Roo.bootstrap.Input
7612  * Bootstrap TextArea class
7613  * @cfg {Number} cols Specifies the visible width of a text area
7614  * @cfg {Number} rows Specifies the visible number of lines in a text area
7615  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7616  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7617  * @cfg {string} html text
7618  * 
7619  * @constructor
7620  * Create a new TextArea
7621  * @param {Object} config The config object
7622  */
7623
7624 Roo.bootstrap.TextArea = function(config){
7625     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7626    
7627 };
7628
7629 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7630      
7631     cols : false,
7632     rows : 5,
7633     readOnly : false,
7634     warp : 'soft',
7635     resize : false,
7636     value: false,
7637     html: false,
7638     
7639     getAutoCreate : function(){
7640         
7641         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7642         
7643         var id = Roo.id();
7644         
7645         var cfg = {};
7646         
7647         var input =  {
7648             tag: 'textarea',
7649             id : id,
7650             warp : this.warp,
7651             rows : this.rows,
7652             value : this.value || '',
7653             html: this.html || '',
7654             cls : 'form-control',
7655             placeholder : this.placeholder || '' 
7656             
7657         };
7658         
7659         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7660             input.maxLength = this.maxLength;
7661         }
7662         
7663         if(this.resize){
7664             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7665         }
7666         
7667         if(this.cols){
7668             input.cols = this.cols;
7669         }
7670         
7671         if (this.readOnly) {
7672             input.readonly = true;
7673         }
7674         
7675         if (this.name) {
7676             input.name = this.name;
7677         }
7678         
7679         if (this.size) {
7680             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7681         }
7682         
7683         var settings=this;
7684         ['xs','sm','md','lg'].map(function(size){
7685             if (settings[size]) {
7686                 cfg.cls += ' col-' + size + '-' + settings[size];
7687             }
7688         });
7689         
7690         var inputblock = input;
7691         
7692         if (this.before || this.after) {
7693             
7694             inputblock = {
7695                 cls : 'input-group',
7696                 cn :  [] 
7697             };
7698             if (this.before) {
7699                 inputblock.cn.push({
7700                     tag :'span',
7701                     cls : 'input-group-addon',
7702                     html : this.before
7703                 });
7704             }
7705             inputblock.cn.push(input);
7706             if (this.after) {
7707                 inputblock.cn.push({
7708                     tag :'span',
7709                     cls : 'input-group-addon',
7710                     html : this.after
7711                 });
7712             }
7713             
7714         }
7715         
7716         if (align ==='left' && this.fieldLabel.length) {
7717                 Roo.log("left and has label");
7718                 cfg.cn = [
7719                     
7720                     {
7721                         tag: 'label',
7722                         'for' :  id,
7723                         cls : 'control-label col-sm-' + this.labelWidth,
7724                         html : this.fieldLabel
7725                         
7726                     },
7727                     {
7728                         cls : "col-sm-" + (12 - this.labelWidth), 
7729                         cn: [
7730                             inputblock
7731                         ]
7732                     }
7733                     
7734                 ];
7735         } else if ( this.fieldLabel.length) {
7736                 Roo.log(" label");
7737                  cfg.cn = [
7738                    
7739                     {
7740                         tag: 'label',
7741                         //cls : 'input-group-addon',
7742                         html : this.fieldLabel
7743                         
7744                     },
7745                     
7746                     inputblock
7747                     
7748                 ];
7749
7750         } else {
7751             
7752                    Roo.log(" no label && no align");
7753                 cfg.cn = [
7754                     
7755                         inputblock
7756                     
7757                 ];
7758                 
7759                 
7760         }
7761         
7762         if (this.disabled) {
7763             input.disabled=true;
7764         }
7765         
7766         return cfg;
7767         
7768     },
7769     /**
7770      * return the real textarea element.
7771      */
7772     inputEl: function ()
7773     {
7774         return this.el.select('textarea.form-control',true).first();
7775     }
7776 });
7777
7778  
7779 /*
7780  * - LGPL
7781  *
7782  * trigger field - base class for combo..
7783  * 
7784  */
7785  
7786 /**
7787  * @class Roo.bootstrap.TriggerField
7788  * @extends Roo.bootstrap.Input
7789  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7790  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7791  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7792  * for which you can provide a custom implementation.  For example:
7793  * <pre><code>
7794 var trigger = new Roo.bootstrap.TriggerField();
7795 trigger.onTriggerClick = myTriggerFn;
7796 trigger.applyTo('my-field');
7797 </code></pre>
7798  *
7799  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7800  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7801  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7802  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7803  * @constructor
7804  * Create a new TriggerField.
7805  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7806  * to the base TextField)
7807  */
7808 Roo.bootstrap.TriggerField = function(config){
7809     this.mimicing = false;
7810     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7811 };
7812
7813 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7814     /**
7815      * @cfg {String} triggerClass A CSS class to apply to the trigger
7816      */
7817      /**
7818      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7819      */
7820     hideTrigger:false,
7821
7822     /** @cfg {Boolean} grow @hide */
7823     /** @cfg {Number} growMin @hide */
7824     /** @cfg {Number} growMax @hide */
7825
7826     /**
7827      * @hide 
7828      * @method
7829      */
7830     autoSize: Roo.emptyFn,
7831     // private
7832     monitorTab : true,
7833     // private
7834     deferHeight : true,
7835
7836     
7837     actionMode : 'wrap',
7838     
7839     
7840     
7841     getAutoCreate : function(){
7842        
7843         var align = this.labelAlign || this.parentLabelAlign();
7844         
7845         var id = Roo.id();
7846         
7847         var cfg = {
7848             cls: 'form-group' //input-group
7849         };
7850         
7851         
7852         var input =  {
7853             tag: 'input',
7854             id : id,
7855             type : this.inputType,
7856             cls : 'form-control',
7857             autocomplete: 'off',
7858             placeholder : this.placeholder || '' 
7859             
7860         };
7861         if (this.name) {
7862             input.name = this.name;
7863         }
7864         if (this.size) {
7865             input.cls += ' input-' + this.size;
7866         }
7867         
7868         if (this.disabled) {
7869             input.disabled=true;
7870         }
7871         
7872         var inputblock = input;
7873         
7874         if (this.before || this.after) {
7875             
7876             inputblock = {
7877                 cls : 'input-group',
7878                 cn :  [] 
7879             };
7880             if (this.before) {
7881                 inputblock.cn.push({
7882                     tag :'span',
7883                     cls : 'input-group-addon',
7884                     html : this.before
7885                 });
7886             }
7887             inputblock.cn.push(input);
7888             if (this.after) {
7889                 inputblock.cn.push({
7890                     tag :'span',
7891                     cls : 'input-group-addon',
7892                     html : this.after
7893                 });
7894             }
7895             
7896         };
7897         
7898         var box = {
7899             tag: 'div',
7900             cn: [
7901                 {
7902                     tag: 'input',
7903                     type : 'hidden',
7904                     cls: 'form-hidden-field'
7905                 },
7906                 inputblock
7907             ]
7908             
7909         };
7910         
7911         if(this.multiple){
7912             Roo.log('multiple');
7913             
7914             box = {
7915                 tag: 'div',
7916                 cn: [
7917                     {
7918                         tag: 'input',
7919                         type : 'hidden',
7920                         cls: 'form-hidden-field'
7921                     },
7922                     {
7923                         tag: 'ul',
7924                         cls: 'select2-choices',
7925                         cn:[
7926                             {
7927                                 tag: 'li',
7928                                 cls: 'select2-search-field',
7929                                 cn: [
7930
7931                                     inputblock
7932                                 ]
7933                             }
7934                         ]
7935                     }
7936                 ]
7937             }
7938         };
7939         
7940         var combobox = {
7941             cls: 'select2-container input-group',
7942             cn: [
7943                 box
7944 //                {
7945 //                    tag: 'ul',
7946 //                    cls: 'typeahead typeahead-long dropdown-menu',
7947 //                    style: 'display:none'
7948 //                }
7949             ]
7950         };
7951         
7952         if(!this.multiple && this.showToggleBtn){
7953             combobox.cn.push({
7954                 tag :'span',
7955                 cls : 'input-group-addon btn dropdown-toggle',
7956                 cn : [
7957                     {
7958                         tag: 'span',
7959                         cls: 'caret'
7960                     },
7961                     {
7962                         tag: 'span',
7963                         cls: 'combobox-clear',
7964                         cn  : [
7965                             {
7966                                 tag : 'i',
7967                                 cls: 'icon-remove'
7968                             }
7969                         ]
7970                     }
7971                 ]
7972
7973             })
7974         }
7975         
7976         if(this.multiple){
7977             combobox.cls += ' select2-container-multi';
7978         }
7979         
7980         if (align ==='left' && this.fieldLabel.length) {
7981             
7982                 Roo.log("left and has label");
7983                 cfg.cn = [
7984                     
7985                     {
7986                         tag: 'label',
7987                         'for' :  id,
7988                         cls : 'control-label col-sm-' + this.labelWidth,
7989                         html : this.fieldLabel
7990                         
7991                     },
7992                     {
7993                         cls : "col-sm-" + (12 - this.labelWidth), 
7994                         cn: [
7995                             combobox
7996                         ]
7997                     }
7998                     
7999                 ];
8000         } else if ( this.fieldLabel.length) {
8001                 Roo.log(" label");
8002                  cfg.cn = [
8003                    
8004                     {
8005                         tag: 'label',
8006                         //cls : 'input-group-addon',
8007                         html : this.fieldLabel
8008                         
8009                     },
8010                     
8011                     combobox
8012                     
8013                 ];
8014
8015         } else {
8016             
8017                 Roo.log(" no label && no align");
8018                 cfg = combobox
8019                      
8020                 
8021         }
8022          
8023         var settings=this;
8024         ['xs','sm','md','lg'].map(function(size){
8025             if (settings[size]) {
8026                 cfg.cls += ' col-' + size + '-' + settings[size];
8027             }
8028         });
8029         
8030         return cfg;
8031         
8032     },
8033     
8034     
8035     
8036     // private
8037     onResize : function(w, h){
8038 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8039 //        if(typeof w == 'number'){
8040 //            var x = w - this.trigger.getWidth();
8041 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8042 //            this.trigger.setStyle('left', x+'px');
8043 //        }
8044     },
8045
8046     // private
8047     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8048
8049     // private
8050     getResizeEl : function(){
8051         return this.inputEl();
8052     },
8053
8054     // private
8055     getPositionEl : function(){
8056         return this.inputEl();
8057     },
8058
8059     // private
8060     alignErrorIcon : function(){
8061         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8062     },
8063
8064     // private
8065     initEvents : function(){
8066         
8067         this.createList();
8068         
8069         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8070         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8071         if(!this.multiple && this.showToggleBtn){
8072             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8073             if(this.hideTrigger){
8074                 this.trigger.setDisplayed(false);
8075             }
8076             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8077         }
8078         
8079         if(this.multiple){
8080             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8081         }
8082         
8083         //this.trigger.addClassOnOver('x-form-trigger-over');
8084         //this.trigger.addClassOnClick('x-form-trigger-click');
8085         
8086         //if(!this.width){
8087         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8088         //}
8089     },
8090     
8091     createList : function()
8092     {
8093         this.list = Roo.get(document.body).createChild({
8094             tag: 'ul',
8095             cls: 'typeahead typeahead-long dropdown-menu',
8096             style: 'display:none'
8097         });
8098         
8099         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8100         
8101     },
8102
8103     // private
8104     initTrigger : function(){
8105        
8106     },
8107
8108     // private
8109     onDestroy : function(){
8110         if(this.trigger){
8111             this.trigger.removeAllListeners();
8112           //  this.trigger.remove();
8113         }
8114         //if(this.wrap){
8115         //    this.wrap.remove();
8116         //}
8117         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8118     },
8119
8120     // private
8121     onFocus : function(){
8122         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8123         /*
8124         if(!this.mimicing){
8125             this.wrap.addClass('x-trigger-wrap-focus');
8126             this.mimicing = true;
8127             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8128             if(this.monitorTab){
8129                 this.el.on("keydown", this.checkTab, this);
8130             }
8131         }
8132         */
8133     },
8134
8135     // private
8136     checkTab : function(e){
8137         if(e.getKey() == e.TAB){
8138             this.triggerBlur();
8139         }
8140     },
8141
8142     // private
8143     onBlur : function(){
8144         // do nothing
8145     },
8146
8147     // private
8148     mimicBlur : function(e, t){
8149         /*
8150         if(!this.wrap.contains(t) && this.validateBlur()){
8151             this.triggerBlur();
8152         }
8153         */
8154     },
8155
8156     // private
8157     triggerBlur : function(){
8158         this.mimicing = false;
8159         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8160         if(this.monitorTab){
8161             this.el.un("keydown", this.checkTab, this);
8162         }
8163         //this.wrap.removeClass('x-trigger-wrap-focus');
8164         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8165     },
8166
8167     // private
8168     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8169     validateBlur : function(e, t){
8170         return true;
8171     },
8172
8173     // private
8174     onDisable : function(){
8175         this.inputEl().dom.disabled = true;
8176         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8177         //if(this.wrap){
8178         //    this.wrap.addClass('x-item-disabled');
8179         //}
8180     },
8181
8182     // private
8183     onEnable : function(){
8184         this.inputEl().dom.disabled = false;
8185         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8186         //if(this.wrap){
8187         //    this.el.removeClass('x-item-disabled');
8188         //}
8189     },
8190
8191     // private
8192     onShow : function(){
8193         var ae = this.getActionEl();
8194         
8195         if(ae){
8196             ae.dom.style.display = '';
8197             ae.dom.style.visibility = 'visible';
8198         }
8199     },
8200
8201     // private
8202     
8203     onHide : function(){
8204         var ae = this.getActionEl();
8205         ae.dom.style.display = 'none';
8206     },
8207
8208     /**
8209      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8210      * by an implementing function.
8211      * @method
8212      * @param {EventObject} e
8213      */
8214     onTriggerClick : Roo.emptyFn
8215 });
8216  /*
8217  * Based on:
8218  * Ext JS Library 1.1.1
8219  * Copyright(c) 2006-2007, Ext JS, LLC.
8220  *
8221  * Originally Released Under LGPL - original licence link has changed is not relivant.
8222  *
8223  * Fork - LGPL
8224  * <script type="text/javascript">
8225  */
8226
8227
8228 /**
8229  * @class Roo.data.SortTypes
8230  * @singleton
8231  * Defines the default sorting (casting?) comparison functions used when sorting data.
8232  */
8233 Roo.data.SortTypes = {
8234     /**
8235      * Default sort that does nothing
8236      * @param {Mixed} s The value being converted
8237      * @return {Mixed} The comparison value
8238      */
8239     none : function(s){
8240         return s;
8241     },
8242     
8243     /**
8244      * The regular expression used to strip tags
8245      * @type {RegExp}
8246      * @property
8247      */
8248     stripTagsRE : /<\/?[^>]+>/gi,
8249     
8250     /**
8251      * Strips all HTML tags to sort on text only
8252      * @param {Mixed} s The value being converted
8253      * @return {String} The comparison value
8254      */
8255     asText : function(s){
8256         return String(s).replace(this.stripTagsRE, "");
8257     },
8258     
8259     /**
8260      * Strips all HTML tags to sort on text only - Case insensitive
8261      * @param {Mixed} s The value being converted
8262      * @return {String} The comparison value
8263      */
8264     asUCText : function(s){
8265         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8266     },
8267     
8268     /**
8269      * Case insensitive string
8270      * @param {Mixed} s The value being converted
8271      * @return {String} The comparison value
8272      */
8273     asUCString : function(s) {
8274         return String(s).toUpperCase();
8275     },
8276     
8277     /**
8278      * Date sorting
8279      * @param {Mixed} s The value being converted
8280      * @return {Number} The comparison value
8281      */
8282     asDate : function(s) {
8283         if(!s){
8284             return 0;
8285         }
8286         if(s instanceof Date){
8287             return s.getTime();
8288         }
8289         return Date.parse(String(s));
8290     },
8291     
8292     /**
8293      * Float sorting
8294      * @param {Mixed} s The value being converted
8295      * @return {Float} The comparison value
8296      */
8297     asFloat : function(s) {
8298         var val = parseFloat(String(s).replace(/,/g, ""));
8299         if(isNaN(val)) val = 0;
8300         return val;
8301     },
8302     
8303     /**
8304      * Integer sorting
8305      * @param {Mixed} s The value being converted
8306      * @return {Number} The comparison value
8307      */
8308     asInt : function(s) {
8309         var val = parseInt(String(s).replace(/,/g, ""));
8310         if(isNaN(val)) val = 0;
8311         return val;
8312     }
8313 };/*
8314  * Based on:
8315  * Ext JS Library 1.1.1
8316  * Copyright(c) 2006-2007, Ext JS, LLC.
8317  *
8318  * Originally Released Under LGPL - original licence link has changed is not relivant.
8319  *
8320  * Fork - LGPL
8321  * <script type="text/javascript">
8322  */
8323
8324 /**
8325 * @class Roo.data.Record
8326  * Instances of this class encapsulate both record <em>definition</em> information, and record
8327  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8328  * to access Records cached in an {@link Roo.data.Store} object.<br>
8329  * <p>
8330  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8331  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8332  * objects.<br>
8333  * <p>
8334  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8335  * @constructor
8336  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8337  * {@link #create}. The parameters are the same.
8338  * @param {Array} data An associative Array of data values keyed by the field name.
8339  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8340  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8341  * not specified an integer id is generated.
8342  */
8343 Roo.data.Record = function(data, id){
8344     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8345     this.data = data;
8346 };
8347
8348 /**
8349  * Generate a constructor for a specific record layout.
8350  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8351  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8352  * Each field definition object may contain the following properties: <ul>
8353  * <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,
8354  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8355  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8356  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8357  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8358  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8359  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8360  * this may be omitted.</p></li>
8361  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8362  * <ul><li>auto (Default, implies no conversion)</li>
8363  * <li>string</li>
8364  * <li>int</li>
8365  * <li>float</li>
8366  * <li>boolean</li>
8367  * <li>date</li></ul></p></li>
8368  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8369  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8370  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8371  * by the Reader into an object that will be stored in the Record. It is passed the
8372  * following parameters:<ul>
8373  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8374  * </ul></p></li>
8375  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8376  * </ul>
8377  * <br>usage:<br><pre><code>
8378 var TopicRecord = Roo.data.Record.create(
8379     {name: 'title', mapping: 'topic_title'},
8380     {name: 'author', mapping: 'username'},
8381     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8382     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8383     {name: 'lastPoster', mapping: 'user2'},
8384     {name: 'excerpt', mapping: 'post_text'}
8385 );
8386
8387 var myNewRecord = new TopicRecord({
8388     title: 'Do my job please',
8389     author: 'noobie',
8390     totalPosts: 1,
8391     lastPost: new Date(),
8392     lastPoster: 'Animal',
8393     excerpt: 'No way dude!'
8394 });
8395 myStore.add(myNewRecord);
8396 </code></pre>
8397  * @method create
8398  * @static
8399  */
8400 Roo.data.Record.create = function(o){
8401     var f = function(){
8402         f.superclass.constructor.apply(this, arguments);
8403     };
8404     Roo.extend(f, Roo.data.Record);
8405     var p = f.prototype;
8406     p.fields = new Roo.util.MixedCollection(false, function(field){
8407         return field.name;
8408     });
8409     for(var i = 0, len = o.length; i < len; i++){
8410         p.fields.add(new Roo.data.Field(o[i]));
8411     }
8412     f.getField = function(name){
8413         return p.fields.get(name);  
8414     };
8415     return f;
8416 };
8417
8418 Roo.data.Record.AUTO_ID = 1000;
8419 Roo.data.Record.EDIT = 'edit';
8420 Roo.data.Record.REJECT = 'reject';
8421 Roo.data.Record.COMMIT = 'commit';
8422
8423 Roo.data.Record.prototype = {
8424     /**
8425      * Readonly flag - true if this record has been modified.
8426      * @type Boolean
8427      */
8428     dirty : false,
8429     editing : false,
8430     error: null,
8431     modified: null,
8432
8433     // private
8434     join : function(store){
8435         this.store = store;
8436     },
8437
8438     /**
8439      * Set the named field to the specified value.
8440      * @param {String} name The name of the field to set.
8441      * @param {Object} value The value to set the field to.
8442      */
8443     set : function(name, value){
8444         if(this.data[name] == value){
8445             return;
8446         }
8447         this.dirty = true;
8448         if(!this.modified){
8449             this.modified = {};
8450         }
8451         if(typeof this.modified[name] == 'undefined'){
8452             this.modified[name] = this.data[name];
8453         }
8454         this.data[name] = value;
8455         if(!this.editing && this.store){
8456             this.store.afterEdit(this);
8457         }       
8458     },
8459
8460     /**
8461      * Get the value of the named field.
8462      * @param {String} name The name of the field to get the value of.
8463      * @return {Object} The value of the field.
8464      */
8465     get : function(name){
8466         return this.data[name]; 
8467     },
8468
8469     // private
8470     beginEdit : function(){
8471         this.editing = true;
8472         this.modified = {}; 
8473     },
8474
8475     // private
8476     cancelEdit : function(){
8477         this.editing = false;
8478         delete this.modified;
8479     },
8480
8481     // private
8482     endEdit : function(){
8483         this.editing = false;
8484         if(this.dirty && this.store){
8485             this.store.afterEdit(this);
8486         }
8487     },
8488
8489     /**
8490      * Usually called by the {@link Roo.data.Store} which owns the Record.
8491      * Rejects all changes made to the Record since either creation, or the last commit operation.
8492      * Modified fields are reverted to their original values.
8493      * <p>
8494      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8495      * of reject operations.
8496      */
8497     reject : function(){
8498         var m = this.modified;
8499         for(var n in m){
8500             if(typeof m[n] != "function"){
8501                 this.data[n] = m[n];
8502             }
8503         }
8504         this.dirty = false;
8505         delete this.modified;
8506         this.editing = false;
8507         if(this.store){
8508             this.store.afterReject(this);
8509         }
8510     },
8511
8512     /**
8513      * Usually called by the {@link Roo.data.Store} which owns the Record.
8514      * Commits all changes made to the Record since either creation, or the last commit operation.
8515      * <p>
8516      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8517      * of commit operations.
8518      */
8519     commit : function(){
8520         this.dirty = false;
8521         delete this.modified;
8522         this.editing = false;
8523         if(this.store){
8524             this.store.afterCommit(this);
8525         }
8526     },
8527
8528     // private
8529     hasError : function(){
8530         return this.error != null;
8531     },
8532
8533     // private
8534     clearError : function(){
8535         this.error = null;
8536     },
8537
8538     /**
8539      * Creates a copy of this record.
8540      * @param {String} id (optional) A new record id if you don't want to use this record's id
8541      * @return {Record}
8542      */
8543     copy : function(newId) {
8544         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8545     }
8546 };/*
8547  * Based on:
8548  * Ext JS Library 1.1.1
8549  * Copyright(c) 2006-2007, Ext JS, LLC.
8550  *
8551  * Originally Released Under LGPL - original licence link has changed is not relivant.
8552  *
8553  * Fork - LGPL
8554  * <script type="text/javascript">
8555  */
8556
8557
8558
8559 /**
8560  * @class Roo.data.Store
8561  * @extends Roo.util.Observable
8562  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8563  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8564  * <p>
8565  * 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
8566  * has no knowledge of the format of the data returned by the Proxy.<br>
8567  * <p>
8568  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8569  * instances from the data object. These records are cached and made available through accessor functions.
8570  * @constructor
8571  * Creates a new Store.
8572  * @param {Object} config A config object containing the objects needed for the Store to access data,
8573  * and read the data into Records.
8574  */
8575 Roo.data.Store = function(config){
8576     this.data = new Roo.util.MixedCollection(false);
8577     this.data.getKey = function(o){
8578         return o.id;
8579     };
8580     this.baseParams = {};
8581     // private
8582     this.paramNames = {
8583         "start" : "start",
8584         "limit" : "limit",
8585         "sort" : "sort",
8586         "dir" : "dir",
8587         "multisort" : "_multisort"
8588     };
8589
8590     if(config && config.data){
8591         this.inlineData = config.data;
8592         delete config.data;
8593     }
8594
8595     Roo.apply(this, config);
8596     
8597     if(this.reader){ // reader passed
8598         this.reader = Roo.factory(this.reader, Roo.data);
8599         this.reader.xmodule = this.xmodule || false;
8600         if(!this.recordType){
8601             this.recordType = this.reader.recordType;
8602         }
8603         if(this.reader.onMetaChange){
8604             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8605         }
8606     }
8607
8608     if(this.recordType){
8609         this.fields = this.recordType.prototype.fields;
8610     }
8611     this.modified = [];
8612
8613     this.addEvents({
8614         /**
8615          * @event datachanged
8616          * Fires when the data cache has changed, and a widget which is using this Store
8617          * as a Record cache should refresh its view.
8618          * @param {Store} this
8619          */
8620         datachanged : true,
8621         /**
8622          * @event metachange
8623          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8624          * @param {Store} this
8625          * @param {Object} meta The JSON metadata
8626          */
8627         metachange : true,
8628         /**
8629          * @event add
8630          * Fires when Records have been added to the Store
8631          * @param {Store} this
8632          * @param {Roo.data.Record[]} records The array of Records added
8633          * @param {Number} index The index at which the record(s) were added
8634          */
8635         add : true,
8636         /**
8637          * @event remove
8638          * Fires when a Record has been removed from the Store
8639          * @param {Store} this
8640          * @param {Roo.data.Record} record The Record that was removed
8641          * @param {Number} index The index at which the record was removed
8642          */
8643         remove : true,
8644         /**
8645          * @event update
8646          * Fires when a Record has been updated
8647          * @param {Store} this
8648          * @param {Roo.data.Record} record The Record that was updated
8649          * @param {String} operation The update operation being performed.  Value may be one of:
8650          * <pre><code>
8651  Roo.data.Record.EDIT
8652  Roo.data.Record.REJECT
8653  Roo.data.Record.COMMIT
8654          * </code></pre>
8655          */
8656         update : true,
8657         /**
8658          * @event clear
8659          * Fires when the data cache has been cleared.
8660          * @param {Store} this
8661          */
8662         clear : true,
8663         /**
8664          * @event beforeload
8665          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8666          * the load action will be canceled.
8667          * @param {Store} this
8668          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8669          */
8670         beforeload : true,
8671         /**
8672          * @event beforeloadadd
8673          * Fires after a new set of Records has been loaded.
8674          * @param {Store} this
8675          * @param {Roo.data.Record[]} records The Records that were loaded
8676          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8677          */
8678         beforeloadadd : true,
8679         /**
8680          * @event load
8681          * Fires after a new set of Records has been loaded, before they are added to the store.
8682          * @param {Store} this
8683          * @param {Roo.data.Record[]} records The Records that were loaded
8684          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8685          * @params {Object} return from reader
8686          */
8687         load : true,
8688         /**
8689          * @event loadexception
8690          * Fires if an exception occurs in the Proxy during loading.
8691          * Called with the signature of the Proxy's "loadexception" event.
8692          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8693          * 
8694          * @param {Proxy} 
8695          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8696          * @param {Object} load options 
8697          * @param {Object} jsonData from your request (normally this contains the Exception)
8698          */
8699         loadexception : true
8700     });
8701     
8702     if(this.proxy){
8703         this.proxy = Roo.factory(this.proxy, Roo.data);
8704         this.proxy.xmodule = this.xmodule || false;
8705         this.relayEvents(this.proxy,  ["loadexception"]);
8706     }
8707     this.sortToggle = {};
8708     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8709
8710     Roo.data.Store.superclass.constructor.call(this);
8711
8712     if(this.inlineData){
8713         this.loadData(this.inlineData);
8714         delete this.inlineData;
8715     }
8716 };
8717
8718 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8719      /**
8720     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8721     * without a remote query - used by combo/forms at present.
8722     */
8723     
8724     /**
8725     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8726     */
8727     /**
8728     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8729     */
8730     /**
8731     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8732     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8733     */
8734     /**
8735     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8736     * on any HTTP request
8737     */
8738     /**
8739     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8740     */
8741     /**
8742     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8743     */
8744     multiSort: false,
8745     /**
8746     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8747     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8748     */
8749     remoteSort : false,
8750
8751     /**
8752     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8753      * loaded or when a record is removed. (defaults to false).
8754     */
8755     pruneModifiedRecords : false,
8756
8757     // private
8758     lastOptions : null,
8759
8760     /**
8761      * Add Records to the Store and fires the add event.
8762      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8763      */
8764     add : function(records){
8765         records = [].concat(records);
8766         for(var i = 0, len = records.length; i < len; i++){
8767             records[i].join(this);
8768         }
8769         var index = this.data.length;
8770         this.data.addAll(records);
8771         this.fireEvent("add", this, records, index);
8772     },
8773
8774     /**
8775      * Remove a Record from the Store and fires the remove event.
8776      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8777      */
8778     remove : function(record){
8779         var index = this.data.indexOf(record);
8780         this.data.removeAt(index);
8781         if(this.pruneModifiedRecords){
8782             this.modified.remove(record);
8783         }
8784         this.fireEvent("remove", this, record, index);
8785     },
8786
8787     /**
8788      * Remove all Records from the Store and fires the clear event.
8789      */
8790     removeAll : function(){
8791         this.data.clear();
8792         if(this.pruneModifiedRecords){
8793             this.modified = [];
8794         }
8795         this.fireEvent("clear", this);
8796     },
8797
8798     /**
8799      * Inserts Records to the Store at the given index and fires the add event.
8800      * @param {Number} index The start index at which to insert the passed Records.
8801      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8802      */
8803     insert : function(index, records){
8804         records = [].concat(records);
8805         for(var i = 0, len = records.length; i < len; i++){
8806             this.data.insert(index, records[i]);
8807             records[i].join(this);
8808         }
8809         this.fireEvent("add", this, records, index);
8810     },
8811
8812     /**
8813      * Get the index within the cache of the passed Record.
8814      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8815      * @return {Number} The index of the passed Record. Returns -1 if not found.
8816      */
8817     indexOf : function(record){
8818         return this.data.indexOf(record);
8819     },
8820
8821     /**
8822      * Get the index within the cache of the Record with the passed id.
8823      * @param {String} id The id of the Record to find.
8824      * @return {Number} The index of the Record. Returns -1 if not found.
8825      */
8826     indexOfId : function(id){
8827         return this.data.indexOfKey(id);
8828     },
8829
8830     /**
8831      * Get the Record with the specified id.
8832      * @param {String} id The id of the Record to find.
8833      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8834      */
8835     getById : function(id){
8836         return this.data.key(id);
8837     },
8838
8839     /**
8840      * Get the Record at the specified index.
8841      * @param {Number} index The index of the Record to find.
8842      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8843      */
8844     getAt : function(index){
8845         return this.data.itemAt(index);
8846     },
8847
8848     /**
8849      * Returns a range of Records between specified indices.
8850      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8851      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8852      * @return {Roo.data.Record[]} An array of Records
8853      */
8854     getRange : function(start, end){
8855         return this.data.getRange(start, end);
8856     },
8857
8858     // private
8859     storeOptions : function(o){
8860         o = Roo.apply({}, o);
8861         delete o.callback;
8862         delete o.scope;
8863         this.lastOptions = o;
8864     },
8865
8866     /**
8867      * Loads the Record cache from the configured Proxy using the configured Reader.
8868      * <p>
8869      * If using remote paging, then the first load call must specify the <em>start</em>
8870      * and <em>limit</em> properties in the options.params property to establish the initial
8871      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8872      * <p>
8873      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8874      * and this call will return before the new data has been loaded. Perform any post-processing
8875      * in a callback function, or in a "load" event handler.</strong>
8876      * <p>
8877      * @param {Object} options An object containing properties which control loading options:<ul>
8878      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8879      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8880      * passed the following arguments:<ul>
8881      * <li>r : Roo.data.Record[]</li>
8882      * <li>options: Options object from the load call</li>
8883      * <li>success: Boolean success indicator</li></ul></li>
8884      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8885      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8886      * </ul>
8887      */
8888     load : function(options){
8889         options = options || {};
8890         if(this.fireEvent("beforeload", this, options) !== false){
8891             this.storeOptions(options);
8892             var p = Roo.apply(options.params || {}, this.baseParams);
8893             // if meta was not loaded from remote source.. try requesting it.
8894             if (!this.reader.metaFromRemote) {
8895                 p._requestMeta = 1;
8896             }
8897             if(this.sortInfo && this.remoteSort){
8898                 var pn = this.paramNames;
8899                 p[pn["sort"]] = this.sortInfo.field;
8900                 p[pn["dir"]] = this.sortInfo.direction;
8901             }
8902             if (this.multiSort) {
8903                 var pn = this.paramNames;
8904                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8905             }
8906             
8907             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8908         }
8909     },
8910
8911     /**
8912      * Reloads the Record cache from the configured Proxy using the configured Reader and
8913      * the options from the last load operation performed.
8914      * @param {Object} options (optional) An object containing properties which may override the options
8915      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8916      * the most recently used options are reused).
8917      */
8918     reload : function(options){
8919         this.load(Roo.applyIf(options||{}, this.lastOptions));
8920     },
8921
8922     // private
8923     // Called as a callback by the Reader during a load operation.
8924     loadRecords : function(o, options, success){
8925         if(!o || success === false){
8926             if(success !== false){
8927                 this.fireEvent("load", this, [], options, o);
8928             }
8929             if(options.callback){
8930                 options.callback.call(options.scope || this, [], options, false);
8931             }
8932             return;
8933         }
8934         // if data returned failure - throw an exception.
8935         if (o.success === false) {
8936             // show a message if no listener is registered.
8937             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8938                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8939             }
8940             // loadmask wil be hooked into this..
8941             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8942             return;
8943         }
8944         var r = o.records, t = o.totalRecords || r.length;
8945         
8946         this.fireEvent("beforeloadadd", this, r, options, o);
8947         
8948         if(!options || options.add !== true){
8949             if(this.pruneModifiedRecords){
8950                 this.modified = [];
8951             }
8952             for(var i = 0, len = r.length; i < len; i++){
8953                 r[i].join(this);
8954             }
8955             if(this.snapshot){
8956                 this.data = this.snapshot;
8957                 delete this.snapshot;
8958             }
8959             this.data.clear();
8960             this.data.addAll(r);
8961             this.totalLength = t;
8962             this.applySort();
8963             this.fireEvent("datachanged", this);
8964         }else{
8965             this.totalLength = Math.max(t, this.data.length+r.length);
8966             this.add(r);
8967         }
8968         this.fireEvent("load", this, r, options, o);
8969         if(options.callback){
8970             options.callback.call(options.scope || this, r, options, true);
8971         }
8972     },
8973
8974
8975     /**
8976      * Loads data from a passed data block. A Reader which understands the format of the data
8977      * must have been configured in the constructor.
8978      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8979      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8980      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8981      */
8982     loadData : function(o, append){
8983         var r = this.reader.readRecords(o);
8984         this.loadRecords(r, {add: append}, true);
8985     },
8986
8987     /**
8988      * Gets the number of cached records.
8989      * <p>
8990      * <em>If using paging, this may not be the total size of the dataset. If the data object
8991      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8992      * the data set size</em>
8993      */
8994     getCount : function(){
8995         return this.data.length || 0;
8996     },
8997
8998     /**
8999      * Gets the total number of records in the dataset as returned by the server.
9000      * <p>
9001      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9002      * the dataset size</em>
9003      */
9004     getTotalCount : function(){
9005         return this.totalLength || 0;
9006     },
9007
9008     /**
9009      * Returns the sort state of the Store as an object with two properties:
9010      * <pre><code>
9011  field {String} The name of the field by which the Records are sorted
9012  direction {String} The sort order, "ASC" or "DESC"
9013      * </code></pre>
9014      */
9015     getSortState : function(){
9016         return this.sortInfo;
9017     },
9018
9019     // private
9020     applySort : function(){
9021         if(this.sortInfo && !this.remoteSort){
9022             var s = this.sortInfo, f = s.field;
9023             var st = this.fields.get(f).sortType;
9024             var fn = function(r1, r2){
9025                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9026                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9027             };
9028             this.data.sort(s.direction, fn);
9029             if(this.snapshot && this.snapshot != this.data){
9030                 this.snapshot.sort(s.direction, fn);
9031             }
9032         }
9033     },
9034
9035     /**
9036      * Sets the default sort column and order to be used by the next load operation.
9037      * @param {String} fieldName The name of the field to sort by.
9038      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9039      */
9040     setDefaultSort : function(field, dir){
9041         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9042     },
9043
9044     /**
9045      * Sort the Records.
9046      * If remote sorting is used, the sort is performed on the server, and the cache is
9047      * reloaded. If local sorting is used, the cache is sorted internally.
9048      * @param {String} fieldName The name of the field to sort by.
9049      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9050      */
9051     sort : function(fieldName, dir){
9052         var f = this.fields.get(fieldName);
9053         if(!dir){
9054             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9055             
9056             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9057                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9058             }else{
9059                 dir = f.sortDir;
9060             }
9061         }
9062         this.sortToggle[f.name] = dir;
9063         this.sortInfo = {field: f.name, direction: dir};
9064         if(!this.remoteSort){
9065             this.applySort();
9066             this.fireEvent("datachanged", this);
9067         }else{
9068             this.load(this.lastOptions);
9069         }
9070     },
9071
9072     /**
9073      * Calls the specified function for each of the Records in the cache.
9074      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9075      * Returning <em>false</em> aborts and exits the iteration.
9076      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9077      */
9078     each : function(fn, scope){
9079         this.data.each(fn, scope);
9080     },
9081
9082     /**
9083      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9084      * (e.g., during paging).
9085      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9086      */
9087     getModifiedRecords : function(){
9088         return this.modified;
9089     },
9090
9091     // private
9092     createFilterFn : function(property, value, anyMatch){
9093         if(!value.exec){ // not a regex
9094             value = String(value);
9095             if(value.length == 0){
9096                 return false;
9097             }
9098             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9099         }
9100         return function(r){
9101             return value.test(r.data[property]);
9102         };
9103     },
9104
9105     /**
9106      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9107      * @param {String} property A field on your records
9108      * @param {Number} start The record index to start at (defaults to 0)
9109      * @param {Number} end The last record index to include (defaults to length - 1)
9110      * @return {Number} The sum
9111      */
9112     sum : function(property, start, end){
9113         var rs = this.data.items, v = 0;
9114         start = start || 0;
9115         end = (end || end === 0) ? end : rs.length-1;
9116
9117         for(var i = start; i <= end; i++){
9118             v += (rs[i].data[property] || 0);
9119         }
9120         return v;
9121     },
9122
9123     /**
9124      * Filter the records by a specified property.
9125      * @param {String} field A field on your records
9126      * @param {String/RegExp} value Either a string that the field
9127      * should start with or a RegExp to test against the field
9128      * @param {Boolean} anyMatch True to match any part not just the beginning
9129      */
9130     filter : function(property, value, anyMatch){
9131         var fn = this.createFilterFn(property, value, anyMatch);
9132         return fn ? this.filterBy(fn) : this.clearFilter();
9133     },
9134
9135     /**
9136      * Filter by a function. The specified function will be called with each
9137      * record in this data source. If the function returns true the record is included,
9138      * otherwise it is filtered.
9139      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9140      * @param {Object} scope (optional) The scope of the function (defaults to this)
9141      */
9142     filterBy : function(fn, scope){
9143         this.snapshot = this.snapshot || this.data;
9144         this.data = this.queryBy(fn, scope||this);
9145         this.fireEvent("datachanged", this);
9146     },
9147
9148     /**
9149      * Query the records by a specified property.
9150      * @param {String} field A field on your records
9151      * @param {String/RegExp} value Either a string that the field
9152      * should start with or a RegExp to test against the field
9153      * @param {Boolean} anyMatch True to match any part not just the beginning
9154      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9155      */
9156     query : function(property, value, anyMatch){
9157         var fn = this.createFilterFn(property, value, anyMatch);
9158         return fn ? this.queryBy(fn) : this.data.clone();
9159     },
9160
9161     /**
9162      * Query by a function. The specified function will be called with each
9163      * record in this data source. If the function returns true the record is included
9164      * in the results.
9165      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9166      * @param {Object} scope (optional) The scope of the function (defaults to this)
9167       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9168      **/
9169     queryBy : function(fn, scope){
9170         var data = this.snapshot || this.data;
9171         return data.filterBy(fn, scope||this);
9172     },
9173
9174     /**
9175      * Collects unique values for a particular dataIndex from this store.
9176      * @param {String} dataIndex The property to collect
9177      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9178      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9179      * @return {Array} An array of the unique values
9180      **/
9181     collect : function(dataIndex, allowNull, bypassFilter){
9182         var d = (bypassFilter === true && this.snapshot) ?
9183                 this.snapshot.items : this.data.items;
9184         var v, sv, r = [], l = {};
9185         for(var i = 0, len = d.length; i < len; i++){
9186             v = d[i].data[dataIndex];
9187             sv = String(v);
9188             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9189                 l[sv] = true;
9190                 r[r.length] = v;
9191             }
9192         }
9193         return r;
9194     },
9195
9196     /**
9197      * Revert to a view of the Record cache with no filtering applied.
9198      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9199      */
9200     clearFilter : function(suppressEvent){
9201         if(this.snapshot && this.snapshot != this.data){
9202             this.data = this.snapshot;
9203             delete this.snapshot;
9204             if(suppressEvent !== true){
9205                 this.fireEvent("datachanged", this);
9206             }
9207         }
9208     },
9209
9210     // private
9211     afterEdit : function(record){
9212         if(this.modified.indexOf(record) == -1){
9213             this.modified.push(record);
9214         }
9215         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9216     },
9217     
9218     // private
9219     afterReject : function(record){
9220         this.modified.remove(record);
9221         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9222     },
9223
9224     // private
9225     afterCommit : function(record){
9226         this.modified.remove(record);
9227         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9228     },
9229
9230     /**
9231      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9232      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9233      */
9234     commitChanges : function(){
9235         var m = this.modified.slice(0);
9236         this.modified = [];
9237         for(var i = 0, len = m.length; i < len; i++){
9238             m[i].commit();
9239         }
9240     },
9241
9242     /**
9243      * Cancel outstanding changes on all changed records.
9244      */
9245     rejectChanges : function(){
9246         var m = this.modified.slice(0);
9247         this.modified = [];
9248         for(var i = 0, len = m.length; i < len; i++){
9249             m[i].reject();
9250         }
9251     },
9252
9253     onMetaChange : function(meta, rtype, o){
9254         this.recordType = rtype;
9255         this.fields = rtype.prototype.fields;
9256         delete this.snapshot;
9257         this.sortInfo = meta.sortInfo || this.sortInfo;
9258         this.modified = [];
9259         this.fireEvent('metachange', this, this.reader.meta);
9260     },
9261     
9262     moveIndex : function(data, type)
9263     {
9264         var index = this.indexOf(data);
9265         
9266         var newIndex = index + type;
9267         
9268         this.remove(data);
9269         
9270         this.insert(newIndex, data);
9271         
9272     }
9273 });/*
9274  * Based on:
9275  * Ext JS Library 1.1.1
9276  * Copyright(c) 2006-2007, Ext JS, LLC.
9277  *
9278  * Originally Released Under LGPL - original licence link has changed is not relivant.
9279  *
9280  * Fork - LGPL
9281  * <script type="text/javascript">
9282  */
9283
9284 /**
9285  * @class Roo.data.SimpleStore
9286  * @extends Roo.data.Store
9287  * Small helper class to make creating Stores from Array data easier.
9288  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9289  * @cfg {Array} fields An array of field definition objects, or field name strings.
9290  * @cfg {Array} data The multi-dimensional array of data
9291  * @constructor
9292  * @param {Object} config
9293  */
9294 Roo.data.SimpleStore = function(config){
9295     Roo.data.SimpleStore.superclass.constructor.call(this, {
9296         isLocal : true,
9297         reader: new Roo.data.ArrayReader({
9298                 id: config.id
9299             },
9300             Roo.data.Record.create(config.fields)
9301         ),
9302         proxy : new Roo.data.MemoryProxy(config.data)
9303     });
9304     this.load();
9305 };
9306 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9307  * Based on:
9308  * Ext JS Library 1.1.1
9309  * Copyright(c) 2006-2007, Ext JS, LLC.
9310  *
9311  * Originally Released Under LGPL - original licence link has changed is not relivant.
9312  *
9313  * Fork - LGPL
9314  * <script type="text/javascript">
9315  */
9316
9317 /**
9318 /**
9319  * @extends Roo.data.Store
9320  * @class Roo.data.JsonStore
9321  * Small helper class to make creating Stores for JSON data easier. <br/>
9322 <pre><code>
9323 var store = new Roo.data.JsonStore({
9324     url: 'get-images.php',
9325     root: 'images',
9326     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9327 });
9328 </code></pre>
9329  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9330  * JsonReader and HttpProxy (unless inline data is provided).</b>
9331  * @cfg {Array} fields An array of field definition objects, or field name strings.
9332  * @constructor
9333  * @param {Object} config
9334  */
9335 Roo.data.JsonStore = function(c){
9336     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9337         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9338         reader: new Roo.data.JsonReader(c, c.fields)
9339     }));
9340 };
9341 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9342  * Based on:
9343  * Ext JS Library 1.1.1
9344  * Copyright(c) 2006-2007, Ext JS, LLC.
9345  *
9346  * Originally Released Under LGPL - original licence link has changed is not relivant.
9347  *
9348  * Fork - LGPL
9349  * <script type="text/javascript">
9350  */
9351
9352  
9353 Roo.data.Field = function(config){
9354     if(typeof config == "string"){
9355         config = {name: config};
9356     }
9357     Roo.apply(this, config);
9358     
9359     if(!this.type){
9360         this.type = "auto";
9361     }
9362     
9363     var st = Roo.data.SortTypes;
9364     // named sortTypes are supported, here we look them up
9365     if(typeof this.sortType == "string"){
9366         this.sortType = st[this.sortType];
9367     }
9368     
9369     // set default sortType for strings and dates
9370     if(!this.sortType){
9371         switch(this.type){
9372             case "string":
9373                 this.sortType = st.asUCString;
9374                 break;
9375             case "date":
9376                 this.sortType = st.asDate;
9377                 break;
9378             default:
9379                 this.sortType = st.none;
9380         }
9381     }
9382
9383     // define once
9384     var stripRe = /[\$,%]/g;
9385
9386     // prebuilt conversion function for this field, instead of
9387     // switching every time we're reading a value
9388     if(!this.convert){
9389         var cv, dateFormat = this.dateFormat;
9390         switch(this.type){
9391             case "":
9392             case "auto":
9393             case undefined:
9394                 cv = function(v){ return v; };
9395                 break;
9396             case "string":
9397                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9398                 break;
9399             case "int":
9400                 cv = function(v){
9401                     return v !== undefined && v !== null && v !== '' ?
9402                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9403                     };
9404                 break;
9405             case "float":
9406                 cv = function(v){
9407                     return v !== undefined && v !== null && v !== '' ?
9408                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9409                     };
9410                 break;
9411             case "bool":
9412             case "boolean":
9413                 cv = function(v){ return v === true || v === "true" || v == 1; };
9414                 break;
9415             case "date":
9416                 cv = function(v){
9417                     if(!v){
9418                         return '';
9419                     }
9420                     if(v instanceof Date){
9421                         return v;
9422                     }
9423                     if(dateFormat){
9424                         if(dateFormat == "timestamp"){
9425                             return new Date(v*1000);
9426                         }
9427                         return Date.parseDate(v, dateFormat);
9428                     }
9429                     var parsed = Date.parse(v);
9430                     return parsed ? new Date(parsed) : null;
9431                 };
9432              break;
9433             
9434         }
9435         this.convert = cv;
9436     }
9437 };
9438
9439 Roo.data.Field.prototype = {
9440     dateFormat: null,
9441     defaultValue: "",
9442     mapping: null,
9443     sortType : null,
9444     sortDir : "ASC"
9445 };/*
9446  * Based on:
9447  * Ext JS Library 1.1.1
9448  * Copyright(c) 2006-2007, Ext JS, LLC.
9449  *
9450  * Originally Released Under LGPL - original licence link has changed is not relivant.
9451  *
9452  * Fork - LGPL
9453  * <script type="text/javascript">
9454  */
9455  
9456 // Base class for reading structured data from a data source.  This class is intended to be
9457 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9458
9459 /**
9460  * @class Roo.data.DataReader
9461  * Base class for reading structured data from a data source.  This class is intended to be
9462  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9463  */
9464
9465 Roo.data.DataReader = function(meta, recordType){
9466     
9467     this.meta = meta;
9468     
9469     this.recordType = recordType instanceof Array ? 
9470         Roo.data.Record.create(recordType) : recordType;
9471 };
9472
9473 Roo.data.DataReader.prototype = {
9474      /**
9475      * Create an empty record
9476      * @param {Object} data (optional) - overlay some values
9477      * @return {Roo.data.Record} record created.
9478      */
9479     newRow :  function(d) {
9480         var da =  {};
9481         this.recordType.prototype.fields.each(function(c) {
9482             switch( c.type) {
9483                 case 'int' : da[c.name] = 0; break;
9484                 case 'date' : da[c.name] = new Date(); break;
9485                 case 'float' : da[c.name] = 0.0; break;
9486                 case 'boolean' : da[c.name] = false; break;
9487                 default : da[c.name] = ""; break;
9488             }
9489             
9490         });
9491         return new this.recordType(Roo.apply(da, d));
9492     }
9493     
9494 };/*
9495  * Based on:
9496  * Ext JS Library 1.1.1
9497  * Copyright(c) 2006-2007, Ext JS, LLC.
9498  *
9499  * Originally Released Under LGPL - original licence link has changed is not relivant.
9500  *
9501  * Fork - LGPL
9502  * <script type="text/javascript">
9503  */
9504
9505 /**
9506  * @class Roo.data.DataProxy
9507  * @extends Roo.data.Observable
9508  * This class is an abstract base class for implementations which provide retrieval of
9509  * unformatted data objects.<br>
9510  * <p>
9511  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9512  * (of the appropriate type which knows how to parse the data object) to provide a block of
9513  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9514  * <p>
9515  * Custom implementations must implement the load method as described in
9516  * {@link Roo.data.HttpProxy#load}.
9517  */
9518 Roo.data.DataProxy = function(){
9519     this.addEvents({
9520         /**
9521          * @event beforeload
9522          * Fires before a network request is made to retrieve a data object.
9523          * @param {Object} This DataProxy object.
9524          * @param {Object} params The params parameter to the load function.
9525          */
9526         beforeload : true,
9527         /**
9528          * @event load
9529          * Fires before the load method's callback is called.
9530          * @param {Object} This DataProxy object.
9531          * @param {Object} o The data object.
9532          * @param {Object} arg The callback argument object passed to the load function.
9533          */
9534         load : true,
9535         /**
9536          * @event loadexception
9537          * Fires if an Exception occurs during data retrieval.
9538          * @param {Object} This DataProxy object.
9539          * @param {Object} o The data object.
9540          * @param {Object} arg The callback argument object passed to the load function.
9541          * @param {Object} e The Exception.
9542          */
9543         loadexception : true
9544     });
9545     Roo.data.DataProxy.superclass.constructor.call(this);
9546 };
9547
9548 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9549
9550     /**
9551      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9552      */
9553 /*
9554  * Based on:
9555  * Ext JS Library 1.1.1
9556  * Copyright(c) 2006-2007, Ext JS, LLC.
9557  *
9558  * Originally Released Under LGPL - original licence link has changed is not relivant.
9559  *
9560  * Fork - LGPL
9561  * <script type="text/javascript">
9562  */
9563 /**
9564  * @class Roo.data.MemoryProxy
9565  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9566  * to the Reader when its load method is called.
9567  * @constructor
9568  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9569  */
9570 Roo.data.MemoryProxy = function(data){
9571     if (data.data) {
9572         data = data.data;
9573     }
9574     Roo.data.MemoryProxy.superclass.constructor.call(this);
9575     this.data = data;
9576 };
9577
9578 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9579     /**
9580      * Load data from the requested source (in this case an in-memory
9581      * data object passed to the constructor), read the data object into
9582      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9583      * process that block using the passed callback.
9584      * @param {Object} params This parameter is not used by the MemoryProxy class.
9585      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9586      * object into a block of Roo.data.Records.
9587      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9588      * The function must be passed <ul>
9589      * <li>The Record block object</li>
9590      * <li>The "arg" argument from the load function</li>
9591      * <li>A boolean success indicator</li>
9592      * </ul>
9593      * @param {Object} scope The scope in which to call the callback
9594      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9595      */
9596     load : function(params, reader, callback, scope, arg){
9597         params = params || {};
9598         var result;
9599         try {
9600             result = reader.readRecords(this.data);
9601         }catch(e){
9602             this.fireEvent("loadexception", this, arg, null, e);
9603             callback.call(scope, null, arg, false);
9604             return;
9605         }
9606         callback.call(scope, result, arg, true);
9607     },
9608     
9609     // private
9610     update : function(params, records){
9611         
9612     }
9613 });/*
9614  * Based on:
9615  * Ext JS Library 1.1.1
9616  * Copyright(c) 2006-2007, Ext JS, LLC.
9617  *
9618  * Originally Released Under LGPL - original licence link has changed is not relivant.
9619  *
9620  * Fork - LGPL
9621  * <script type="text/javascript">
9622  */
9623 /**
9624  * @class Roo.data.HttpProxy
9625  * @extends Roo.data.DataProxy
9626  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9627  * configured to reference a certain URL.<br><br>
9628  * <p>
9629  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9630  * from which the running page was served.<br><br>
9631  * <p>
9632  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9633  * <p>
9634  * Be aware that to enable the browser to parse an XML document, the server must set
9635  * the Content-Type header in the HTTP response to "text/xml".
9636  * @constructor
9637  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9638  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9639  * will be used to make the request.
9640  */
9641 Roo.data.HttpProxy = function(conn){
9642     Roo.data.HttpProxy.superclass.constructor.call(this);
9643     // is conn a conn config or a real conn?
9644     this.conn = conn;
9645     this.useAjax = !conn || !conn.events;
9646   
9647 };
9648
9649 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9650     // thse are take from connection...
9651     
9652     /**
9653      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9654      */
9655     /**
9656      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9657      * extra parameters to each request made by this object. (defaults to undefined)
9658      */
9659     /**
9660      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9661      *  to each request made by this object. (defaults to undefined)
9662      */
9663     /**
9664      * @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)
9665      */
9666     /**
9667      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9668      */
9669      /**
9670      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9671      * @type Boolean
9672      */
9673   
9674
9675     /**
9676      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9677      * @type Boolean
9678      */
9679     /**
9680      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9681      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9682      * a finer-grained basis than the DataProxy events.
9683      */
9684     getConnection : function(){
9685         return this.useAjax ? Roo.Ajax : this.conn;
9686     },
9687
9688     /**
9689      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9690      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9691      * process that block using the passed callback.
9692      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9693      * for the request to the remote server.
9694      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9695      * object into a block of Roo.data.Records.
9696      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9697      * The function must be passed <ul>
9698      * <li>The Record block object</li>
9699      * <li>The "arg" argument from the load function</li>
9700      * <li>A boolean success indicator</li>
9701      * </ul>
9702      * @param {Object} scope The scope in which to call the callback
9703      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9704      */
9705     load : function(params, reader, callback, scope, arg){
9706         if(this.fireEvent("beforeload", this, params) !== false){
9707             var  o = {
9708                 params : params || {},
9709                 request: {
9710                     callback : callback,
9711                     scope : scope,
9712                     arg : arg
9713                 },
9714                 reader: reader,
9715                 callback : this.loadResponse,
9716                 scope: this
9717             };
9718             if(this.useAjax){
9719                 Roo.applyIf(o, this.conn);
9720                 if(this.activeRequest){
9721                     Roo.Ajax.abort(this.activeRequest);
9722                 }
9723                 this.activeRequest = Roo.Ajax.request(o);
9724             }else{
9725                 this.conn.request(o);
9726             }
9727         }else{
9728             callback.call(scope||this, null, arg, false);
9729         }
9730     },
9731
9732     // private
9733     loadResponse : function(o, success, response){
9734         delete this.activeRequest;
9735         if(!success){
9736             this.fireEvent("loadexception", this, o, response);
9737             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9738             return;
9739         }
9740         var result;
9741         try {
9742             result = o.reader.read(response);
9743         }catch(e){
9744             this.fireEvent("loadexception", this, o, response, e);
9745             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9746             return;
9747         }
9748         
9749         this.fireEvent("load", this, o, o.request.arg);
9750         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9751     },
9752
9753     // private
9754     update : function(dataSet){
9755
9756     },
9757
9758     // private
9759     updateResponse : function(dataSet){
9760
9761     }
9762 });/*
9763  * Based on:
9764  * Ext JS Library 1.1.1
9765  * Copyright(c) 2006-2007, Ext JS, LLC.
9766  *
9767  * Originally Released Under LGPL - original licence link has changed is not relivant.
9768  *
9769  * Fork - LGPL
9770  * <script type="text/javascript">
9771  */
9772
9773 /**
9774  * @class Roo.data.ScriptTagProxy
9775  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9776  * other than the originating domain of the running page.<br><br>
9777  * <p>
9778  * <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
9779  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9780  * <p>
9781  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9782  * source code that is used as the source inside a &lt;script> tag.<br><br>
9783  * <p>
9784  * In order for the browser to process the returned data, the server must wrap the data object
9785  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9786  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9787  * depending on whether the callback name was passed:
9788  * <p>
9789  * <pre><code>
9790 boolean scriptTag = false;
9791 String cb = request.getParameter("callback");
9792 if (cb != null) {
9793     scriptTag = true;
9794     response.setContentType("text/javascript");
9795 } else {
9796     response.setContentType("application/x-json");
9797 }
9798 Writer out = response.getWriter();
9799 if (scriptTag) {
9800     out.write(cb + "(");
9801 }
9802 out.print(dataBlock.toJsonString());
9803 if (scriptTag) {
9804     out.write(");");
9805 }
9806 </pre></code>
9807  *
9808  * @constructor
9809  * @param {Object} config A configuration object.
9810  */
9811 Roo.data.ScriptTagProxy = function(config){
9812     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9813     Roo.apply(this, config);
9814     this.head = document.getElementsByTagName("head")[0];
9815 };
9816
9817 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9818
9819 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9820     /**
9821      * @cfg {String} url The URL from which to request the data object.
9822      */
9823     /**
9824      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9825      */
9826     timeout : 30000,
9827     /**
9828      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9829      * the server the name of the callback function set up by the load call to process the returned data object.
9830      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9831      * javascript output which calls this named function passing the data object as its only parameter.
9832      */
9833     callbackParam : "callback",
9834     /**
9835      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9836      * name to the request.
9837      */
9838     nocache : true,
9839
9840     /**
9841      * Load data from the configured URL, read the data object into
9842      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9843      * process that block using the passed callback.
9844      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9845      * for the request to the remote server.
9846      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9847      * object into a block of Roo.data.Records.
9848      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9849      * The function must be passed <ul>
9850      * <li>The Record block object</li>
9851      * <li>The "arg" argument from the load function</li>
9852      * <li>A boolean success indicator</li>
9853      * </ul>
9854      * @param {Object} scope The scope in which to call the callback
9855      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9856      */
9857     load : function(params, reader, callback, scope, arg){
9858         if(this.fireEvent("beforeload", this, params) !== false){
9859
9860             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9861
9862             var url = this.url;
9863             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9864             if(this.nocache){
9865                 url += "&_dc=" + (new Date().getTime());
9866             }
9867             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9868             var trans = {
9869                 id : transId,
9870                 cb : "stcCallback"+transId,
9871                 scriptId : "stcScript"+transId,
9872                 params : params,
9873                 arg : arg,
9874                 url : url,
9875                 callback : callback,
9876                 scope : scope,
9877                 reader : reader
9878             };
9879             var conn = this;
9880
9881             window[trans.cb] = function(o){
9882                 conn.handleResponse(o, trans);
9883             };
9884
9885             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9886
9887             if(this.autoAbort !== false){
9888                 this.abort();
9889             }
9890
9891             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9892
9893             var script = document.createElement("script");
9894             script.setAttribute("src", url);
9895             script.setAttribute("type", "text/javascript");
9896             script.setAttribute("id", trans.scriptId);
9897             this.head.appendChild(script);
9898
9899             this.trans = trans;
9900         }else{
9901             callback.call(scope||this, null, arg, false);
9902         }
9903     },
9904
9905     // private
9906     isLoading : function(){
9907         return this.trans ? true : false;
9908     },
9909
9910     /**
9911      * Abort the current server request.
9912      */
9913     abort : function(){
9914         if(this.isLoading()){
9915             this.destroyTrans(this.trans);
9916         }
9917     },
9918
9919     // private
9920     destroyTrans : function(trans, isLoaded){
9921         this.head.removeChild(document.getElementById(trans.scriptId));
9922         clearTimeout(trans.timeoutId);
9923         if(isLoaded){
9924             window[trans.cb] = undefined;
9925             try{
9926                 delete window[trans.cb];
9927             }catch(e){}
9928         }else{
9929             // if hasn't been loaded, wait for load to remove it to prevent script error
9930             window[trans.cb] = function(){
9931                 window[trans.cb] = undefined;
9932                 try{
9933                     delete window[trans.cb];
9934                 }catch(e){}
9935             };
9936         }
9937     },
9938
9939     // private
9940     handleResponse : function(o, trans){
9941         this.trans = false;
9942         this.destroyTrans(trans, true);
9943         var result;
9944         try {
9945             result = trans.reader.readRecords(o);
9946         }catch(e){
9947             this.fireEvent("loadexception", this, o, trans.arg, e);
9948             trans.callback.call(trans.scope||window, null, trans.arg, false);
9949             return;
9950         }
9951         this.fireEvent("load", this, o, trans.arg);
9952         trans.callback.call(trans.scope||window, result, trans.arg, true);
9953     },
9954
9955     // private
9956     handleFailure : function(trans){
9957         this.trans = false;
9958         this.destroyTrans(trans, false);
9959         this.fireEvent("loadexception", this, null, trans.arg);
9960         trans.callback.call(trans.scope||window, null, trans.arg, false);
9961     }
9962 });/*
9963  * Based on:
9964  * Ext JS Library 1.1.1
9965  * Copyright(c) 2006-2007, Ext JS, LLC.
9966  *
9967  * Originally Released Under LGPL - original licence link has changed is not relivant.
9968  *
9969  * Fork - LGPL
9970  * <script type="text/javascript">
9971  */
9972
9973 /**
9974  * @class Roo.data.JsonReader
9975  * @extends Roo.data.DataReader
9976  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9977  * based on mappings in a provided Roo.data.Record constructor.
9978  * 
9979  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9980  * in the reply previously. 
9981  * 
9982  * <p>
9983  * Example code:
9984  * <pre><code>
9985 var RecordDef = Roo.data.Record.create([
9986     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9987     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9988 ]);
9989 var myReader = new Roo.data.JsonReader({
9990     totalProperty: "results",    // The property which contains the total dataset size (optional)
9991     root: "rows",                // The property which contains an Array of row objects
9992     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9993 }, RecordDef);
9994 </code></pre>
9995  * <p>
9996  * This would consume a JSON file like this:
9997  * <pre><code>
9998 { 'results': 2, 'rows': [
9999     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10000     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10001 }
10002 </code></pre>
10003  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10004  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10005  * paged from the remote server.
10006  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10007  * @cfg {String} root name of the property which contains the Array of row objects.
10008  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10009  * @constructor
10010  * Create a new JsonReader
10011  * @param {Object} meta Metadata configuration options
10012  * @param {Object} recordType Either an Array of field definition objects,
10013  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10014  */
10015 Roo.data.JsonReader = function(meta, recordType){
10016     
10017     meta = meta || {};
10018     // set some defaults:
10019     Roo.applyIf(meta, {
10020         totalProperty: 'total',
10021         successProperty : 'success',
10022         root : 'data',
10023         id : 'id'
10024     });
10025     
10026     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10027 };
10028 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10029     
10030     /**
10031      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10032      * Used by Store query builder to append _requestMeta to params.
10033      * 
10034      */
10035     metaFromRemote : false,
10036     /**
10037      * This method is only used by a DataProxy which has retrieved data from a remote server.
10038      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10039      * @return {Object} data A data block which is used by an Roo.data.Store object as
10040      * a cache of Roo.data.Records.
10041      */
10042     read : function(response){
10043         var json = response.responseText;
10044        
10045         var o = /* eval:var:o */ eval("("+json+")");
10046         if(!o) {
10047             throw {message: "JsonReader.read: Json object not found"};
10048         }
10049         
10050         if(o.metaData){
10051             
10052             delete this.ef;
10053             this.metaFromRemote = true;
10054             this.meta = o.metaData;
10055             this.recordType = Roo.data.Record.create(o.metaData.fields);
10056             this.onMetaChange(this.meta, this.recordType, o);
10057         }
10058         return this.readRecords(o);
10059     },
10060
10061     // private function a store will implement
10062     onMetaChange : function(meta, recordType, o){
10063
10064     },
10065
10066     /**
10067          * @ignore
10068          */
10069     simpleAccess: function(obj, subsc) {
10070         return obj[subsc];
10071     },
10072
10073         /**
10074          * @ignore
10075          */
10076     getJsonAccessor: function(){
10077         var re = /[\[\.]/;
10078         return function(expr) {
10079             try {
10080                 return(re.test(expr))
10081                     ? new Function("obj", "return obj." + expr)
10082                     : function(obj){
10083                         return obj[expr];
10084                     };
10085             } catch(e){}
10086             return Roo.emptyFn;
10087         };
10088     }(),
10089
10090     /**
10091      * Create a data block containing Roo.data.Records from an XML document.
10092      * @param {Object} o An object which contains an Array of row objects in the property specified
10093      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10094      * which contains the total size of the dataset.
10095      * @return {Object} data A data block which is used by an Roo.data.Store object as
10096      * a cache of Roo.data.Records.
10097      */
10098     readRecords : function(o){
10099         /**
10100          * After any data loads, the raw JSON data is available for further custom processing.
10101          * @type Object
10102          */
10103         this.o = o;
10104         var s = this.meta, Record = this.recordType,
10105             f = Record.prototype.fields, fi = f.items, fl = f.length;
10106
10107 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10108         if (!this.ef) {
10109             if(s.totalProperty) {
10110                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10111                 }
10112                 if(s.successProperty) {
10113                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10114                 }
10115                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10116                 if (s.id) {
10117                         var g = this.getJsonAccessor(s.id);
10118                         this.getId = function(rec) {
10119                                 var r = g(rec);
10120                                 return (r === undefined || r === "") ? null : r;
10121                         };
10122                 } else {
10123                         this.getId = function(){return null;};
10124                 }
10125             this.ef = [];
10126             for(var jj = 0; jj < fl; jj++){
10127                 f = fi[jj];
10128                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10129                 this.ef[jj] = this.getJsonAccessor(map);
10130             }
10131         }
10132
10133         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10134         if(s.totalProperty){
10135             var vt = parseInt(this.getTotal(o), 10);
10136             if(!isNaN(vt)){
10137                 totalRecords = vt;
10138             }
10139         }
10140         if(s.successProperty){
10141             var vs = this.getSuccess(o);
10142             if(vs === false || vs === 'false'){
10143                 success = false;
10144             }
10145         }
10146         var records = [];
10147             for(var i = 0; i < c; i++){
10148                     var n = root[i];
10149                 var values = {};
10150                 var id = this.getId(n);
10151                 for(var j = 0; j < fl; j++){
10152                     f = fi[j];
10153                 var v = this.ef[j](n);
10154                 if (!f.convert) {
10155                     Roo.log('missing convert for ' + f.name);
10156                     Roo.log(f);
10157                     continue;
10158                 }
10159                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10160                 }
10161                 var record = new Record(values, id);
10162                 record.json = n;
10163                 records[i] = record;
10164             }
10165             return {
10166             raw : o,
10167                 success : success,
10168                 records : records,
10169                 totalRecords : totalRecords
10170             };
10171     }
10172 });/*
10173  * Based on:
10174  * Ext JS Library 1.1.1
10175  * Copyright(c) 2006-2007, Ext JS, LLC.
10176  *
10177  * Originally Released Under LGPL - original licence link has changed is not relivant.
10178  *
10179  * Fork - LGPL
10180  * <script type="text/javascript">
10181  */
10182
10183 /**
10184  * @class Roo.data.ArrayReader
10185  * @extends Roo.data.DataReader
10186  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10187  * Each element of that Array represents a row of data fields. The
10188  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10189  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10190  * <p>
10191  * Example code:.
10192  * <pre><code>
10193 var RecordDef = Roo.data.Record.create([
10194     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10195     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10196 ]);
10197 var myReader = new Roo.data.ArrayReader({
10198     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10199 }, RecordDef);
10200 </code></pre>
10201  * <p>
10202  * This would consume an Array like this:
10203  * <pre><code>
10204 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10205   </code></pre>
10206  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10207  * @constructor
10208  * Create a new JsonReader
10209  * @param {Object} meta Metadata configuration options.
10210  * @param {Object} recordType Either an Array of field definition objects
10211  * as specified to {@link Roo.data.Record#create},
10212  * or an {@link Roo.data.Record} object
10213  * created using {@link Roo.data.Record#create}.
10214  */
10215 Roo.data.ArrayReader = function(meta, recordType){
10216     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10217 };
10218
10219 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10220     /**
10221      * Create a data block containing Roo.data.Records from an XML document.
10222      * @param {Object} o An Array of row objects which represents the dataset.
10223      * @return {Object} data A data block which is used by an Roo.data.Store object as
10224      * a cache of Roo.data.Records.
10225      */
10226     readRecords : function(o){
10227         var sid = this.meta ? this.meta.id : null;
10228         var recordType = this.recordType, fields = recordType.prototype.fields;
10229         var records = [];
10230         var root = o;
10231             for(var i = 0; i < root.length; i++){
10232                     var n = root[i];
10233                 var values = {};
10234                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10235                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10236                 var f = fields.items[j];
10237                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10238                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10239                 v = f.convert(v);
10240                 values[f.name] = v;
10241             }
10242                 var record = new recordType(values, id);
10243                 record.json = n;
10244                 records[records.length] = record;
10245             }
10246             return {
10247                 records : records,
10248                 totalRecords : records.length
10249             };
10250     }
10251 });/*
10252  * - LGPL
10253  * * 
10254  */
10255
10256 /**
10257  * @class Roo.bootstrap.ComboBox
10258  * @extends Roo.bootstrap.TriggerField
10259  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10260  * @cfg {Boolean} append (true|false) default false
10261  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10262  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10263  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10264  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10265  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10266  * @constructor
10267  * Create a new ComboBox.
10268  * @param {Object} config Configuration options
10269  */
10270 Roo.bootstrap.ComboBox = function(config){
10271     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10272     this.addEvents({
10273         /**
10274          * @event expand
10275          * Fires when the dropdown list is expanded
10276              * @param {Roo.bootstrap.ComboBox} combo This combo box
10277              */
10278         'expand' : true,
10279         /**
10280          * @event collapse
10281          * Fires when the dropdown list is collapsed
10282              * @param {Roo.bootstrap.ComboBox} combo This combo box
10283              */
10284         'collapse' : true,
10285         /**
10286          * @event beforeselect
10287          * Fires before a list item is selected. Return false to cancel the selection.
10288              * @param {Roo.bootstrap.ComboBox} combo This combo box
10289              * @param {Roo.data.Record} record The data record returned from the underlying store
10290              * @param {Number} index The index of the selected item in the dropdown list
10291              */
10292         'beforeselect' : true,
10293         /**
10294          * @event select
10295          * Fires when a list item is selected
10296              * @param {Roo.bootstrap.ComboBox} combo This combo box
10297              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10298              * @param {Number} index The index of the selected item in the dropdown list
10299              */
10300         'select' : true,
10301         /**
10302          * @event beforequery
10303          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10304          * The event object passed has these properties:
10305              * @param {Roo.bootstrap.ComboBox} combo This combo box
10306              * @param {String} query The query
10307              * @param {Boolean} forceAll true to force "all" query
10308              * @param {Boolean} cancel true to cancel the query
10309              * @param {Object} e The query event object
10310              */
10311         'beforequery': true,
10312          /**
10313          * @event add
10314          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10315              * @param {Roo.bootstrap.ComboBox} combo This combo box
10316              */
10317         'add' : true,
10318         /**
10319          * @event edit
10320          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10321              * @param {Roo.bootstrap.ComboBox} combo This combo box
10322              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10323              */
10324         'edit' : true,
10325         /**
10326          * @event remove
10327          * Fires when the remove value from the combobox array
10328              * @param {Roo.bootstrap.ComboBox} combo This combo box
10329              */
10330         'remove' : true
10331         
10332     });
10333     
10334     this.item = [];
10335     this.tickItems = [];
10336     
10337     this.selectedIndex = -1;
10338     if(this.mode == 'local'){
10339         if(config.queryDelay === undefined){
10340             this.queryDelay = 10;
10341         }
10342         if(config.minChars === undefined){
10343             this.minChars = 0;
10344         }
10345     }
10346 };
10347
10348 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10349      
10350     /**
10351      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10352      * rendering into an Roo.Editor, defaults to false)
10353      */
10354     /**
10355      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10356      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10357      */
10358     /**
10359      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10360      */
10361     /**
10362      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10363      * the dropdown list (defaults to undefined, with no header element)
10364      */
10365
10366      /**
10367      * @cfg {String/Roo.Template} tpl The template to use to render the output
10368      */
10369      
10370      /**
10371      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10372      */
10373     listWidth: undefined,
10374     /**
10375      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10376      * mode = 'remote' or 'text' if mode = 'local')
10377      */
10378     displayField: undefined,
10379     /**
10380      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10381      * mode = 'remote' or 'value' if mode = 'local'). 
10382      * Note: use of a valueField requires the user make a selection
10383      * in order for a value to be mapped.
10384      */
10385     valueField: undefined,
10386     
10387     
10388     /**
10389      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10390      * field's data value (defaults to the underlying DOM element's name)
10391      */
10392     hiddenName: undefined,
10393     /**
10394      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10395      */
10396     listClass: '',
10397     /**
10398      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10399      */
10400     selectedClass: 'active',
10401     
10402     /**
10403      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10404      */
10405     shadow:'sides',
10406     /**
10407      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10408      * anchor positions (defaults to 'tl-bl')
10409      */
10410     listAlign: 'tl-bl?',
10411     /**
10412      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10413      */
10414     maxHeight: 300,
10415     /**
10416      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10417      * query specified by the allQuery config option (defaults to 'query')
10418      */
10419     triggerAction: 'query',
10420     /**
10421      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10422      * (defaults to 4, does not apply if editable = false)
10423      */
10424     minChars : 4,
10425     /**
10426      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10427      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10428      */
10429     typeAhead: false,
10430     /**
10431      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10432      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10433      */
10434     queryDelay: 500,
10435     /**
10436      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10437      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10438      */
10439     pageSize: 0,
10440     /**
10441      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10442      * when editable = true (defaults to false)
10443      */
10444     selectOnFocus:false,
10445     /**
10446      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10447      */
10448     queryParam: 'query',
10449     /**
10450      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10451      * when mode = 'remote' (defaults to 'Loading...')
10452      */
10453     loadingText: 'Loading...',
10454     /**
10455      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10456      */
10457     resizable: false,
10458     /**
10459      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10460      */
10461     handleHeight : 8,
10462     /**
10463      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10464      * traditional select (defaults to true)
10465      */
10466     editable: true,
10467     /**
10468      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10469      */
10470     allQuery: '',
10471     /**
10472      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10473      */
10474     mode: 'remote',
10475     /**
10476      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10477      * listWidth has a higher value)
10478      */
10479     minListWidth : 70,
10480     /**
10481      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10482      * allow the user to set arbitrary text into the field (defaults to false)
10483      */
10484     forceSelection:false,
10485     /**
10486      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10487      * if typeAhead = true (defaults to 250)
10488      */
10489     typeAheadDelay : 250,
10490     /**
10491      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10492      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10493      */
10494     valueNotFoundText : undefined,
10495     /**
10496      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10497      */
10498     blockFocus : false,
10499     
10500     /**
10501      * @cfg {Boolean} disableClear Disable showing of clear button.
10502      */
10503     disableClear : false,
10504     /**
10505      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10506      */
10507     alwaysQuery : false,
10508     
10509     /**
10510      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10511      */
10512     multiple : false,
10513     
10514     //private
10515     addicon : false,
10516     editicon: false,
10517     
10518     page: 0,
10519     hasQuery: false,
10520     append: false,
10521     loadNext: false,
10522     autoFocus : true,
10523     tickable : false,
10524     btnPosition : 'right',
10525     triggerList : true,
10526     showToggleBtn : true,
10527     // element that contains real text value.. (when hidden is used..)
10528     
10529     getAutoCreate : function()
10530     {
10531         var cfg = false;
10532         
10533         /*
10534          *  Normal ComboBox
10535          */
10536         if(!this.tickable){
10537             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10538             return cfg;
10539         }
10540         
10541         /*
10542          *  ComboBox with tickable selections
10543          */
10544              
10545         var align = this.labelAlign || this.parentLabelAlign();
10546         
10547         cfg = {
10548             cls : 'form-group roo-combobox-tickable' //input-group
10549         };
10550         
10551         
10552         var buttons = {
10553             tag : 'div',
10554             cls : 'tickable-buttons',
10555             cn : [
10556                 {
10557                     tag : 'button',
10558                     type : 'button',
10559                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10560                     html : 'Edit'
10561                 },
10562                 {
10563                     tag : 'button',
10564                     type : 'button',
10565                     name : 'ok',
10566                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10567                     html : 'Done'
10568                 },
10569                 {
10570                     tag : 'button',
10571                     type : 'button',
10572                     name : 'cancel',
10573                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10574                     html : 'Cancel'
10575                 }
10576             ]
10577         };
10578         
10579         var _this = this;
10580         Roo.each(buttons.cn, function(c){
10581             if (_this.size) {
10582                 c.cls += ' btn-' + _this.size;
10583             }
10584
10585             if (_this.disabled) {
10586                 c.disabled = true;
10587             }
10588         });
10589         
10590         var box = {
10591             tag: 'div',
10592             cn: [
10593                 {
10594                     tag: 'input',
10595                     type : 'hidden',
10596                     cls: 'form-hidden-field'
10597                 },
10598                 {
10599                     tag: 'ul',
10600                     cls: 'select2-choices',
10601                     cn:[
10602                         {
10603                             tag: 'li',
10604                             cls: 'select2-search-field',
10605                             cn: [
10606
10607                                 buttons
10608                             ]
10609                         }
10610                     ]
10611                 }
10612             ]
10613         }
10614         
10615         var combobox = {
10616             cls: 'select2-container input-group select2-container-multi',
10617             cn: [
10618                 box
10619 //                {
10620 //                    tag: 'ul',
10621 //                    cls: 'typeahead typeahead-long dropdown-menu',
10622 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10623 //                }
10624             ]
10625         };
10626         
10627         if (align ==='left' && this.fieldLabel.length) {
10628             
10629                 Roo.log("left and has label");
10630                 cfg.cn = [
10631                     
10632                     {
10633                         tag: 'label',
10634                         'for' :  id,
10635                         cls : 'control-label col-sm-' + this.labelWidth,
10636                         html : this.fieldLabel
10637                         
10638                     },
10639                     {
10640                         cls : "col-sm-" + (12 - this.labelWidth), 
10641                         cn: [
10642                             combobox
10643                         ]
10644                     }
10645                     
10646                 ];
10647         } else if ( this.fieldLabel.length) {
10648                 Roo.log(" label");
10649                  cfg.cn = [
10650                    
10651                     {
10652                         tag: 'label',
10653                         //cls : 'input-group-addon',
10654                         html : this.fieldLabel
10655                         
10656                     },
10657                     
10658                     combobox
10659                     
10660                 ];
10661
10662         } else {
10663             
10664                 Roo.log(" no label && no align");
10665                 cfg = combobox
10666                      
10667                 
10668         }
10669          
10670         var settings=this;
10671         ['xs','sm','md','lg'].map(function(size){
10672             if (settings[size]) {
10673                 cfg.cls += ' col-' + size + '-' + settings[size];
10674             }
10675         });
10676         
10677         return cfg;
10678         
10679     },
10680     
10681     // private
10682     initEvents: function()
10683     {
10684         
10685         if (!this.store) {
10686             throw "can not find store for combo";
10687         }
10688         this.store = Roo.factory(this.store, Roo.data);
10689         
10690         if(this.tickable){
10691             this.initTickableEvents();
10692             return;
10693         }
10694         
10695         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10696         
10697         if(this.hiddenName){
10698             
10699             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10700             
10701             this.hiddenField.dom.value =
10702                 this.hiddenValue !== undefined ? this.hiddenValue :
10703                 this.value !== undefined ? this.value : '';
10704
10705             // prevent input submission
10706             this.el.dom.removeAttribute('name');
10707             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10708              
10709              
10710         }
10711         //if(Roo.isGecko){
10712         //    this.el.dom.setAttribute('autocomplete', 'off');
10713         //}
10714         
10715         var cls = 'x-combo-list';
10716         
10717         //this.list = new Roo.Layer({
10718         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10719         //});
10720         
10721         var _this = this;
10722         
10723         (function(){
10724             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10725             _this.list.setWidth(lw);
10726         }).defer(100);
10727         
10728         this.list.on('mouseover', this.onViewOver, this);
10729         this.list.on('mousemove', this.onViewMove, this);
10730         
10731         this.list.on('scroll', this.onViewScroll, this);
10732         
10733         /*
10734         this.list.swallowEvent('mousewheel');
10735         this.assetHeight = 0;
10736
10737         if(this.title){
10738             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10739             this.assetHeight += this.header.getHeight();
10740         }
10741
10742         this.innerList = this.list.createChild({cls:cls+'-inner'});
10743         this.innerList.on('mouseover', this.onViewOver, this);
10744         this.innerList.on('mousemove', this.onViewMove, this);
10745         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10746         
10747         if(this.allowBlank && !this.pageSize && !this.disableClear){
10748             this.footer = this.list.createChild({cls:cls+'-ft'});
10749             this.pageTb = new Roo.Toolbar(this.footer);
10750            
10751         }
10752         if(this.pageSize){
10753             this.footer = this.list.createChild({cls:cls+'-ft'});
10754             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10755                     {pageSize: this.pageSize});
10756             
10757         }
10758         
10759         if (this.pageTb && this.allowBlank && !this.disableClear) {
10760             var _this = this;
10761             this.pageTb.add(new Roo.Toolbar.Fill(), {
10762                 cls: 'x-btn-icon x-btn-clear',
10763                 text: '&#160;',
10764                 handler: function()
10765                 {
10766                     _this.collapse();
10767                     _this.clearValue();
10768                     _this.onSelect(false, -1);
10769                 }
10770             });
10771         }
10772         if (this.footer) {
10773             this.assetHeight += this.footer.getHeight();
10774         }
10775         */
10776             
10777         if(!this.tpl){
10778             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10779         }
10780
10781         this.view = new Roo.View(this.list, this.tpl, {
10782             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10783         });
10784         //this.view.wrapEl.setDisplayed(false);
10785         this.view.on('click', this.onViewClick, this);
10786         
10787         
10788         
10789         this.store.on('beforeload', this.onBeforeLoad, this);
10790         this.store.on('load', this.onLoad, this);
10791         this.store.on('loadexception', this.onLoadException, this);
10792         /*
10793         if(this.resizable){
10794             this.resizer = new Roo.Resizable(this.list,  {
10795                pinned:true, handles:'se'
10796             });
10797             this.resizer.on('resize', function(r, w, h){
10798                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10799                 this.listWidth = w;
10800                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10801                 this.restrictHeight();
10802             }, this);
10803             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10804         }
10805         */
10806         if(!this.editable){
10807             this.editable = true;
10808             this.setEditable(false);
10809         }
10810         
10811         /*
10812         
10813         if (typeof(this.events.add.listeners) != 'undefined') {
10814             
10815             this.addicon = this.wrap.createChild(
10816                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10817        
10818             this.addicon.on('click', function(e) {
10819                 this.fireEvent('add', this);
10820             }, this);
10821         }
10822         if (typeof(this.events.edit.listeners) != 'undefined') {
10823             
10824             this.editicon = this.wrap.createChild(
10825                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10826             if (this.addicon) {
10827                 this.editicon.setStyle('margin-left', '40px');
10828             }
10829             this.editicon.on('click', function(e) {
10830                 
10831                 // we fire even  if inothing is selected..
10832                 this.fireEvent('edit', this, this.lastData );
10833                 
10834             }, this);
10835         }
10836         */
10837         
10838         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10839             "up" : function(e){
10840                 this.inKeyMode = true;
10841                 this.selectPrev();
10842             },
10843
10844             "down" : function(e){
10845                 if(!this.isExpanded()){
10846                     this.onTriggerClick();
10847                 }else{
10848                     this.inKeyMode = true;
10849                     this.selectNext();
10850                 }
10851             },
10852
10853             "enter" : function(e){
10854 //                this.onViewClick();
10855                 //return true;
10856                 this.collapse();
10857                 
10858                 if(this.fireEvent("specialkey", this, e)){
10859                     this.onViewClick(false);
10860                 }
10861                 
10862                 return true;
10863             },
10864
10865             "esc" : function(e){
10866                 this.collapse();
10867             },
10868
10869             "tab" : function(e){
10870                 this.collapse();
10871                 
10872                 if(this.fireEvent("specialkey", this, e)){
10873                     this.onViewClick(false);
10874                 }
10875                 
10876                 return true;
10877             },
10878
10879             scope : this,
10880
10881             doRelay : function(foo, bar, hname){
10882                 if(hname == 'down' || this.scope.isExpanded()){
10883                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10884                 }
10885                 return true;
10886             },
10887
10888             forceKeyDown: true
10889         });
10890         
10891         
10892         this.queryDelay = Math.max(this.queryDelay || 10,
10893                 this.mode == 'local' ? 10 : 250);
10894         
10895         
10896         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10897         
10898         if(this.typeAhead){
10899             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10900         }
10901         if(this.editable !== false){
10902             this.inputEl().on("keyup", this.onKeyUp, this);
10903         }
10904         if(this.forceSelection){
10905             this.inputEl().on('blur', this.doForce, this);
10906         }
10907         
10908         if(this.multiple){
10909             this.choices = this.el.select('ul.select2-choices', true).first();
10910             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10911         }
10912     },
10913     
10914     initTickableEvents: function()
10915     {   
10916         this.createList();
10917         
10918         if(this.hiddenName){
10919             
10920             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10921             
10922             this.hiddenField.dom.value =
10923                 this.hiddenValue !== undefined ? this.hiddenValue :
10924                 this.value !== undefined ? this.value : '';
10925
10926             // prevent input submission
10927             this.el.dom.removeAttribute('name');
10928             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10929              
10930              
10931         }
10932         
10933 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10934         
10935         this.choices = this.el.select('ul.select2-choices', true).first();
10936         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10937         if(this.triggerList){
10938             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10939         }
10940          
10941         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10942         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10943         
10944         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10945         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10946         
10947         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10948         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10949         
10950         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10951         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10952         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10953         
10954         this.okBtn.hide();
10955         this.cancelBtn.hide();
10956         
10957         var _this = this;
10958         
10959         (function(){
10960             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10961             _this.list.setWidth(lw);
10962         }).defer(100);
10963         
10964         this.list.on('mouseover', this.onViewOver, this);
10965         this.list.on('mousemove', this.onViewMove, this);
10966         
10967         this.list.on('scroll', this.onViewScroll, this);
10968         
10969         if(!this.tpl){
10970             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>';
10971         }
10972
10973         this.view = new Roo.View(this.list, this.tpl, {
10974             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10975         });
10976         
10977         //this.view.wrapEl.setDisplayed(false);
10978         this.view.on('click', this.onViewClick, this);
10979         
10980         
10981         
10982         this.store.on('beforeload', this.onBeforeLoad, this);
10983         this.store.on('load', this.onLoad, this);
10984         this.store.on('loadexception', this.onLoadException, this);
10985         
10986 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10987 //            "up" : function(e){
10988 //                this.inKeyMode = true;
10989 //                this.selectPrev();
10990 //            },
10991 //
10992 //            "down" : function(e){
10993 //                if(!this.isExpanded()){
10994 //                    this.onTriggerClick();
10995 //                }else{
10996 //                    this.inKeyMode = true;
10997 //                    this.selectNext();
10998 //                }
10999 //            },
11000 //
11001 //            "enter" : function(e){
11002 ////                this.onViewClick();
11003 //                //return true;
11004 //                this.collapse();
11005 //                
11006 //                if(this.fireEvent("specialkey", this, e)){
11007 //                    this.onViewClick(false);
11008 //                }
11009 //                
11010 //                return true;
11011 //            },
11012 //
11013 //            "esc" : function(e){
11014 //                this.collapse();
11015 //            },
11016 //
11017 //            "tab" : function(e){
11018 //                this.collapse();
11019 //                
11020 //                if(this.fireEvent("specialkey", this, e)){
11021 //                    this.onViewClick(false);
11022 //                }
11023 //                
11024 //                return true;
11025 //            },
11026 //
11027 //            scope : this,
11028 //
11029 //            doRelay : function(foo, bar, hname){
11030 //                if(hname == 'down' || this.scope.isExpanded()){
11031 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11032 //                }
11033 //                return true;
11034 //            },
11035 //
11036 //            forceKeyDown: true
11037 //        });
11038         
11039         
11040         this.queryDelay = Math.max(this.queryDelay || 10,
11041                 this.mode == 'local' ? 10 : 250);
11042         
11043         
11044         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11045         
11046         if(this.typeAhead){
11047             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11048         }
11049     },
11050
11051     onDestroy : function(){
11052         if(this.view){
11053             this.view.setStore(null);
11054             this.view.el.removeAllListeners();
11055             this.view.el.remove();
11056             this.view.purgeListeners();
11057         }
11058         if(this.list){
11059             this.list.dom.innerHTML  = '';
11060         }
11061         
11062         if(this.store){
11063             this.store.un('beforeload', this.onBeforeLoad, this);
11064             this.store.un('load', this.onLoad, this);
11065             this.store.un('loadexception', this.onLoadException, this);
11066         }
11067         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11068     },
11069
11070     // private
11071     fireKey : function(e){
11072         if(e.isNavKeyPress() && !this.list.isVisible()){
11073             this.fireEvent("specialkey", this, e);
11074         }
11075     },
11076
11077     // private
11078     onResize: function(w, h){
11079 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11080 //        
11081 //        if(typeof w != 'number'){
11082 //            // we do not handle it!?!?
11083 //            return;
11084 //        }
11085 //        var tw = this.trigger.getWidth();
11086 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11087 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11088 //        var x = w - tw;
11089 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11090 //            
11091 //        //this.trigger.setStyle('left', x+'px');
11092 //        
11093 //        if(this.list && this.listWidth === undefined){
11094 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11095 //            this.list.setWidth(lw);
11096 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11097 //        }
11098         
11099     
11100         
11101     },
11102
11103     /**
11104      * Allow or prevent the user from directly editing the field text.  If false is passed,
11105      * the user will only be able to select from the items defined in the dropdown list.  This method
11106      * is the runtime equivalent of setting the 'editable' config option at config time.
11107      * @param {Boolean} value True to allow the user to directly edit the field text
11108      */
11109     setEditable : function(value){
11110         if(value == this.editable){
11111             return;
11112         }
11113         this.editable = value;
11114         if(!value){
11115             this.inputEl().dom.setAttribute('readOnly', true);
11116             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11117             this.inputEl().addClass('x-combo-noedit');
11118         }else{
11119             this.inputEl().dom.setAttribute('readOnly', false);
11120             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11121             this.inputEl().removeClass('x-combo-noedit');
11122         }
11123     },
11124
11125     // private
11126     
11127     onBeforeLoad : function(combo,opts){
11128         if(!this.hasFocus){
11129             return;
11130         }
11131          if (!opts.add) {
11132             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11133          }
11134         this.restrictHeight();
11135         this.selectedIndex = -1;
11136     },
11137
11138     // private
11139     onLoad : function(){
11140         
11141         this.hasQuery = false;
11142         
11143         if(!this.hasFocus){
11144             return;
11145         }
11146         
11147         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11148             this.loading.hide();
11149         }
11150         
11151         if(this.store.getCount() > 0){
11152             this.expand();
11153 //            this.restrictHeight();
11154             if(this.lastQuery == this.allQuery){
11155                 if(this.editable && !this.tickable){
11156                     this.inputEl().dom.select();
11157                 }
11158                 
11159                 if(
11160                     !this.selectByValue(this.value, true) &&
11161                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11162                     this.store.lastOptions.add != true)
11163                 ){
11164                     this.select(0, true);
11165                 }
11166             }else{
11167                 if(this.autoFocus){
11168                     this.selectNext();
11169                 }
11170                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11171                     this.taTask.delay(this.typeAheadDelay);
11172                 }
11173             }
11174         }else{
11175             this.onEmptyResults();
11176         }
11177         
11178         //this.el.focus();
11179     },
11180     // private
11181     onLoadException : function()
11182     {
11183         this.hasQuery = false;
11184         
11185         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11186             this.loading.hide();
11187         }
11188         
11189         this.collapse();
11190         Roo.log(this.store.reader.jsonData);
11191         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11192             // fixme
11193             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11194         }
11195         
11196         
11197     },
11198     // private
11199     onTypeAhead : function(){
11200         if(this.store.getCount() > 0){
11201             var r = this.store.getAt(0);
11202             var newValue = r.data[this.displayField];
11203             var len = newValue.length;
11204             var selStart = this.getRawValue().length;
11205             
11206             if(selStart != len){
11207                 this.setRawValue(newValue);
11208                 this.selectText(selStart, newValue.length);
11209             }
11210         }
11211     },
11212
11213     // private
11214     onSelect : function(record, index){
11215         
11216         if(this.fireEvent('beforeselect', this, record, index) !== false){
11217         
11218             this.setFromData(index > -1 ? record.data : false);
11219             
11220             this.collapse();
11221             this.fireEvent('select', this, record, index);
11222         }
11223     },
11224
11225     /**
11226      * Returns the currently selected field value or empty string if no value is set.
11227      * @return {String} value The selected value
11228      */
11229     getValue : function(){
11230         
11231         if(this.multiple){
11232             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11233         }
11234         
11235         if(this.valueField){
11236             return typeof this.value != 'undefined' ? this.value : '';
11237         }else{
11238             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11239         }
11240     },
11241
11242     /**
11243      * Clears any text/value currently set in the field
11244      */
11245     clearValue : function(){
11246         if(this.hiddenField){
11247             this.hiddenField.dom.value = '';
11248         }
11249         this.value = '';
11250         this.setRawValue('');
11251         this.lastSelectionText = '';
11252         
11253     },
11254
11255     /**
11256      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11257      * will be displayed in the field.  If the value does not match the data value of an existing item,
11258      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11259      * Otherwise the field will be blank (although the value will still be set).
11260      * @param {String} value The value to match
11261      */
11262     setValue : function(v){
11263         if(this.multiple){
11264             this.syncValue();
11265             return;
11266         }
11267         
11268         var text = v;
11269         if(this.valueField){
11270             var r = this.findRecord(this.valueField, v);
11271             if(r){
11272                 text = r.data[this.displayField];
11273             }else if(this.valueNotFoundText !== undefined){
11274                 text = this.valueNotFoundText;
11275             }
11276         }
11277         this.lastSelectionText = text;
11278         if(this.hiddenField){
11279             this.hiddenField.dom.value = v;
11280         }
11281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11282         this.value = v;
11283     },
11284     /**
11285      * @property {Object} the last set data for the element
11286      */
11287     
11288     lastData : false,
11289     /**
11290      * Sets the value of the field based on a object which is related to the record format for the store.
11291      * @param {Object} value the value to set as. or false on reset?
11292      */
11293     setFromData : function(o){
11294         
11295         if(this.multiple){
11296             if(typeof o.display_name !== 'string'){
11297                 for(var i=0;i<o.display_name.length;i++){
11298                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11299                 }
11300                 return;
11301             }
11302             this.addItem(o);
11303             return;
11304         }
11305             
11306         var dv = ''; // display value
11307         var vv = ''; // value value..
11308         this.lastData = o;
11309         if (this.displayField) {
11310             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11311         } else {
11312             // this is an error condition!!!
11313             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11314         }
11315         
11316         if(this.valueField){
11317             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11318         }
11319         
11320         if(this.hiddenField){
11321             this.hiddenField.dom.value = vv;
11322             
11323             this.lastSelectionText = dv;
11324             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11325             this.value = vv;
11326             return;
11327         }
11328         // no hidden field.. - we store the value in 'value', but still display
11329         // display field!!!!
11330         this.lastSelectionText = dv;
11331         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11332         this.value = vv;
11333         
11334         
11335     },
11336     // private
11337     reset : function(){
11338         // overridden so that last data is reset..
11339         this.setValue(this.originalValue);
11340         this.clearInvalid();
11341         this.lastData = false;
11342         if (this.view) {
11343             this.view.clearSelections();
11344         }
11345     },
11346     // private
11347     findRecord : function(prop, value){
11348         var record;
11349         if(this.store.getCount() > 0){
11350             this.store.each(function(r){
11351                 if(r.data[prop] == value){
11352                     record = r;
11353                     return false;
11354                 }
11355                 return true;
11356             });
11357         }
11358         return record;
11359     },
11360     
11361     getName: function()
11362     {
11363         // returns hidden if it's set..
11364         if (!this.rendered) {return ''};
11365         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11366         
11367     },
11368     // private
11369     onViewMove : function(e, t){
11370         this.inKeyMode = false;
11371     },
11372
11373     // private
11374     onViewOver : function(e, t){
11375         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11376             return;
11377         }
11378         var item = this.view.findItemFromChild(t);
11379         
11380         if(item){
11381             var index = this.view.indexOf(item);
11382             this.select(index, false);
11383         }
11384     },
11385
11386     // private
11387     onViewClick : function(view, doFocus, el, e)
11388     {
11389         var index = this.view.getSelectedIndexes()[0];
11390         
11391         var r = this.store.getAt(index);
11392         
11393         if(this.tickable){
11394             
11395             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11396                 return;
11397             }
11398             
11399             var rm = false;
11400             var _this = this;
11401             
11402             Roo.each(this.tickItems, function(v,k){
11403                 
11404                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11405                     _this.tickItems.splice(k, 1);
11406                     rm = true;
11407                     return;
11408                 }
11409             })
11410             
11411             if(rm){
11412                 return;
11413             }
11414             
11415             this.tickItems.push(r.data);
11416             return;
11417         }
11418         
11419         if(r){
11420             this.onSelect(r, index);
11421         }
11422         if(doFocus !== false && !this.blockFocus){
11423             this.inputEl().focus();
11424         }
11425     },
11426
11427     // private
11428     restrictHeight : function(){
11429         //this.innerList.dom.style.height = '';
11430         //var inner = this.innerList.dom;
11431         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11432         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11433         //this.list.beginUpdate();
11434         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11435         this.list.alignTo(this.inputEl(), this.listAlign);
11436         this.list.alignTo(this.inputEl(), this.listAlign);
11437         //this.list.endUpdate();
11438     },
11439
11440     // private
11441     onEmptyResults : function(){
11442         this.collapse();
11443     },
11444
11445     /**
11446      * Returns true if the dropdown list is expanded, else false.
11447      */
11448     isExpanded : function(){
11449         return this.list.isVisible();
11450     },
11451
11452     /**
11453      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11454      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11455      * @param {String} value The data value of the item to select
11456      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11457      * selected item if it is not currently in view (defaults to true)
11458      * @return {Boolean} True if the value matched an item in the list, else false
11459      */
11460     selectByValue : function(v, scrollIntoView){
11461         if(v !== undefined && v !== null){
11462             var r = this.findRecord(this.valueField || this.displayField, v);
11463             if(r){
11464                 this.select(this.store.indexOf(r), scrollIntoView);
11465                 return true;
11466             }
11467         }
11468         return false;
11469     },
11470
11471     /**
11472      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11473      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11474      * @param {Number} index The zero-based index of the list item to select
11475      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11476      * selected item if it is not currently in view (defaults to true)
11477      */
11478     select : function(index, scrollIntoView){
11479         this.selectedIndex = index;
11480         this.view.select(index);
11481         if(scrollIntoView !== false){
11482             var el = this.view.getNode(index);
11483             if(el && !this.multiple && !this.tickable){
11484                 this.list.scrollChildIntoView(el, false);
11485             }
11486         }
11487     },
11488
11489     // private
11490     selectNext : function(){
11491         var ct = this.store.getCount();
11492         if(ct > 0){
11493             if(this.selectedIndex == -1){
11494                 this.select(0);
11495             }else if(this.selectedIndex < ct-1){
11496                 this.select(this.selectedIndex+1);
11497             }
11498         }
11499     },
11500
11501     // private
11502     selectPrev : 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 != 0){
11508                 this.select(this.selectedIndex-1);
11509             }
11510         }
11511     },
11512
11513     // private
11514     onKeyUp : function(e){
11515         if(this.editable !== false && !e.isSpecialKey()){
11516             this.lastKey = e.getKey();
11517             this.dqTask.delay(this.queryDelay);
11518         }
11519     },
11520
11521     // private
11522     validateBlur : function(){
11523         return !this.list || !this.list.isVisible();   
11524     },
11525
11526     // private
11527     initQuery : function(){
11528         this.doQuery(this.getRawValue());
11529     },
11530
11531     // private
11532     doForce : function(){
11533         if(this.inputEl().dom.value.length > 0){
11534             this.inputEl().dom.value =
11535                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11536              
11537         }
11538     },
11539
11540     /**
11541      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11542      * query allowing the query action to be canceled if needed.
11543      * @param {String} query The SQL query to execute
11544      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11545      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11546      * saved in the current store (defaults to false)
11547      */
11548     doQuery : function(q, forceAll){
11549         
11550         if(q === undefined || q === null){
11551             q = '';
11552         }
11553         var qe = {
11554             query: q,
11555             forceAll: forceAll,
11556             combo: this,
11557             cancel:false
11558         };
11559         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11560             return false;
11561         }
11562         q = qe.query;
11563         
11564         forceAll = qe.forceAll;
11565         if(forceAll === true || (q.length >= this.minChars)){
11566             
11567             this.hasQuery = true;
11568             
11569             if(this.lastQuery != q || this.alwaysQuery){
11570                 this.lastQuery = q;
11571                 if(this.mode == 'local'){
11572                     this.selectedIndex = -1;
11573                     if(forceAll){
11574                         this.store.clearFilter();
11575                     }else{
11576                         this.store.filter(this.displayField, q);
11577                     }
11578                     this.onLoad();
11579                 }else{
11580                     this.store.baseParams[this.queryParam] = q;
11581                     
11582                     var options = {params : this.getParams(q)};
11583                     
11584                     if(this.loadNext){
11585                         options.add = true;
11586                         options.params.start = this.page * this.pageSize;
11587                     }
11588                     
11589                     this.store.load(options);
11590                     /*
11591                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11592                      *  we should expand the list on onLoad
11593                      *  so command out it
11594                      */
11595 //                    this.expand();
11596                 }
11597             }else{
11598                 this.selectedIndex = -1;
11599                 this.onLoad();   
11600             }
11601         }
11602         
11603         this.loadNext = false;
11604     },
11605
11606     // private
11607     getParams : function(q){
11608         var p = {};
11609         //p[this.queryParam] = q;
11610         
11611         if(this.pageSize){
11612             p.start = 0;
11613             p.limit = this.pageSize;
11614         }
11615         return p;
11616     },
11617
11618     /**
11619      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11620      */
11621     collapse : function(){
11622         if(!this.isExpanded()){
11623             return;
11624         }
11625         
11626         this.list.hide();
11627         
11628         if(this.tickable){
11629             this.okBtn.hide();
11630             this.cancelBtn.hide();
11631             this.trigger.show();
11632         }
11633         
11634         Roo.get(document).un('mousedown', this.collapseIf, this);
11635         Roo.get(document).un('mousewheel', this.collapseIf, this);
11636         if (!this.editable) {
11637             Roo.get(document).un('keydown', this.listKeyPress, this);
11638         }
11639         this.fireEvent('collapse', this);
11640     },
11641
11642     // private
11643     collapseIf : function(e){
11644         var in_combo  = e.within(this.el);
11645         var in_list =  e.within(this.list);
11646         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11647         
11648         if (in_combo || in_list || is_list) {
11649             //e.stopPropagation();
11650             return;
11651         }
11652         
11653         if(this.tickable){
11654             this.onTickableFooterButtonClick(e, false, false);
11655         }
11656
11657         this.collapse();
11658         
11659     },
11660
11661     /**
11662      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11663      */
11664     expand : function(){
11665        
11666         if(this.isExpanded() || !this.hasFocus){
11667             return;
11668         }
11669         
11670         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11671         this.list.setWidth(lw);
11672         
11673         
11674          Roo.log('expand');
11675         
11676         this.list.show();
11677         
11678         this.restrictHeight();
11679         
11680         if(this.tickable){
11681             
11682             this.tickItems = Roo.apply([], this.item);
11683             
11684             this.okBtn.show();
11685             this.cancelBtn.show();
11686             this.trigger.hide();
11687             
11688         }
11689         
11690         Roo.get(document).on('mousedown', this.collapseIf, this);
11691         Roo.get(document).on('mousewheel', this.collapseIf, this);
11692         if (!this.editable) {
11693             Roo.get(document).on('keydown', this.listKeyPress, this);
11694         }
11695         
11696         this.fireEvent('expand', this);
11697     },
11698
11699     // private
11700     // Implements the default empty TriggerField.onTriggerClick function
11701     onTriggerClick : function(e)
11702     {
11703         Roo.log('trigger click');
11704         
11705         if(this.disabled || !this.triggerList){
11706             return;
11707         }
11708         
11709         this.page = 0;
11710         this.loadNext = false;
11711         
11712         if(this.isExpanded()){
11713             this.collapse();
11714             if (!this.blockFocus) {
11715                 this.inputEl().focus();
11716             }
11717             
11718         }else {
11719             this.hasFocus = true;
11720             if(this.triggerAction == 'all') {
11721                 this.doQuery(this.allQuery, true);
11722             } else {
11723                 this.doQuery(this.getRawValue());
11724             }
11725             if (!this.blockFocus) {
11726                 this.inputEl().focus();
11727             }
11728         }
11729     },
11730     
11731     onTickableTriggerClick : function(e)
11732     {
11733         if(this.disabled){
11734             return;
11735         }
11736         
11737         this.page = 0;
11738         this.loadNext = false;
11739         this.hasFocus = true;
11740         
11741         if(this.triggerAction == 'all') {
11742             this.doQuery(this.allQuery, true);
11743         } else {
11744             this.doQuery(this.getRawValue());
11745         }
11746     },
11747     
11748     onSearchFieldClick : function(e)
11749     {
11750         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11751             return;
11752         }
11753         
11754         this.page = 0;
11755         this.loadNext = false;
11756         this.hasFocus = true;
11757         
11758         if(this.triggerAction == 'all') {
11759             this.doQuery(this.allQuery, true);
11760         } else {
11761             this.doQuery(this.getRawValue());
11762         }
11763     },
11764     
11765     listKeyPress : function(e)
11766     {
11767         //Roo.log('listkeypress');
11768         // scroll to first matching element based on key pres..
11769         if (e.isSpecialKey()) {
11770             return false;
11771         }
11772         var k = String.fromCharCode(e.getKey()).toUpperCase();
11773         //Roo.log(k);
11774         var match  = false;
11775         var csel = this.view.getSelectedNodes();
11776         var cselitem = false;
11777         if (csel.length) {
11778             var ix = this.view.indexOf(csel[0]);
11779             cselitem  = this.store.getAt(ix);
11780             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11781                 cselitem = false;
11782             }
11783             
11784         }
11785         
11786         this.store.each(function(v) { 
11787             if (cselitem) {
11788                 // start at existing selection.
11789                 if (cselitem.id == v.id) {
11790                     cselitem = false;
11791                 }
11792                 return true;
11793             }
11794                 
11795             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11796                 match = this.store.indexOf(v);
11797                 return false;
11798             }
11799             return true;
11800         }, this);
11801         
11802         if (match === false) {
11803             return true; // no more action?
11804         }
11805         // scroll to?
11806         this.view.select(match);
11807         var sn = Roo.get(this.view.getSelectedNodes()[0])
11808         //sn.scrollIntoView(sn.dom.parentNode, false);
11809     },
11810     
11811     onViewScroll : function(e, t){
11812         
11813         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){
11814             return;
11815         }
11816         
11817         this.hasQuery = true;
11818         
11819         this.loading = this.list.select('.loading', true).first();
11820         
11821         if(this.loading === null){
11822             this.list.createChild({
11823                 tag: 'div',
11824                 cls: 'loading select2-more-results select2-active',
11825                 html: 'Loading more results...'
11826             })
11827             
11828             this.loading = this.list.select('.loading', true).first();
11829             
11830             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11831             
11832             this.loading.hide();
11833         }
11834         
11835         this.loading.show();
11836         
11837         var _combo = this;
11838         
11839         this.page++;
11840         this.loadNext = true;
11841         
11842         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11843         
11844         return;
11845     },
11846     
11847     addItem : function(o)
11848     {   
11849         var dv = ''; // display value
11850         
11851         if (this.displayField) {
11852             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11853         } else {
11854             // this is an error condition!!!
11855             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11856         }
11857         
11858         if(!dv.length){
11859             return;
11860         }
11861         
11862         var choice = this.choices.createChild({
11863             tag: 'li',
11864             cls: 'select2-search-choice',
11865             cn: [
11866                 {
11867                     tag: 'div',
11868                     html: dv
11869                 },
11870                 {
11871                     tag: 'a',
11872                     href: '#',
11873                     cls: 'select2-search-choice-close',
11874                     tabindex: '-1'
11875                 }
11876             ]
11877             
11878         }, this.searchField);
11879         
11880         var close = choice.select('a.select2-search-choice-close', true).first()
11881         
11882         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11883         
11884         this.item.push(o);
11885         
11886         this.lastData = o;
11887         
11888         this.syncValue();
11889         
11890         this.inputEl().dom.value = '';
11891         
11892     },
11893     
11894     onRemoveItem : function(e, _self, o)
11895     {
11896         e.preventDefault();
11897         var index = this.item.indexOf(o.data) * 1;
11898         
11899         if( index < 0){
11900             Roo.log('not this item?!');
11901             return;
11902         }
11903         
11904         this.item.splice(index, 1);
11905         o.item.remove();
11906         
11907         this.syncValue();
11908         
11909         this.fireEvent('remove', this, e);
11910         
11911     },
11912     
11913     syncValue : function()
11914     {
11915         if(!this.item.length){
11916             this.clearValue();
11917             return;
11918         }
11919             
11920         var value = [];
11921         var _this = this;
11922         Roo.each(this.item, function(i){
11923             if(_this.valueField){
11924                 value.push(i[_this.valueField]);
11925                 return;
11926             }
11927
11928             value.push(i);
11929         });
11930
11931         this.value = value.join(',');
11932
11933         if(this.hiddenField){
11934             this.hiddenField.dom.value = this.value;
11935         }
11936     },
11937     
11938     clearItem : function()
11939     {
11940         if(!this.multiple){
11941             return;
11942         }
11943         
11944         this.item = [];
11945         
11946         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11947            c.remove();
11948         });
11949         
11950         this.syncValue();
11951     },
11952     
11953     inputEl: function ()
11954     {
11955         if(this.tickable){
11956             return this.searchField;
11957         }
11958         return this.el.select('input.form-control',true).first();
11959     },
11960     
11961     
11962     onTickableFooterButtonClick : function(e, btn, el)
11963     {
11964         e.preventDefault();
11965         
11966         if(btn && btn.name == 'cancel'){
11967             this.tickItems = Roo.apply([], this.item);
11968             this.collapse();
11969             return;
11970         }
11971         
11972         this.clearItem();
11973         
11974         var _this = this;
11975         
11976         Roo.each(this.tickItems, function(o){
11977             _this.addItem(o);
11978         });
11979         
11980         this.collapse();
11981         
11982     }
11983     
11984     
11985
11986     /** 
11987     * @cfg {Boolean} grow 
11988     * @hide 
11989     */
11990     /** 
11991     * @cfg {Number} growMin 
11992     * @hide 
11993     */
11994     /** 
11995     * @cfg {Number} growMax 
11996     * @hide 
11997     */
11998     /**
11999      * @hide
12000      * @method autoSize
12001      */
12002 });
12003 /*
12004  * Based on:
12005  * Ext JS Library 1.1.1
12006  * Copyright(c) 2006-2007, Ext JS, LLC.
12007  *
12008  * Originally Released Under LGPL - original licence link has changed is not relivant.
12009  *
12010  * Fork - LGPL
12011  * <script type="text/javascript">
12012  */
12013
12014 /**
12015  * @class Roo.View
12016  * @extends Roo.util.Observable
12017  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12018  * This class also supports single and multi selection modes. <br>
12019  * Create a data model bound view:
12020  <pre><code>
12021  var store = new Roo.data.Store(...);
12022
12023  var view = new Roo.View({
12024     el : "my-element",
12025     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12026  
12027     singleSelect: true,
12028     selectedClass: "ydataview-selected",
12029     store: store
12030  });
12031
12032  // listen for node click?
12033  view.on("click", function(vw, index, node, e){
12034  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12035  });
12036
12037  // load XML data
12038  dataModel.load("foobar.xml");
12039  </code></pre>
12040  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12041  * <br><br>
12042  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12043  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12044  * 
12045  * Note: old style constructor is still suported (container, template, config)
12046  * 
12047  * @constructor
12048  * Create a new View
12049  * @param {Object} config The config object
12050  * 
12051  */
12052 Roo.View = function(config, depreciated_tpl, depreciated_config){
12053     
12054     this.parent = false;
12055     
12056     if (typeof(depreciated_tpl) == 'undefined') {
12057         // new way.. - universal constructor.
12058         Roo.apply(this, config);
12059         this.el  = Roo.get(this.el);
12060     } else {
12061         // old format..
12062         this.el  = Roo.get(config);
12063         this.tpl = depreciated_tpl;
12064         Roo.apply(this, depreciated_config);
12065     }
12066     this.wrapEl  = this.el.wrap().wrap();
12067     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12068     
12069     
12070     if(typeof(this.tpl) == "string"){
12071         this.tpl = new Roo.Template(this.tpl);
12072     } else {
12073         // support xtype ctors..
12074         this.tpl = new Roo.factory(this.tpl, Roo);
12075     }
12076     
12077     
12078     this.tpl.compile();
12079     
12080     /** @private */
12081     this.addEvents({
12082         /**
12083          * @event beforeclick
12084          * Fires before a click is processed. Returns false to cancel the default action.
12085          * @param {Roo.View} this
12086          * @param {Number} index The index of the target node
12087          * @param {HTMLElement} node The target node
12088          * @param {Roo.EventObject} e The raw event object
12089          */
12090             "beforeclick" : true,
12091         /**
12092          * @event click
12093          * Fires when a template node is clicked.
12094          * @param {Roo.View} this
12095          * @param {Number} index The index of the target node
12096          * @param {HTMLElement} node The target node
12097          * @param {Roo.EventObject} e The raw event object
12098          */
12099             "click" : true,
12100         /**
12101          * @event dblclick
12102          * Fires when a template node is double clicked.
12103          * @param {Roo.View} this
12104          * @param {Number} index The index of the target node
12105          * @param {HTMLElement} node The target node
12106          * @param {Roo.EventObject} e The raw event object
12107          */
12108             "dblclick" : true,
12109         /**
12110          * @event contextmenu
12111          * Fires when a template node is right clicked.
12112          * @param {Roo.View} this
12113          * @param {Number} index The index of the target node
12114          * @param {HTMLElement} node The target node
12115          * @param {Roo.EventObject} e The raw event object
12116          */
12117             "contextmenu" : true,
12118         /**
12119          * @event selectionchange
12120          * Fires when the selected nodes change.
12121          * @param {Roo.View} this
12122          * @param {Array} selections Array of the selected nodes
12123          */
12124             "selectionchange" : true,
12125     
12126         /**
12127          * @event beforeselect
12128          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12129          * @param {Roo.View} this
12130          * @param {HTMLElement} node The node to be selected
12131          * @param {Array} selections Array of currently selected nodes
12132          */
12133             "beforeselect" : true,
12134         /**
12135          * @event preparedata
12136          * Fires on every row to render, to allow you to change the data.
12137          * @param {Roo.View} this
12138          * @param {Object} data to be rendered (change this)
12139          */
12140           "preparedata" : true
12141           
12142           
12143         });
12144
12145
12146
12147     this.el.on({
12148         "click": this.onClick,
12149         "dblclick": this.onDblClick,
12150         "contextmenu": this.onContextMenu,
12151         scope:this
12152     });
12153
12154     this.selections = [];
12155     this.nodes = [];
12156     this.cmp = new Roo.CompositeElementLite([]);
12157     if(this.store){
12158         this.store = Roo.factory(this.store, Roo.data);
12159         this.setStore(this.store, true);
12160     }
12161     
12162     if ( this.footer && this.footer.xtype) {
12163            
12164          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12165         
12166         this.footer.dataSource = this.store
12167         this.footer.container = fctr;
12168         this.footer = Roo.factory(this.footer, Roo);
12169         fctr.insertFirst(this.el);
12170         
12171         // this is a bit insane - as the paging toolbar seems to detach the el..
12172 //        dom.parentNode.parentNode.parentNode
12173          // they get detached?
12174     }
12175     
12176     
12177     Roo.View.superclass.constructor.call(this);
12178     
12179     
12180 };
12181
12182 Roo.extend(Roo.View, Roo.util.Observable, {
12183     
12184      /**
12185      * @cfg {Roo.data.Store} store Data store to load data from.
12186      */
12187     store : false,
12188     
12189     /**
12190      * @cfg {String|Roo.Element} el The container element.
12191      */
12192     el : '',
12193     
12194     /**
12195      * @cfg {String|Roo.Template} tpl The template used by this View 
12196      */
12197     tpl : false,
12198     /**
12199      * @cfg {String} dataName the named area of the template to use as the data area
12200      *                          Works with domtemplates roo-name="name"
12201      */
12202     dataName: false,
12203     /**
12204      * @cfg {String} selectedClass The css class to add to selected nodes
12205      */
12206     selectedClass : "x-view-selected",
12207      /**
12208      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12209      */
12210     emptyText : "",
12211     
12212     /**
12213      * @cfg {String} text to display on mask (default Loading)
12214      */
12215     mask : false,
12216     /**
12217      * @cfg {Boolean} multiSelect Allow multiple selection
12218      */
12219     multiSelect : false,
12220     /**
12221      * @cfg {Boolean} singleSelect Allow single selection
12222      */
12223     singleSelect:  false,
12224     
12225     /**
12226      * @cfg {Boolean} toggleSelect - selecting 
12227      */
12228     toggleSelect : false,
12229     
12230     /**
12231      * @cfg {Boolean} tickable - selecting 
12232      */
12233     tickable : false,
12234     
12235     /**
12236      * Returns the element this view is bound to.
12237      * @return {Roo.Element}
12238      */
12239     getEl : function(){
12240         return this.wrapEl;
12241     },
12242     
12243     
12244
12245     /**
12246      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12247      */
12248     refresh : function(){
12249         Roo.log('refresh');
12250         var t = this.tpl;
12251         
12252         // if we are using something like 'domtemplate', then
12253         // the what gets used is:
12254         // t.applySubtemplate(NAME, data, wrapping data..)
12255         // the outer template then get' applied with
12256         //     the store 'extra data'
12257         // and the body get's added to the
12258         //      roo-name="data" node?
12259         //      <span class='roo-tpl-{name}'></span> ?????
12260         
12261         
12262         
12263         this.clearSelections();
12264         this.el.update("");
12265         var html = [];
12266         var records = this.store.getRange();
12267         if(records.length < 1) {
12268             
12269             // is this valid??  = should it render a template??
12270             
12271             this.el.update(this.emptyText);
12272             return;
12273         }
12274         var el = this.el;
12275         if (this.dataName) {
12276             this.el.update(t.apply(this.store.meta)); //????
12277             el = this.el.child('.roo-tpl-' + this.dataName);
12278         }
12279         
12280         for(var i = 0, len = records.length; i < len; i++){
12281             var data = this.prepareData(records[i].data, i, records[i]);
12282             this.fireEvent("preparedata", this, data, i, records[i]);
12283             
12284             var d = Roo.apply({}, data);
12285             
12286             if(this.tickable){
12287                 Roo.apply(d, {'roo-id' : Roo.id()});
12288                 
12289                 var _this = this;
12290             
12291                 Roo.each(this.parent.item, function(item){
12292                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12293                         return;
12294                     }
12295                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12296                 });
12297             }
12298             
12299             html[html.length] = Roo.util.Format.trim(
12300                 this.dataName ?
12301                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12302                     t.apply(d)
12303             );
12304         }
12305         
12306         
12307         
12308         el.update(html.join(""));
12309         this.nodes = el.dom.childNodes;
12310         this.updateIndexes(0);
12311     },
12312     
12313
12314     /**
12315      * Function to override to reformat the data that is sent to
12316      * the template for each node.
12317      * DEPRICATED - use the preparedata event handler.
12318      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12319      * a JSON object for an UpdateManager bound view).
12320      */
12321     prepareData : function(data, index, record)
12322     {
12323         this.fireEvent("preparedata", this, data, index, record);
12324         return data;
12325     },
12326
12327     onUpdate : function(ds, record){
12328          Roo.log('on update');   
12329         this.clearSelections();
12330         var index = this.store.indexOf(record);
12331         var n = this.nodes[index];
12332         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12333         n.parentNode.removeChild(n);
12334         this.updateIndexes(index, index);
12335     },
12336
12337     
12338     
12339 // --------- FIXME     
12340     onAdd : function(ds, records, index)
12341     {
12342         Roo.log(['on Add', ds, records, index] );        
12343         this.clearSelections();
12344         if(this.nodes.length == 0){
12345             this.refresh();
12346             return;
12347         }
12348         var n = this.nodes[index];
12349         for(var i = 0, len = records.length; i < len; i++){
12350             var d = this.prepareData(records[i].data, i, records[i]);
12351             if(n){
12352                 this.tpl.insertBefore(n, d);
12353             }else{
12354                 
12355                 this.tpl.append(this.el, d);
12356             }
12357         }
12358         this.updateIndexes(index);
12359     },
12360
12361     onRemove : function(ds, record, index){
12362         Roo.log('onRemove');
12363         this.clearSelections();
12364         var el = this.dataName  ?
12365             this.el.child('.roo-tpl-' + this.dataName) :
12366             this.el; 
12367         
12368         el.dom.removeChild(this.nodes[index]);
12369         this.updateIndexes(index);
12370     },
12371
12372     /**
12373      * Refresh an individual node.
12374      * @param {Number} index
12375      */
12376     refreshNode : function(index){
12377         this.onUpdate(this.store, this.store.getAt(index));
12378     },
12379
12380     updateIndexes : function(startIndex, endIndex){
12381         var ns = this.nodes;
12382         startIndex = startIndex || 0;
12383         endIndex = endIndex || ns.length - 1;
12384         for(var i = startIndex; i <= endIndex; i++){
12385             ns[i].nodeIndex = i;
12386         }
12387     },
12388
12389     /**
12390      * Changes the data store this view uses and refresh the view.
12391      * @param {Store} store
12392      */
12393     setStore : function(store, initial){
12394         if(!initial && this.store){
12395             this.store.un("datachanged", this.refresh);
12396             this.store.un("add", this.onAdd);
12397             this.store.un("remove", this.onRemove);
12398             this.store.un("update", this.onUpdate);
12399             this.store.un("clear", this.refresh);
12400             this.store.un("beforeload", this.onBeforeLoad);
12401             this.store.un("load", this.onLoad);
12402             this.store.un("loadexception", this.onLoad);
12403         }
12404         if(store){
12405           
12406             store.on("datachanged", this.refresh, this);
12407             store.on("add", this.onAdd, this);
12408             store.on("remove", this.onRemove, this);
12409             store.on("update", this.onUpdate, this);
12410             store.on("clear", this.refresh, this);
12411             store.on("beforeload", this.onBeforeLoad, this);
12412             store.on("load", this.onLoad, this);
12413             store.on("loadexception", this.onLoad, this);
12414         }
12415         
12416         if(store){
12417             this.refresh();
12418         }
12419     },
12420     /**
12421      * onbeforeLoad - masks the loading area.
12422      *
12423      */
12424     onBeforeLoad : function(store,opts)
12425     {
12426          Roo.log('onBeforeLoad');   
12427         if (!opts.add) {
12428             this.el.update("");
12429         }
12430         this.el.mask(this.mask ? this.mask : "Loading" ); 
12431     },
12432     onLoad : function ()
12433     {
12434         this.el.unmask();
12435     },
12436     
12437
12438     /**
12439      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12440      * @param {HTMLElement} node
12441      * @return {HTMLElement} The template node
12442      */
12443     findItemFromChild : function(node){
12444         var el = this.dataName  ?
12445             this.el.child('.roo-tpl-' + this.dataName,true) :
12446             this.el.dom; 
12447         
12448         if(!node || node.parentNode == el){
12449                     return node;
12450             }
12451             var p = node.parentNode;
12452             while(p && p != el){
12453             if(p.parentNode == el){
12454                 return p;
12455             }
12456             p = p.parentNode;
12457         }
12458             return null;
12459     },
12460
12461     /** @ignore */
12462     onClick : function(e){
12463         var item = this.findItemFromChild(e.getTarget());
12464         if(item){
12465             var index = this.indexOf(item);
12466             if(this.onItemClick(item, index, e) !== false){
12467                 this.fireEvent("click", this, index, item, e);
12468             }
12469         }else{
12470             this.clearSelections();
12471         }
12472     },
12473
12474     /** @ignore */
12475     onContextMenu : function(e){
12476         var item = this.findItemFromChild(e.getTarget());
12477         if(item){
12478             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12479         }
12480     },
12481
12482     /** @ignore */
12483     onDblClick : function(e){
12484         var item = this.findItemFromChild(e.getTarget());
12485         if(item){
12486             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12487         }
12488     },
12489
12490     onItemClick : function(item, index, e)
12491     {
12492         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12493             return false;
12494         }
12495         if (this.toggleSelect) {
12496             var m = this.isSelected(item) ? 'unselect' : 'select';
12497             Roo.log(m);
12498             var _t = this;
12499             _t[m](item, true, false);
12500             return true;
12501         }
12502         if(this.multiSelect || this.singleSelect){
12503             if(this.multiSelect && e.shiftKey && this.lastSelection){
12504                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12505             }else{
12506                 this.select(item, this.multiSelect && e.ctrlKey);
12507                 this.lastSelection = item;
12508             }
12509             
12510             if(!this.tickable){
12511                 e.preventDefault();
12512             }
12513             
12514         }
12515         return true;
12516     },
12517
12518     /**
12519      * Get the number of selected nodes.
12520      * @return {Number}
12521      */
12522     getSelectionCount : function(){
12523         return this.selections.length;
12524     },
12525
12526     /**
12527      * Get the currently selected nodes.
12528      * @return {Array} An array of HTMLElements
12529      */
12530     getSelectedNodes : function(){
12531         return this.selections;
12532     },
12533
12534     /**
12535      * Get the indexes of the selected nodes.
12536      * @return {Array}
12537      */
12538     getSelectedIndexes : function(){
12539         var indexes = [], s = this.selections;
12540         for(var i = 0, len = s.length; i < len; i++){
12541             indexes.push(s[i].nodeIndex);
12542         }
12543         return indexes;
12544     },
12545
12546     /**
12547      * Clear all selections
12548      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12549      */
12550     clearSelections : function(suppressEvent){
12551         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12552             this.cmp.elements = this.selections;
12553             this.cmp.removeClass(this.selectedClass);
12554             this.selections = [];
12555             if(!suppressEvent){
12556                 this.fireEvent("selectionchange", this, this.selections);
12557             }
12558         }
12559     },
12560
12561     /**
12562      * Returns true if the passed node is selected
12563      * @param {HTMLElement/Number} node The node or node index
12564      * @return {Boolean}
12565      */
12566     isSelected : function(node){
12567         var s = this.selections;
12568         if(s.length < 1){
12569             return false;
12570         }
12571         node = this.getNode(node);
12572         return s.indexOf(node) !== -1;
12573     },
12574
12575     /**
12576      * Selects nodes.
12577      * @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
12578      * @param {Boolean} keepExisting (optional) true to keep existing selections
12579      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12580      */
12581     select : function(nodeInfo, keepExisting, suppressEvent){
12582         if(nodeInfo instanceof Array){
12583             if(!keepExisting){
12584                 this.clearSelections(true);
12585             }
12586             for(var i = 0, len = nodeInfo.length; i < len; i++){
12587                 this.select(nodeInfo[i], true, true);
12588             }
12589             return;
12590         } 
12591         var node = this.getNode(nodeInfo);
12592         if(!node || this.isSelected(node)){
12593             return; // already selected.
12594         }
12595         if(!keepExisting){
12596             this.clearSelections(true);
12597         }
12598         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12599             Roo.fly(node).addClass(this.selectedClass);
12600             this.selections.push(node);
12601             if(!suppressEvent){
12602                 this.fireEvent("selectionchange", this, this.selections);
12603             }
12604         }
12605         
12606         
12607     },
12608       /**
12609      * Unselects nodes.
12610      * @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
12611      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12612      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12613      */
12614     unselect : function(nodeInfo, keepExisting, suppressEvent)
12615     {
12616         if(nodeInfo instanceof Array){
12617             Roo.each(this.selections, function(s) {
12618                 this.unselect(s, nodeInfo);
12619             }, this);
12620             return;
12621         }
12622         var node = this.getNode(nodeInfo);
12623         if(!node || !this.isSelected(node)){
12624             Roo.log("not selected");
12625             return; // not selected.
12626         }
12627         // fireevent???
12628         var ns = [];
12629         Roo.each(this.selections, function(s) {
12630             if (s == node ) {
12631                 Roo.fly(node).removeClass(this.selectedClass);
12632
12633                 return;
12634             }
12635             ns.push(s);
12636         },this);
12637         
12638         this.selections= ns;
12639         this.fireEvent("selectionchange", this, this.selections);
12640     },
12641
12642     /**
12643      * Gets a template node.
12644      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12645      * @return {HTMLElement} The node or null if it wasn't found
12646      */
12647     getNode : function(nodeInfo){
12648         if(typeof nodeInfo == "string"){
12649             return document.getElementById(nodeInfo);
12650         }else if(typeof nodeInfo == "number"){
12651             return this.nodes[nodeInfo];
12652         }
12653         return nodeInfo;
12654     },
12655
12656     /**
12657      * Gets a range template nodes.
12658      * @param {Number} startIndex
12659      * @param {Number} endIndex
12660      * @return {Array} An array of nodes
12661      */
12662     getNodes : function(start, end){
12663         var ns = this.nodes;
12664         start = start || 0;
12665         end = typeof end == "undefined" ? ns.length - 1 : end;
12666         var nodes = [];
12667         if(start <= end){
12668             for(var i = start; i <= end; i++){
12669                 nodes.push(ns[i]);
12670             }
12671         } else{
12672             for(var i = start; i >= end; i--){
12673                 nodes.push(ns[i]);
12674             }
12675         }
12676         return nodes;
12677     },
12678
12679     /**
12680      * Finds the index of the passed node
12681      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12682      * @return {Number} The index of the node or -1
12683      */
12684     indexOf : function(node){
12685         node = this.getNode(node);
12686         if(typeof node.nodeIndex == "number"){
12687             return node.nodeIndex;
12688         }
12689         var ns = this.nodes;
12690         for(var i = 0, len = ns.length; i < len; i++){
12691             if(ns[i] == node){
12692                 return i;
12693             }
12694         }
12695         return -1;
12696     }
12697 });
12698 /*
12699  * - LGPL
12700  *
12701  * based on jquery fullcalendar
12702  * 
12703  */
12704
12705 Roo.bootstrap = Roo.bootstrap || {};
12706 /**
12707  * @class Roo.bootstrap.Calendar
12708  * @extends Roo.bootstrap.Component
12709  * Bootstrap Calendar class
12710  * @cfg {Boolean} loadMask (true|false) default false
12711  * @cfg {Object} header generate the user specific header of the calendar, default false
12712
12713  * @constructor
12714  * Create a new Container
12715  * @param {Object} config The config object
12716  */
12717
12718
12719
12720 Roo.bootstrap.Calendar = function(config){
12721     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12722      this.addEvents({
12723         /**
12724              * @event select
12725              * Fires when a date is selected
12726              * @param {DatePicker} this
12727              * @param {Date} date The selected date
12728              */
12729         'select': true,
12730         /**
12731              * @event monthchange
12732              * Fires when the displayed month changes 
12733              * @param {DatePicker} this
12734              * @param {Date} date The selected month
12735              */
12736         'monthchange': true,
12737         /**
12738              * @event evententer
12739              * Fires when mouse over an event
12740              * @param {Calendar} this
12741              * @param {event} Event
12742              */
12743         'evententer': true,
12744         /**
12745              * @event eventleave
12746              * Fires when the mouse leaves an
12747              * @param {Calendar} this
12748              * @param {event}
12749              */
12750         'eventleave': true,
12751         /**
12752              * @event eventclick
12753              * Fires when the mouse click an
12754              * @param {Calendar} this
12755              * @param {event}
12756              */
12757         'eventclick': true
12758         
12759     });
12760
12761 };
12762
12763 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12764     
12765      /**
12766      * @cfg {Number} startDay
12767      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12768      */
12769     startDay : 0,
12770     
12771     loadMask : false,
12772     
12773     header : false,
12774       
12775     getAutoCreate : function(){
12776         
12777         
12778         var fc_button = function(name, corner, style, content ) {
12779             return Roo.apply({},{
12780                 tag : 'span',
12781                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12782                          (corner.length ?
12783                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12784                             ''
12785                         ),
12786                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12787                 unselectable: 'on'
12788             });
12789         };
12790         
12791         var header = {};
12792         
12793         if(!this.header){
12794             header = {
12795                 tag : 'table',
12796                 cls : 'fc-header',
12797                 style : 'width:100%',
12798                 cn : [
12799                     {
12800                         tag: 'tr',
12801                         cn : [
12802                             {
12803                                 tag : 'td',
12804                                 cls : 'fc-header-left',
12805                                 cn : [
12806                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12807                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12808                                     { tag: 'span', cls: 'fc-header-space' },
12809                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12810
12811
12812                                 ]
12813                             },
12814
12815                             {
12816                                 tag : 'td',
12817                                 cls : 'fc-header-center',
12818                                 cn : [
12819                                     {
12820                                         tag: 'span',
12821                                         cls: 'fc-header-title',
12822                                         cn : {
12823                                             tag: 'H2',
12824                                             html : 'month / year'
12825                                         }
12826                                     }
12827
12828                                 ]
12829                             },
12830                             {
12831                                 tag : 'td',
12832                                 cls : 'fc-header-right',
12833                                 cn : [
12834                               /*      fc_button('month', 'left', '', 'month' ),
12835                                     fc_button('week', '', '', 'week' ),
12836                                     fc_button('day', 'right', '', 'day' )
12837                                 */    
12838
12839                                 ]
12840                             }
12841
12842                         ]
12843                     }
12844                 ]
12845             };
12846         }
12847         
12848         header = this.header;
12849         
12850        
12851         var cal_heads = function() {
12852             var ret = [];
12853             // fixme - handle this.
12854             
12855             for (var i =0; i < Date.dayNames.length; i++) {
12856                 var d = Date.dayNames[i];
12857                 ret.push({
12858                     tag: 'th',
12859                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12860                     html : d.substring(0,3)
12861                 });
12862                 
12863             }
12864             ret[0].cls += ' fc-first';
12865             ret[6].cls += ' fc-last';
12866             return ret;
12867         };
12868         var cal_cell = function(n) {
12869             return  {
12870                 tag: 'td',
12871                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12872                 cn : [
12873                     {
12874                         cn : [
12875                             {
12876                                 cls: 'fc-day-number',
12877                                 html: 'D'
12878                             },
12879                             {
12880                                 cls: 'fc-day-content',
12881                              
12882                                 cn : [
12883                                      {
12884                                         style: 'position: relative;' // height: 17px;
12885                                     }
12886                                 ]
12887                             }
12888                             
12889                             
12890                         ]
12891                     }
12892                 ]
12893                 
12894             }
12895         };
12896         var cal_rows = function() {
12897             
12898             var ret = []
12899             for (var r = 0; r < 6; r++) {
12900                 var row= {
12901                     tag : 'tr',
12902                     cls : 'fc-week',
12903                     cn : []
12904                 };
12905                 
12906                 for (var i =0; i < Date.dayNames.length; i++) {
12907                     var d = Date.dayNames[i];
12908                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12909
12910                 }
12911                 row.cn[0].cls+=' fc-first';
12912                 row.cn[0].cn[0].style = 'min-height:90px';
12913                 row.cn[6].cls+=' fc-last';
12914                 ret.push(row);
12915                 
12916             }
12917             ret[0].cls += ' fc-first';
12918             ret[4].cls += ' fc-prev-last';
12919             ret[5].cls += ' fc-last';
12920             return ret;
12921             
12922         };
12923         
12924         var cal_table = {
12925             tag: 'table',
12926             cls: 'fc-border-separate',
12927             style : 'width:100%',
12928             cellspacing  : 0,
12929             cn : [
12930                 { 
12931                     tag: 'thead',
12932                     cn : [
12933                         { 
12934                             tag: 'tr',
12935                             cls : 'fc-first fc-last',
12936                             cn : cal_heads()
12937                         }
12938                     ]
12939                 },
12940                 { 
12941                     tag: 'tbody',
12942                     cn : cal_rows()
12943                 }
12944                   
12945             ]
12946         };
12947          
12948          var cfg = {
12949             cls : 'fc fc-ltr',
12950             cn : [
12951                 header,
12952                 {
12953                     cls : 'fc-content',
12954                     style : "position: relative;",
12955                     cn : [
12956                         {
12957                             cls : 'fc-view fc-view-month fc-grid',
12958                             style : 'position: relative',
12959                             unselectable : 'on',
12960                             cn : [
12961                                 {
12962                                     cls : 'fc-event-container',
12963                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12964                                 },
12965                                 cal_table
12966                             ]
12967                         }
12968                     ]
12969     
12970                 }
12971            ] 
12972             
12973         };
12974         
12975          
12976         
12977         return cfg;
12978     },
12979     
12980     
12981     initEvents : function()
12982     {
12983         if(!this.store){
12984             throw "can not find store for calendar";
12985         }
12986         
12987         var mark = {
12988             tag: "div",
12989             cls:"x-dlg-mask",
12990             style: "text-align:center",
12991             cn: [
12992                 {
12993                     tag: "div",
12994                     style: "background-color:white;width:50%;margin:250 auto",
12995                     cn: [
12996                         {
12997                             tag: "img",
12998                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12999                         },
13000                         {
13001                             tag: "span",
13002                             html: "Loading"
13003                         }
13004                         
13005                     ]
13006                 }
13007             ]
13008         }
13009         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13010         
13011         var size = this.el.select('.fc-content', true).first().getSize();
13012         this.maskEl.setSize(size.width, size.height);
13013         this.maskEl.enableDisplayMode("block");
13014         if(!this.loadMask){
13015             this.maskEl.hide();
13016         }
13017         
13018         this.store = Roo.factory(this.store, Roo.data);
13019         this.store.on('load', this.onLoad, this);
13020         this.store.on('beforeload', this.onBeforeLoad, this);
13021         
13022         this.resize();
13023         
13024         this.cells = this.el.select('.fc-day',true);
13025         //Roo.log(this.cells);
13026         this.textNodes = this.el.query('.fc-day-number');
13027         this.cells.addClassOnOver('fc-state-hover');
13028         
13029         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13030         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13031         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13032         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13033         
13034         this.on('monthchange', this.onMonthChange, this);
13035         
13036         this.update(new Date().clearTime());
13037     },
13038     
13039     resize : function() {
13040         var sz  = this.el.getSize();
13041         
13042         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13043         this.el.select('.fc-day-content div',true).setHeight(34);
13044     },
13045     
13046     
13047     // private
13048     showPrevMonth : function(e){
13049         this.update(this.activeDate.add("mo", -1));
13050     },
13051     showToday : function(e){
13052         this.update(new Date().clearTime());
13053     },
13054     // private
13055     showNextMonth : function(e){
13056         this.update(this.activeDate.add("mo", 1));
13057     },
13058
13059     // private
13060     showPrevYear : function(){
13061         this.update(this.activeDate.add("y", -1));
13062     },
13063
13064     // private
13065     showNextYear : function(){
13066         this.update(this.activeDate.add("y", 1));
13067     },
13068
13069     
13070    // private
13071     update : function(date)
13072     {
13073         var vd = this.activeDate;
13074         this.activeDate = date;
13075 //        if(vd && this.el){
13076 //            var t = date.getTime();
13077 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13078 //                Roo.log('using add remove');
13079 //                
13080 //                this.fireEvent('monthchange', this, date);
13081 //                
13082 //                this.cells.removeClass("fc-state-highlight");
13083 //                this.cells.each(function(c){
13084 //                   if(c.dateValue == t){
13085 //                       c.addClass("fc-state-highlight");
13086 //                       setTimeout(function(){
13087 //                            try{c.dom.firstChild.focus();}catch(e){}
13088 //                       }, 50);
13089 //                       return false;
13090 //                   }
13091 //                   return true;
13092 //                });
13093 //                return;
13094 //            }
13095 //        }
13096         
13097         var days = date.getDaysInMonth();
13098         
13099         var firstOfMonth = date.getFirstDateOfMonth();
13100         var startingPos = firstOfMonth.getDay()-this.startDay;
13101         
13102         if(startingPos < this.startDay){
13103             startingPos += 7;
13104         }
13105         
13106         var pm = date.add(Date.MONTH, -1);
13107         var prevStart = pm.getDaysInMonth()-startingPos;
13108 //        
13109         this.cells = this.el.select('.fc-day',true);
13110         this.textNodes = this.el.query('.fc-day-number');
13111         this.cells.addClassOnOver('fc-state-hover');
13112         
13113         var cells = this.cells.elements;
13114         var textEls = this.textNodes;
13115         
13116         Roo.each(cells, function(cell){
13117             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13118         });
13119         
13120         days += startingPos;
13121
13122         // convert everything to numbers so it's fast
13123         var day = 86400000;
13124         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13125         //Roo.log(d);
13126         //Roo.log(pm);
13127         //Roo.log(prevStart);
13128         
13129         var today = new Date().clearTime().getTime();
13130         var sel = date.clearTime().getTime();
13131         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13132         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13133         var ddMatch = this.disabledDatesRE;
13134         var ddText = this.disabledDatesText;
13135         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13136         var ddaysText = this.disabledDaysText;
13137         var format = this.format;
13138         
13139         var setCellClass = function(cal, cell){
13140             cell.row = 0;
13141             cell.events = [];
13142             cell.more = [];
13143             //Roo.log('set Cell Class');
13144             cell.title = "";
13145             var t = d.getTime();
13146             
13147             //Roo.log(d);
13148             
13149             cell.dateValue = t;
13150             if(t == today){
13151                 cell.className += " fc-today";
13152                 cell.className += " fc-state-highlight";
13153                 cell.title = cal.todayText;
13154             }
13155             if(t == sel){
13156                 // disable highlight in other month..
13157                 //cell.className += " fc-state-highlight";
13158                 
13159             }
13160             // disabling
13161             if(t < min) {
13162                 cell.className = " fc-state-disabled";
13163                 cell.title = cal.minText;
13164                 return;
13165             }
13166             if(t > max) {
13167                 cell.className = " fc-state-disabled";
13168                 cell.title = cal.maxText;
13169                 return;
13170             }
13171             if(ddays){
13172                 if(ddays.indexOf(d.getDay()) != -1){
13173                     cell.title = ddaysText;
13174                     cell.className = " fc-state-disabled";
13175                 }
13176             }
13177             if(ddMatch && format){
13178                 var fvalue = d.dateFormat(format);
13179                 if(ddMatch.test(fvalue)){
13180                     cell.title = ddText.replace("%0", fvalue);
13181                     cell.className = " fc-state-disabled";
13182                 }
13183             }
13184             
13185             if (!cell.initialClassName) {
13186                 cell.initialClassName = cell.dom.className;
13187             }
13188             
13189             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13190         };
13191
13192         var i = 0;
13193         
13194         for(; i < startingPos; i++) {
13195             textEls[i].innerHTML = (++prevStart);
13196             d.setDate(d.getDate()+1);
13197             
13198             cells[i].className = "fc-past fc-other-month";
13199             setCellClass(this, cells[i]);
13200         }
13201         
13202         var intDay = 0;
13203         
13204         for(; i < days; i++){
13205             intDay = i - startingPos + 1;
13206             textEls[i].innerHTML = (intDay);
13207             d.setDate(d.getDate()+1);
13208             
13209             cells[i].className = ''; // "x-date-active";
13210             setCellClass(this, cells[i]);
13211         }
13212         var extraDays = 0;
13213         
13214         for(; i < 42; i++) {
13215             textEls[i].innerHTML = (++extraDays);
13216             d.setDate(d.getDate()+1);
13217             
13218             cells[i].className = "fc-future fc-other-month";
13219             setCellClass(this, cells[i]);
13220         }
13221         
13222         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13223         
13224         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13225         
13226         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13227         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13228         
13229         if(totalRows != 6){
13230             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13231             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13232         }
13233         
13234         this.fireEvent('monthchange', this, date);
13235         
13236         
13237         /*
13238         if(!this.internalRender){
13239             var main = this.el.dom.firstChild;
13240             var w = main.offsetWidth;
13241             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13242             Roo.fly(main).setWidth(w);
13243             this.internalRender = true;
13244             // opera does not respect the auto grow header center column
13245             // then, after it gets a width opera refuses to recalculate
13246             // without a second pass
13247             if(Roo.isOpera && !this.secondPass){
13248                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13249                 this.secondPass = true;
13250                 this.update.defer(10, this, [date]);
13251             }
13252         }
13253         */
13254         
13255     },
13256     
13257     findCell : function(dt) {
13258         dt = dt.clearTime().getTime();
13259         var ret = false;
13260         this.cells.each(function(c){
13261             //Roo.log("check " +c.dateValue + '?=' + dt);
13262             if(c.dateValue == dt){
13263                 ret = c;
13264                 return false;
13265             }
13266             return true;
13267         });
13268         
13269         return ret;
13270     },
13271     
13272     findCells : function(ev) {
13273         var s = ev.start.clone().clearTime().getTime();
13274        // Roo.log(s);
13275         var e= ev.end.clone().clearTime().getTime();
13276        // Roo.log(e);
13277         var ret = [];
13278         this.cells.each(function(c){
13279              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13280             
13281             if(c.dateValue > e){
13282                 return ;
13283             }
13284             if(c.dateValue < s){
13285                 return ;
13286             }
13287             ret.push(c);
13288         });
13289         
13290         return ret;    
13291     },
13292     
13293 //    findBestRow: function(cells)
13294 //    {
13295 //        var ret = 0;
13296 //        
13297 //        for (var i =0 ; i < cells.length;i++) {
13298 //            ret  = Math.max(cells[i].rows || 0,ret);
13299 //        }
13300 //        return ret;
13301 //        
13302 //    },
13303     
13304     
13305     addItem : function(ev)
13306     {
13307         // look for vertical location slot in
13308         var cells = this.findCells(ev);
13309         
13310 //        ev.row = this.findBestRow(cells);
13311         
13312         // work out the location.
13313         
13314         var crow = false;
13315         var rows = [];
13316         for(var i =0; i < cells.length; i++) {
13317             
13318             cells[i].row = cells[0].row;
13319             
13320             if(i == 0){
13321                 cells[i].row = cells[i].row + 1;
13322             }
13323             
13324             if (!crow) {
13325                 crow = {
13326                     start : cells[i],
13327                     end :  cells[i]
13328                 };
13329                 continue;
13330             }
13331             if (crow.start.getY() == cells[i].getY()) {
13332                 // on same row.
13333                 crow.end = cells[i];
13334                 continue;
13335             }
13336             // different row.
13337             rows.push(crow);
13338             crow = {
13339                 start: cells[i],
13340                 end : cells[i]
13341             };
13342             
13343         }
13344         
13345         rows.push(crow);
13346         ev.els = [];
13347         ev.rows = rows;
13348         ev.cells = cells;
13349         
13350         cells[0].events.push(ev);
13351         
13352         this.calevents.push(ev);
13353     },
13354     
13355     clearEvents: function() {
13356         
13357         if(!this.calevents){
13358             return;
13359         }
13360         
13361         Roo.each(this.cells.elements, function(c){
13362             c.row = 0;
13363             c.events = [];
13364             c.more = [];
13365         });
13366         
13367         Roo.each(this.calevents, function(e) {
13368             Roo.each(e.els, function(el) {
13369                 el.un('mouseenter' ,this.onEventEnter, this);
13370                 el.un('mouseleave' ,this.onEventLeave, this);
13371                 el.remove();
13372             },this);
13373         },this);
13374         
13375         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13376             e.remove();
13377         });
13378         
13379     },
13380     
13381     renderEvents: function()
13382     {   
13383         var _this = this;
13384         
13385         this.cells.each(function(c) {
13386             
13387             if(c.row < 5){
13388                 return;
13389             }
13390             
13391             var ev = c.events;
13392             
13393             var r = 4;
13394             if(c.row != c.events.length){
13395                 r = 4 - (4 - (c.row - c.events.length));
13396             }
13397             
13398             c.events = ev.slice(0, r);
13399             c.more = ev.slice(r);
13400             
13401             if(c.more.length && c.more.length == 1){
13402                 c.events.push(c.more.pop());
13403             }
13404             
13405             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13406             
13407         });
13408             
13409         this.cells.each(function(c) {
13410             
13411             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13412             
13413             
13414             for (var e = 0; e < c.events.length; e++){
13415                 var ev = c.events[e];
13416                 var rows = ev.rows;
13417                 
13418                 for(var i = 0; i < rows.length; i++) {
13419                 
13420                     // how many rows should it span..
13421
13422                     var  cfg = {
13423                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13424                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13425
13426                         unselectable : "on",
13427                         cn : [
13428                             {
13429                                 cls: 'fc-event-inner',
13430                                 cn : [
13431     //                                {
13432     //                                  tag:'span',
13433     //                                  cls: 'fc-event-time',
13434     //                                  html : cells.length > 1 ? '' : ev.time
13435     //                                },
13436                                     {
13437                                       tag:'span',
13438                                       cls: 'fc-event-title',
13439                                       html : String.format('{0}', ev.title)
13440                                     }
13441
13442
13443                                 ]
13444                             },
13445                             {
13446                                 cls: 'ui-resizable-handle ui-resizable-e',
13447                                 html : '&nbsp;&nbsp;&nbsp'
13448                             }
13449
13450                         ]
13451                     };
13452
13453                     if (i == 0) {
13454                         cfg.cls += ' fc-event-start';
13455                     }
13456                     if ((i+1) == rows.length) {
13457                         cfg.cls += ' fc-event-end';
13458                     }
13459
13460                     var ctr = _this.el.select('.fc-event-container',true).first();
13461                     var cg = ctr.createChild(cfg);
13462
13463                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13464                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13465
13466                     var r = (c.more.length) ? 1 : 0;
13467                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13468                     cg.setWidth(ebox.right - sbox.x -2);
13469
13470                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13471                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13472                     cg.on('click', _this.onEventClick, _this, ev);
13473
13474                     ev.els.push(cg);
13475                     
13476                 }
13477                 
13478             }
13479             
13480             
13481             if(c.more.length){
13482                 var  cfg = {
13483                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13484                     style : 'position: absolute',
13485                     unselectable : "on",
13486                     cn : [
13487                         {
13488                             cls: 'fc-event-inner',
13489                             cn : [
13490                                 {
13491                                   tag:'span',
13492                                   cls: 'fc-event-title',
13493                                   html : 'More'
13494                                 }
13495
13496
13497                             ]
13498                         },
13499                         {
13500                             cls: 'ui-resizable-handle ui-resizable-e',
13501                             html : '&nbsp;&nbsp;&nbsp'
13502                         }
13503
13504                     ]
13505                 };
13506
13507                 var ctr = _this.el.select('.fc-event-container',true).first();
13508                 var cg = ctr.createChild(cfg);
13509
13510                 var sbox = c.select('.fc-day-content',true).first().getBox();
13511                 var ebox = c.select('.fc-day-content',true).first().getBox();
13512                 //Roo.log(cg);
13513                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13514                 cg.setWidth(ebox.right - sbox.x -2);
13515
13516                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13517                 
13518             }
13519             
13520         });
13521         
13522         
13523         
13524     },
13525     
13526     onEventEnter: function (e, el,event,d) {
13527         this.fireEvent('evententer', this, el, event);
13528     },
13529     
13530     onEventLeave: function (e, el,event,d) {
13531         this.fireEvent('eventleave', this, el, event);
13532     },
13533     
13534     onEventClick: function (e, el,event,d) {
13535         this.fireEvent('eventclick', this, el, event);
13536     },
13537     
13538     onMonthChange: function () {
13539         this.store.load();
13540     },
13541     
13542     onMoreEventClick: function(e, el, more)
13543     {
13544         var _this = this;
13545         
13546         this.calpopover.placement = 'right';
13547         this.calpopover.setTitle('More');
13548         
13549         this.calpopover.setContent('');
13550         
13551         var ctr = this.calpopover.el.select('.popover-content', true).first();
13552         
13553         Roo.each(more, function(m){
13554             var cfg = {
13555                 cls : 'fc-event-hori fc-event-draggable',
13556                 html : m.title
13557             }
13558             var cg = ctr.createChild(cfg);
13559             
13560             cg.on('click', _this.onEventClick, _this, m);
13561         });
13562         
13563         this.calpopover.show(el);
13564         
13565         
13566     },
13567     
13568     onLoad: function () 
13569     {   
13570         this.calevents = [];
13571         var cal = this;
13572         
13573         if(this.store.getCount() > 0){
13574             this.store.data.each(function(d){
13575                cal.addItem({
13576                     id : d.data.id,
13577                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13578                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13579                     time : d.data.start_time,
13580                     title : d.data.title,
13581                     description : d.data.description,
13582                     venue : d.data.venue
13583                 });
13584             });
13585         }
13586         
13587         this.renderEvents();
13588         
13589         if(this.calevents.length && this.loadMask){
13590             this.maskEl.hide();
13591         }
13592     },
13593     
13594     onBeforeLoad: function()
13595     {
13596         this.clearEvents();
13597         if(this.loadMask){
13598             this.maskEl.show();
13599         }
13600     }
13601 });
13602
13603  
13604  /*
13605  * - LGPL
13606  *
13607  * element
13608  * 
13609  */
13610
13611 /**
13612  * @class Roo.bootstrap.Popover
13613  * @extends Roo.bootstrap.Component
13614  * Bootstrap Popover class
13615  * @cfg {String} html contents of the popover   (or false to use children..)
13616  * @cfg {String} title of popover (or false to hide)
13617  * @cfg {String} placement how it is placed
13618  * @cfg {String} trigger click || hover (or false to trigger manually)
13619  * @cfg {String} over what (parent or false to trigger manually.)
13620  * 
13621  * @constructor
13622  * Create a new Popover
13623  * @param {Object} config The config object
13624  */
13625
13626 Roo.bootstrap.Popover = function(config){
13627     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13628 };
13629
13630 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13631     
13632     title: 'Fill in a title',
13633     html: false,
13634     
13635     placement : 'right',
13636     trigger : 'hover', // hover
13637     
13638     over: 'parent',
13639     
13640     can_build_overlaid : false,
13641     
13642     getChildContainer : function()
13643     {
13644         return this.el.select('.popover-content',true).first();
13645     },
13646     
13647     getAutoCreate : function(){
13648          Roo.log('make popover?');
13649         var cfg = {
13650            cls : 'popover roo-dynamic',
13651            style: 'display:block',
13652            cn : [
13653                 {
13654                     cls : 'arrow'
13655                 },
13656                 {
13657                     cls : 'popover-inner',
13658                     cn : [
13659                         {
13660                             tag: 'h3',
13661                             cls: 'popover-title',
13662                             html : this.title
13663                         },
13664                         {
13665                             cls : 'popover-content',
13666                             html : this.html
13667                         }
13668                     ]
13669                     
13670                 }
13671            ]
13672         };
13673         
13674         return cfg;
13675     },
13676     setTitle: function(str)
13677     {
13678         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13679     },
13680     setContent: function(str)
13681     {
13682         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13683     },
13684     // as it get's added to the bottom of the page.
13685     onRender : function(ct, position)
13686     {
13687         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13688         if(!this.el){
13689             var cfg = Roo.apply({},  this.getAutoCreate());
13690             cfg.id = Roo.id();
13691             
13692             if (this.cls) {
13693                 cfg.cls += ' ' + this.cls;
13694             }
13695             if (this.style) {
13696                 cfg.style = this.style;
13697             }
13698             Roo.log("adding to ")
13699             this.el = Roo.get(document.body).createChild(cfg, position);
13700             Roo.log(this.el);
13701         }
13702         this.initEvents();
13703     },
13704     
13705     initEvents : function()
13706     {
13707         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13708         this.el.enableDisplayMode('block');
13709         this.el.hide();
13710         if (this.over === false) {
13711             return; 
13712         }
13713         if (this.triggers === false) {
13714             return;
13715         }
13716         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13717         var triggers = this.trigger ? this.trigger.split(' ') : [];
13718         Roo.each(triggers, function(trigger) {
13719         
13720             if (trigger == 'click') {
13721                 on_el.on('click', this.toggle, this);
13722             } else if (trigger != 'manual') {
13723                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13724                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13725       
13726                 on_el.on(eventIn  ,this.enter, this);
13727                 on_el.on(eventOut, this.leave, this);
13728             }
13729         }, this);
13730         
13731     },
13732     
13733     
13734     // private
13735     timeout : null,
13736     hoverState : null,
13737     
13738     toggle : function () {
13739         this.hoverState == 'in' ? this.leave() : this.enter();
13740     },
13741     
13742     enter : function () {
13743        
13744     
13745         clearTimeout(this.timeout);
13746     
13747         this.hoverState = 'in'
13748     
13749         if (!this.delay || !this.delay.show) {
13750             this.show();
13751             return 
13752         }
13753         var _t = this;
13754         this.timeout = setTimeout(function () {
13755             if (_t.hoverState == 'in') {
13756                 _t.show();
13757             }
13758         }, this.delay.show)
13759     },
13760     leave : function() {
13761         clearTimeout(this.timeout);
13762     
13763         this.hoverState = 'out'
13764     
13765         if (!this.delay || !this.delay.hide) {
13766             this.hide();
13767             return 
13768         }
13769         var _t = this;
13770         this.timeout = setTimeout(function () {
13771             if (_t.hoverState == 'out') {
13772                 _t.hide();
13773             }
13774         }, this.delay.hide)
13775     },
13776     
13777     show : function (on_el)
13778     {
13779         if (!on_el) {
13780             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13781         }
13782         // set content.
13783         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13784         if (this.html !== false) {
13785             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13786         }
13787         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13788         if (!this.title.length) {
13789             this.el.select('.popover-title',true).hide();
13790         }
13791         
13792         var placement = typeof this.placement == 'function' ?
13793             this.placement.call(this, this.el, on_el) :
13794             this.placement;
13795             
13796         var autoToken = /\s?auto?\s?/i;
13797         var autoPlace = autoToken.test(placement);
13798         if (autoPlace) {
13799             placement = placement.replace(autoToken, '') || 'top';
13800         }
13801         
13802         //this.el.detach()
13803         //this.el.setXY([0,0]);
13804         this.el.show();
13805         this.el.dom.style.display='block';
13806         this.el.addClass(placement);
13807         
13808         //this.el.appendTo(on_el);
13809         
13810         var p = this.getPosition();
13811         var box = this.el.getBox();
13812         
13813         if (autoPlace) {
13814             // fixme..
13815         }
13816         var align = Roo.bootstrap.Popover.alignment[placement]
13817         this.el.alignTo(on_el, align[0],align[1]);
13818         //var arrow = this.el.select('.arrow',true).first();
13819         //arrow.set(align[2], 
13820         
13821         this.el.addClass('in');
13822         this.hoverState = null;
13823         
13824         if (this.el.hasClass('fade')) {
13825             // fade it?
13826         }
13827         
13828     },
13829     hide : function()
13830     {
13831         this.el.setXY([0,0]);
13832         this.el.removeClass('in');
13833         this.el.hide();
13834         
13835     }
13836     
13837 });
13838
13839 Roo.bootstrap.Popover.alignment = {
13840     'left' : ['r-l', [-10,0], 'right'],
13841     'right' : ['l-r', [10,0], 'left'],
13842     'bottom' : ['t-b', [0,10], 'top'],
13843     'top' : [ 'b-t', [0,-10], 'bottom']
13844 };
13845
13846  /*
13847  * - LGPL
13848  *
13849  * Progress
13850  * 
13851  */
13852
13853 /**
13854  * @class Roo.bootstrap.Progress
13855  * @extends Roo.bootstrap.Component
13856  * Bootstrap Progress class
13857  * @cfg {Boolean} striped striped of the progress bar
13858  * @cfg {Boolean} active animated of the progress bar
13859  * 
13860  * 
13861  * @constructor
13862  * Create a new Progress
13863  * @param {Object} config The config object
13864  */
13865
13866 Roo.bootstrap.Progress = function(config){
13867     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13868 };
13869
13870 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13871     
13872     striped : false,
13873     active: false,
13874     
13875     getAutoCreate : function(){
13876         var cfg = {
13877             tag: 'div',
13878             cls: 'progress'
13879         };
13880         
13881         
13882         if(this.striped){
13883             cfg.cls += ' progress-striped';
13884         }
13885       
13886         if(this.active){
13887             cfg.cls += ' active';
13888         }
13889         
13890         
13891         return cfg;
13892     }
13893    
13894 });
13895
13896  
13897
13898  /*
13899  * - LGPL
13900  *
13901  * ProgressBar
13902  * 
13903  */
13904
13905 /**
13906  * @class Roo.bootstrap.ProgressBar
13907  * @extends Roo.bootstrap.Component
13908  * Bootstrap ProgressBar class
13909  * @cfg {Number} aria_valuenow aria-value now
13910  * @cfg {Number} aria_valuemin aria-value min
13911  * @cfg {Number} aria_valuemax aria-value max
13912  * @cfg {String} label label for the progress bar
13913  * @cfg {String} panel (success | info | warning | danger )
13914  * @cfg {String} role role of the progress bar
13915  * @cfg {String} sr_only text
13916  * 
13917  * 
13918  * @constructor
13919  * Create a new ProgressBar
13920  * @param {Object} config The config object
13921  */
13922
13923 Roo.bootstrap.ProgressBar = function(config){
13924     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13925 };
13926
13927 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13928     
13929     aria_valuenow : 0,
13930     aria_valuemin : 0,
13931     aria_valuemax : 100,
13932     label : false,
13933     panel : false,
13934     role : false,
13935     sr_only: false,
13936     
13937     getAutoCreate : function()
13938     {
13939         
13940         var cfg = {
13941             tag: 'div',
13942             cls: 'progress-bar',
13943             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13944         };
13945         
13946         if(this.sr_only){
13947             cfg.cn = {
13948                 tag: 'span',
13949                 cls: 'sr-only',
13950                 html: this.sr_only
13951             }
13952         }
13953         
13954         if(this.role){
13955             cfg.role = this.role;
13956         }
13957         
13958         if(this.aria_valuenow){
13959             cfg['aria-valuenow'] = this.aria_valuenow;
13960         }
13961         
13962         if(this.aria_valuemin){
13963             cfg['aria-valuemin'] = this.aria_valuemin;
13964         }
13965         
13966         if(this.aria_valuemax){
13967             cfg['aria-valuemax'] = this.aria_valuemax;
13968         }
13969         
13970         if(this.label && !this.sr_only){
13971             cfg.html = this.label;
13972         }
13973         
13974         if(this.panel){
13975             cfg.cls += ' progress-bar-' + this.panel;
13976         }
13977         
13978         return cfg;
13979     },
13980     
13981     update : function(aria_valuenow)
13982     {
13983         this.aria_valuenow = aria_valuenow;
13984         
13985         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13986     }
13987    
13988 });
13989
13990  
13991
13992  /*
13993  * - LGPL
13994  *
13995  * column
13996  * 
13997  */
13998
13999 /**
14000  * @class Roo.bootstrap.TabGroup
14001  * @extends Roo.bootstrap.Column
14002  * Bootstrap Column class
14003  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14004  * @cfg {Boolean} carousel true to make the group behave like a carousel
14005  * 
14006  * @constructor
14007  * Create a new TabGroup
14008  * @param {Object} config The config object
14009  */
14010
14011 Roo.bootstrap.TabGroup = function(config){
14012     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14013     if (!this.navId) {
14014         this.navId = Roo.id();
14015     }
14016     this.tabs = [];
14017     Roo.bootstrap.TabGroup.register(this);
14018     
14019 };
14020
14021 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14022     
14023     carousel : false,
14024     transition : false,
14025      
14026     getAutoCreate : function()
14027     {
14028         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14029         
14030         cfg.cls += ' tab-content';
14031         
14032         if (this.carousel) {
14033             cfg.cls += ' carousel slide';
14034             cfg.cn = [{
14035                cls : 'carousel-inner'
14036             }]
14037         }
14038         
14039         
14040         return cfg;
14041     },
14042     getChildContainer : function()
14043     {
14044         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14045     },
14046     
14047     /**
14048     * register a Navigation item
14049     * @param {Roo.bootstrap.NavItem} the navitem to add
14050     */
14051     register : function(item)
14052     {
14053         this.tabs.push( item);
14054         item.navId = this.navId; // not really needed..
14055     
14056     },
14057     
14058     getActivePanel : function()
14059     {
14060         var r = false;
14061         Roo.each(this.tabs, function(t) {
14062             if (t.active) {
14063                 r = t;
14064                 return false;
14065             }
14066             return null;
14067         });
14068         return r;
14069         
14070     },
14071     getPanelByName : function(n)
14072     {
14073         var r = false;
14074         Roo.each(this.tabs, function(t) {
14075             if (t.tabId == n) {
14076                 r = t;
14077                 return false;
14078             }
14079             return null;
14080         });
14081         return r;
14082     },
14083     indexOfPanel : function(p)
14084     {
14085         var r = false;
14086         Roo.each(this.tabs, function(t,i) {
14087             if (t.tabId == p.tabId) {
14088                 r = i;
14089                 return false;
14090             }
14091             return null;
14092         });
14093         return r;
14094     },
14095     /**
14096      * show a specific panel
14097      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14098      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14099      */
14100     showPanel : function (pan)
14101     {
14102         
14103         if (typeof(pan) == 'number') {
14104             pan = this.tabs[pan];
14105         }
14106         if (typeof(pan) == 'string') {
14107             pan = this.getPanelByName(pan);
14108         }
14109         if (pan.tabId == this.getActivePanel().tabId) {
14110             return true;
14111         }
14112         var cur = this.getActivePanel();
14113         
14114         if (false === cur.fireEvent('beforedeactivate')) {
14115             return false;
14116         }
14117         
14118         if (this.carousel) {
14119             this.transition = true;
14120             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14121             var lr = dir == 'next' ? 'left' : 'right';
14122             pan.el.addClass(dir); // or prev
14123             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14124             cur.el.addClass(lr); // or right
14125             pan.el.addClass(lr);
14126             
14127             var _this = this;
14128             cur.el.on('transitionend', function() {
14129                 Roo.log("trans end?");
14130                 
14131                 pan.el.removeClass([lr,dir]);
14132                 pan.setActive(true);
14133                 
14134                 cur.el.removeClass([lr]);
14135                 cur.setActive(false);
14136                 
14137                 _this.transition = false;
14138                 
14139             }, this, { single:  true } );
14140             return true;
14141         }
14142         
14143         cur.setActive(false);
14144         pan.setActive(true);
14145         return true;
14146         
14147     },
14148     showPanelNext : function()
14149     {
14150         var i = this.indexOfPanel(this.getActivePanel());
14151         if (i > this.tabs.length) {
14152             return;
14153         }
14154         this.showPanel(this.tabs[i+1]);
14155     },
14156     showPanelPrev : function()
14157     {
14158         var i = this.indexOfPanel(this.getActivePanel());
14159         if (i  < 1) {
14160             return;
14161         }
14162         this.showPanel(this.tabs[i-1]);
14163     }
14164     
14165     
14166   
14167 });
14168
14169  
14170
14171  
14172  
14173 Roo.apply(Roo.bootstrap.TabGroup, {
14174     
14175     groups: {},
14176      /**
14177     * register a Navigation Group
14178     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14179     */
14180     register : function(navgrp)
14181     {
14182         this.groups[navgrp.navId] = navgrp;
14183         
14184     },
14185     /**
14186     * fetch a Navigation Group based on the navigation ID
14187     * if one does not exist , it will get created.
14188     * @param {string} the navgroup to add
14189     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14190     */
14191     get: function(navId) {
14192         if (typeof(this.groups[navId]) == 'undefined') {
14193             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14194         }
14195         return this.groups[navId] ;
14196     }
14197     
14198     
14199     
14200 });
14201
14202  /*
14203  * - LGPL
14204  *
14205  * TabPanel
14206  * 
14207  */
14208
14209 /**
14210  * @class Roo.bootstrap.TabPanel
14211  * @extends Roo.bootstrap.Component
14212  * Bootstrap TabPanel class
14213  * @cfg {Boolean} active panel active
14214  * @cfg {String} html panel content
14215  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14216  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14217  * 
14218  * 
14219  * @constructor
14220  * Create a new TabPanel
14221  * @param {Object} config The config object
14222  */
14223
14224 Roo.bootstrap.TabPanel = function(config){
14225     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14226     this.addEvents({
14227         /**
14228              * @event changed
14229              * Fires when the active status changes
14230              * @param {Roo.bootstrap.TabPanel} this
14231              * @param {Boolean} state the new state
14232             
14233          */
14234         'changed': true,
14235         /**
14236              * @event beforedeactivate
14237              * Fires before a tab is de-activated - can be used to do validation on a form.
14238              * @param {Roo.bootstrap.TabPanel} this
14239              * @return {Boolean} false if there is an error
14240             
14241          */
14242         'beforedeactivate': true
14243      });
14244     
14245     this.tabId = this.tabId || Roo.id();
14246   
14247 };
14248
14249 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14250     
14251     active: false,
14252     html: false,
14253     tabId: false,
14254     navId : false,
14255     
14256     getAutoCreate : function(){
14257         var cfg = {
14258             tag: 'div',
14259             // item is needed for carousel - not sure if it has any effect otherwise
14260             cls: 'tab-pane item',
14261             html: this.html || ''
14262         };
14263         
14264         if(this.active){
14265             cfg.cls += ' active';
14266         }
14267         
14268         if(this.tabId){
14269             cfg.tabId = this.tabId;
14270         }
14271         
14272         
14273         return cfg;
14274     },
14275     
14276     initEvents:  function()
14277     {
14278         Roo.log('-------- init events on tab panel ---------');
14279         
14280         var p = this.parent();
14281         this.navId = this.navId || p.navId;
14282         
14283         if (typeof(this.navId) != 'undefined') {
14284             // not really needed.. but just in case.. parent should be a NavGroup.
14285             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14286             Roo.log(['register', tg, this]);
14287             tg.register(this);
14288         }
14289     },
14290     
14291     
14292     onRender : function(ct, position)
14293     {
14294        // Roo.log("Call onRender: " + this.xtype);
14295         
14296         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14297         
14298         
14299         
14300         
14301         
14302     },
14303     
14304     setActive: function(state)
14305     {
14306         Roo.log("panel - set active " + this.tabId + "=" + state);
14307         
14308         this.active = state;
14309         if (!state) {
14310             this.el.removeClass('active');
14311             
14312         } else  if (!this.el.hasClass('active')) {
14313             this.el.addClass('active');
14314         }
14315         this.fireEvent('changed', this, state);
14316     }
14317     
14318     
14319 });
14320  
14321
14322  
14323
14324  /*
14325  * - LGPL
14326  *
14327  * DateField
14328  * 
14329  */
14330
14331 /**
14332  * @class Roo.bootstrap.DateField
14333  * @extends Roo.bootstrap.Input
14334  * Bootstrap DateField class
14335  * @cfg {Number} weekStart default 0
14336  * @cfg {Number} weekStart default 0
14337  * @cfg {Number} viewMode default empty, (months|years)
14338  * @cfg {Number} minViewMode default empty, (months|years)
14339  * @cfg {Number} startDate default -Infinity
14340  * @cfg {Number} endDate default Infinity
14341  * @cfg {Boolean} todayHighlight default false
14342  * @cfg {Boolean} todayBtn default false
14343  * @cfg {Boolean} calendarWeeks default false
14344  * @cfg {Object} daysOfWeekDisabled default empty
14345  * 
14346  * @cfg {Boolean} keyboardNavigation default true
14347  * @cfg {String} language default en
14348  * 
14349  * @constructor
14350  * Create a new DateField
14351  * @param {Object} config The config object
14352  */
14353
14354 Roo.bootstrap.DateField = function(config){
14355     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14356      this.addEvents({
14357             /**
14358              * @event show
14359              * Fires when this field show.
14360              * @param {Roo.bootstrap.DateField} this
14361              * @param {Mixed} date The date value
14362              */
14363             show : true,
14364             /**
14365              * @event show
14366              * Fires when this field hide.
14367              * @param {Roo.bootstrap.DateField} this
14368              * @param {Mixed} date The date value
14369              */
14370             hide : true,
14371             /**
14372              * @event select
14373              * Fires when select a date.
14374              * @param {Roo.bootstrap.DateField} this
14375              * @param {Mixed} date The date value
14376              */
14377             select : true
14378         });
14379 };
14380
14381 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14382     
14383     /**
14384      * @cfg {String} format
14385      * The default date format string which can be overriden for localization support.  The format must be
14386      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14387      */
14388     format : "m/d/y",
14389     /**
14390      * @cfg {String} altFormats
14391      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14392      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14393      */
14394     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14395     
14396     weekStart : 0,
14397     
14398     viewMode : '',
14399     
14400     minViewMode : '',
14401     
14402     todayHighlight : false,
14403     
14404     todayBtn: false,
14405     
14406     language: 'en',
14407     
14408     keyboardNavigation: true,
14409     
14410     calendarWeeks: false,
14411     
14412     startDate: -Infinity,
14413     
14414     endDate: Infinity,
14415     
14416     daysOfWeekDisabled: [],
14417     
14418     _events: [],
14419     
14420     UTCDate: function()
14421     {
14422         return new Date(Date.UTC.apply(Date, arguments));
14423     },
14424     
14425     UTCToday: function()
14426     {
14427         var today = new Date();
14428         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14429     },
14430     
14431     getDate: function() {
14432             var d = this.getUTCDate();
14433             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14434     },
14435     
14436     getUTCDate: function() {
14437             return this.date;
14438     },
14439     
14440     setDate: function(d) {
14441             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14442     },
14443     
14444     setUTCDate: function(d) {
14445             this.date = d;
14446             this.setValue(this.formatDate(this.date));
14447     },
14448         
14449     onRender: function(ct, position)
14450     {
14451         
14452         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14453         
14454         this.language = this.language || 'en';
14455         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14456         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14457         
14458         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14459         this.format = this.format || 'm/d/y';
14460         this.isInline = false;
14461         this.isInput = true;
14462         this.component = this.el.select('.add-on', true).first() || false;
14463         this.component = (this.component && this.component.length === 0) ? false : this.component;
14464         this.hasInput = this.component && this.inputEL().length;
14465         
14466         if (typeof(this.minViewMode === 'string')) {
14467             switch (this.minViewMode) {
14468                 case 'months':
14469                     this.minViewMode = 1;
14470                     break;
14471                 case 'years':
14472                     this.minViewMode = 2;
14473                     break;
14474                 default:
14475                     this.minViewMode = 0;
14476                     break;
14477             }
14478         }
14479         
14480         if (typeof(this.viewMode === 'string')) {
14481             switch (this.viewMode) {
14482                 case 'months':
14483                     this.viewMode = 1;
14484                     break;
14485                 case 'years':
14486                     this.viewMode = 2;
14487                     break;
14488                 default:
14489                     this.viewMode = 0;
14490                     break;
14491             }
14492         }
14493                 
14494         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14495         
14496 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14497         
14498         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14499         
14500         this.picker().on('mousedown', this.onMousedown, this);
14501         this.picker().on('click', this.onClick, this);
14502         
14503         this.picker().addClass('datepicker-dropdown');
14504         
14505         this.startViewMode = this.viewMode;
14506         
14507         
14508         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14509             if(!this.calendarWeeks){
14510                 v.remove();
14511                 return;
14512             };
14513             
14514             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14515             v.attr('colspan', function(i, val){
14516                 return parseInt(val) + 1;
14517             });
14518         })
14519                         
14520         
14521         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14522         
14523         this.setStartDate(this.startDate);
14524         this.setEndDate(this.endDate);
14525         
14526         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14527         
14528         this.fillDow();
14529         this.fillMonths();
14530         this.update();
14531         this.showMode();
14532         
14533         if(this.isInline) {
14534             this.show();
14535         }
14536     },
14537     
14538     picker : function()
14539     {
14540         return this.pickerEl;
14541 //        return this.el.select('.datepicker', true).first();
14542     },
14543     
14544     fillDow: function()
14545     {
14546         var dowCnt = this.weekStart;
14547         
14548         var dow = {
14549             tag: 'tr',
14550             cn: [
14551                 
14552             ]
14553         };
14554         
14555         if(this.calendarWeeks){
14556             dow.cn.push({
14557                 tag: 'th',
14558                 cls: 'cw',
14559                 html: '&nbsp;'
14560             })
14561         }
14562         
14563         while (dowCnt < this.weekStart + 7) {
14564             dow.cn.push({
14565                 tag: 'th',
14566                 cls: 'dow',
14567                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14568             });
14569         }
14570         
14571         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14572     },
14573     
14574     fillMonths: function()
14575     {    
14576         var i = 0
14577         var months = this.picker().select('>.datepicker-months td', true).first();
14578         
14579         months.dom.innerHTML = '';
14580         
14581         while (i < 12) {
14582             var month = {
14583                 tag: 'span',
14584                 cls: 'month',
14585                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14586             }
14587             
14588             months.createChild(month);
14589         }
14590         
14591     },
14592     
14593     update: function()
14594     {
14595         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;
14596         
14597         if (this.date < this.startDate) {
14598             this.viewDate = new Date(this.startDate);
14599         } else if (this.date > this.endDate) {
14600             this.viewDate = new Date(this.endDate);
14601         } else {
14602             this.viewDate = new Date(this.date);
14603         }
14604         
14605         this.fill();
14606     },
14607     
14608     fill: function() 
14609     {
14610         var d = new Date(this.viewDate),
14611                 year = d.getUTCFullYear(),
14612                 month = d.getUTCMonth(),
14613                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14614                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14615                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14616                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14617                 currentDate = this.date && this.date.valueOf(),
14618                 today = this.UTCToday();
14619         
14620         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14621         
14622 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14623         
14624 //        this.picker.select('>tfoot th.today').
14625 //                                              .text(dates[this.language].today)
14626 //                                              .toggle(this.todayBtn !== false);
14627     
14628         this.updateNavArrows();
14629         this.fillMonths();
14630                                                 
14631         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14632         
14633         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14634          
14635         prevMonth.setUTCDate(day);
14636         
14637         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14638         
14639         var nextMonth = new Date(prevMonth);
14640         
14641         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14642         
14643         nextMonth = nextMonth.valueOf();
14644         
14645         var fillMonths = false;
14646         
14647         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14648         
14649         while(prevMonth.valueOf() < nextMonth) {
14650             var clsName = '';
14651             
14652             if (prevMonth.getUTCDay() === this.weekStart) {
14653                 if(fillMonths){
14654                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14655                 }
14656                     
14657                 fillMonths = {
14658                     tag: 'tr',
14659                     cn: []
14660                 };
14661                 
14662                 if(this.calendarWeeks){
14663                     // ISO 8601: First week contains first thursday.
14664                     // ISO also states week starts on Monday, but we can be more abstract here.
14665                     var
14666                     // Start of current week: based on weekstart/current date
14667                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14668                     // Thursday of this week
14669                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14670                     // First Thursday of year, year from thursday
14671                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14672                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14673                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14674                     
14675                     fillMonths.cn.push({
14676                         tag: 'td',
14677                         cls: 'cw',
14678                         html: calWeek
14679                     });
14680                 }
14681             }
14682             
14683             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14684                 clsName += ' old';
14685             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14686                 clsName += ' new';
14687             }
14688             if (this.todayHighlight &&
14689                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14690                 prevMonth.getUTCMonth() == today.getMonth() &&
14691                 prevMonth.getUTCDate() == today.getDate()) {
14692                 clsName += ' today';
14693             }
14694             
14695             if (currentDate && prevMonth.valueOf() === currentDate) {
14696                 clsName += ' active';
14697             }
14698             
14699             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14700                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14701                     clsName += ' disabled';
14702             }
14703             
14704             fillMonths.cn.push({
14705                 tag: 'td',
14706                 cls: 'day ' + clsName,
14707                 html: prevMonth.getDate()
14708             })
14709             
14710             prevMonth.setDate(prevMonth.getDate()+1);
14711         }
14712           
14713         var currentYear = this.date && this.date.getUTCFullYear();
14714         var currentMonth = this.date && this.date.getUTCMonth();
14715         
14716         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14717         
14718         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14719             v.removeClass('active');
14720             
14721             if(currentYear === year && k === currentMonth){
14722                 v.addClass('active');
14723             }
14724             
14725             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14726                 v.addClass('disabled');
14727             }
14728             
14729         });
14730         
14731         
14732         year = parseInt(year/10, 10) * 10;
14733         
14734         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14735         
14736         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14737         
14738         year -= 1;
14739         for (var i = -1; i < 11; i++) {
14740             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14741                 tag: 'span',
14742                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14743                 html: year
14744             })
14745             
14746             year += 1;
14747         }
14748     },
14749     
14750     showMode: function(dir) 
14751     {
14752         if (dir) {
14753             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14754         }
14755         Roo.each(this.picker().select('>div',true).elements, function(v){
14756             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14757             v.hide();
14758         });
14759         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14760     },
14761     
14762     place: function()
14763     {
14764         if(this.isInline) return;
14765         
14766         this.picker().removeClass(['bottom', 'top']);
14767         
14768         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14769             /*
14770              * place to the top of element!
14771              *
14772              */
14773             
14774             this.picker().addClass('top');
14775             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14776             
14777             return;
14778         }
14779         
14780         this.picker().addClass('bottom');
14781         
14782         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14783     },
14784     
14785     parseDate : function(value)
14786     {
14787         if(!value || value instanceof Date){
14788             return value;
14789         }
14790         var v = Date.parseDate(value, this.format);
14791         if (!v && this.useIso) {
14792             v = Date.parseDate(value, 'Y-m-d');
14793         }
14794         if(!v && this.altFormats){
14795             if(!this.altFormatsArray){
14796                 this.altFormatsArray = this.altFormats.split("|");
14797             }
14798             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14799                 v = Date.parseDate(value, this.altFormatsArray[i]);
14800             }
14801         }
14802         return v;
14803     },
14804     
14805     formatDate : function(date, fmt)
14806     {
14807         return (!date || !(date instanceof Date)) ?
14808         date : date.dateFormat(fmt || this.format);
14809     },
14810     
14811     onFocus : function()
14812     {
14813         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14814         this.show();
14815     },
14816     
14817     onBlur : function()
14818     {
14819         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14820         
14821         var d = this.inputEl().getValue();
14822         
14823         this.setValue(d);
14824                 
14825         this.hide();
14826     },
14827     
14828     show : function()
14829     {
14830         this.picker().show();
14831         this.update();
14832         this.place();
14833         
14834         this.fireEvent('show', this, this.date);
14835     },
14836     
14837     hide : function()
14838     {
14839         if(this.isInline) return;
14840         this.picker().hide();
14841         this.viewMode = this.startViewMode;
14842         this.showMode();
14843         
14844         this.fireEvent('hide', this, this.date);
14845         
14846     },
14847     
14848     onMousedown: function(e)
14849     {
14850         e.stopPropagation();
14851         e.preventDefault();
14852     },
14853     
14854     keyup: function(e)
14855     {
14856         Roo.bootstrap.DateField.superclass.keyup.call(this);
14857         this.update();
14858     },
14859
14860     setValue: function(v)
14861     {
14862         var d = new Date(v).clearTime();
14863         
14864         if(isNaN(d.getTime())){
14865             this.date = this.viewDate = '';
14866             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14867             return;
14868         }
14869         
14870         v = this.formatDate(d);
14871         
14872         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14873         
14874         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14875      
14876         this.update();
14877
14878         this.fireEvent('select', this, this.date);
14879         
14880     },
14881     
14882     getValue: function()
14883     {
14884         return this.formatDate(this.date);
14885     },
14886     
14887     fireKey: function(e)
14888     {
14889         if (!this.picker().isVisible()){
14890             if (e.keyCode == 27) // allow escape to hide and re-show picker
14891                 this.show();
14892             return;
14893         }
14894         
14895         var dateChanged = false,
14896         dir, day, month,
14897         newDate, newViewDate;
14898         
14899         switch(e.keyCode){
14900             case 27: // escape
14901                 this.hide();
14902                 e.preventDefault();
14903                 break;
14904             case 37: // left
14905             case 39: // right
14906                 if (!this.keyboardNavigation) break;
14907                 dir = e.keyCode == 37 ? -1 : 1;
14908                 
14909                 if (e.ctrlKey){
14910                     newDate = this.moveYear(this.date, dir);
14911                     newViewDate = this.moveYear(this.viewDate, dir);
14912                 } else if (e.shiftKey){
14913                     newDate = this.moveMonth(this.date, dir);
14914                     newViewDate = this.moveMonth(this.viewDate, dir);
14915                 } else {
14916                     newDate = new Date(this.date);
14917                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14918                     newViewDate = new Date(this.viewDate);
14919                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14920                 }
14921                 if (this.dateWithinRange(newDate)){
14922                     this.date = newDate;
14923                     this.viewDate = newViewDate;
14924                     this.setValue(this.formatDate(this.date));
14925 //                    this.update();
14926                     e.preventDefault();
14927                     dateChanged = true;
14928                 }
14929                 break;
14930             case 38: // up
14931             case 40: // down
14932                 if (!this.keyboardNavigation) break;
14933                 dir = e.keyCode == 38 ? -1 : 1;
14934                 if (e.ctrlKey){
14935                     newDate = this.moveYear(this.date, dir);
14936                     newViewDate = this.moveYear(this.viewDate, dir);
14937                 } else if (e.shiftKey){
14938                     newDate = this.moveMonth(this.date, dir);
14939                     newViewDate = this.moveMonth(this.viewDate, dir);
14940                 } else {
14941                     newDate = new Date(this.date);
14942                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14943                     newViewDate = new Date(this.viewDate);
14944                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14945                 }
14946                 if (this.dateWithinRange(newDate)){
14947                     this.date = newDate;
14948                     this.viewDate = newViewDate;
14949                     this.setValue(this.formatDate(this.date));
14950 //                    this.update();
14951                     e.preventDefault();
14952                     dateChanged = true;
14953                 }
14954                 break;
14955             case 13: // enter
14956                 this.setValue(this.formatDate(this.date));
14957                 this.hide();
14958                 e.preventDefault();
14959                 break;
14960             case 9: // tab
14961                 this.setValue(this.formatDate(this.date));
14962                 this.hide();
14963                 break;
14964             case 16: // shift
14965             case 17: // ctrl
14966             case 18: // alt
14967                 break;
14968             default :
14969                 this.hide();
14970                 
14971         }
14972     },
14973     
14974     
14975     onClick: function(e) 
14976     {
14977         e.stopPropagation();
14978         e.preventDefault();
14979         
14980         var target = e.getTarget();
14981         
14982         if(target.nodeName.toLowerCase() === 'i'){
14983             target = Roo.get(target).dom.parentNode;
14984         }
14985         
14986         var nodeName = target.nodeName;
14987         var className = target.className;
14988         var html = target.innerHTML;
14989         
14990         switch(nodeName.toLowerCase()) {
14991             case 'th':
14992                 switch(className) {
14993                     case 'switch':
14994                         this.showMode(1);
14995                         break;
14996                     case 'prev':
14997                     case 'next':
14998                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14999                         switch(this.viewMode){
15000                                 case 0:
15001                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15002                                         break;
15003                                 case 1:
15004                                 case 2:
15005                                         this.viewDate = this.moveYear(this.viewDate, dir);
15006                                         break;
15007                         }
15008                         this.fill();
15009                         break;
15010                     case 'today':
15011                         var date = new Date();
15012                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15013 //                        this.fill()
15014                         this.setValue(this.formatDate(this.date));
15015                         
15016                         this.hide();
15017                         break;
15018                 }
15019                 break;
15020             case 'span':
15021                 if (className.indexOf('disabled') === -1) {
15022                     this.viewDate.setUTCDate(1);
15023                     if (className.indexOf('month') !== -1) {
15024                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15025                     } else {
15026                         var year = parseInt(html, 10) || 0;
15027                         this.viewDate.setUTCFullYear(year);
15028                         
15029                     }
15030                     this.showMode(-1);
15031                     this.fill();
15032                 }
15033                 break;
15034                 
15035             case 'td':
15036                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15037                     var day = parseInt(html, 10) || 1;
15038                     var year = this.viewDate.getUTCFullYear(),
15039                         month = this.viewDate.getUTCMonth();
15040
15041                     if (className.indexOf('old') !== -1) {
15042                         if(month === 0 ){
15043                             month = 11;
15044                             year -= 1;
15045                         }else{
15046                             month -= 1;
15047                         }
15048                     } else if (className.indexOf('new') !== -1) {
15049                         if (month == 11) {
15050                             month = 0;
15051                             year += 1;
15052                         } else {
15053                             month += 1;
15054                         }
15055                     }
15056                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15057                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15058 //                    this.fill();
15059                     this.setValue(this.formatDate(this.date));
15060                     this.hide();
15061                 }
15062                 break;
15063         }
15064     },
15065     
15066     setStartDate: function(startDate)
15067     {
15068         this.startDate = startDate || -Infinity;
15069         if (this.startDate !== -Infinity) {
15070             this.startDate = this.parseDate(this.startDate);
15071         }
15072         this.update();
15073         this.updateNavArrows();
15074     },
15075
15076     setEndDate: function(endDate)
15077     {
15078         this.endDate = endDate || Infinity;
15079         if (this.endDate !== Infinity) {
15080             this.endDate = this.parseDate(this.endDate);
15081         }
15082         this.update();
15083         this.updateNavArrows();
15084     },
15085     
15086     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15087     {
15088         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15089         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15090             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15091         }
15092         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15093             return parseInt(d, 10);
15094         });
15095         this.update();
15096         this.updateNavArrows();
15097     },
15098     
15099     updateNavArrows: function() 
15100     {
15101         var d = new Date(this.viewDate),
15102         year = d.getUTCFullYear(),
15103         month = d.getUTCMonth();
15104         
15105         Roo.each(this.picker().select('.prev', true).elements, function(v){
15106             v.show();
15107             switch (this.viewMode) {
15108                 case 0:
15109
15110                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15111                         v.hide();
15112                     }
15113                     break;
15114                 case 1:
15115                 case 2:
15116                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15117                         v.hide();
15118                     }
15119                     break;
15120             }
15121         });
15122         
15123         Roo.each(this.picker().select('.next', true).elements, function(v){
15124             v.show();
15125             switch (this.viewMode) {
15126                 case 0:
15127
15128                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15129                         v.hide();
15130                     }
15131                     break;
15132                 case 1:
15133                 case 2:
15134                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15135                         v.hide();
15136                     }
15137                     break;
15138             }
15139         })
15140     },
15141     
15142     moveMonth: function(date, dir)
15143     {
15144         if (!dir) return date;
15145         var new_date = new Date(date.valueOf()),
15146         day = new_date.getUTCDate(),
15147         month = new_date.getUTCMonth(),
15148         mag = Math.abs(dir),
15149         new_month, test;
15150         dir = dir > 0 ? 1 : -1;
15151         if (mag == 1){
15152             test = dir == -1
15153             // If going back one month, make sure month is not current month
15154             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15155             ? function(){
15156                 return new_date.getUTCMonth() == month;
15157             }
15158             // If going forward one month, make sure month is as expected
15159             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15160             : function(){
15161                 return new_date.getUTCMonth() != new_month;
15162             };
15163             new_month = month + dir;
15164             new_date.setUTCMonth(new_month);
15165             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15166             if (new_month < 0 || new_month > 11)
15167                 new_month = (new_month + 12) % 12;
15168         } else {
15169             // For magnitudes >1, move one month at a time...
15170             for (var i=0; i<mag; i++)
15171                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15172                 new_date = this.moveMonth(new_date, dir);
15173             // ...then reset the day, keeping it in the new month
15174             new_month = new_date.getUTCMonth();
15175             new_date.setUTCDate(day);
15176             test = function(){
15177                 return new_month != new_date.getUTCMonth();
15178             };
15179         }
15180         // Common date-resetting loop -- if date is beyond end of month, make it
15181         // end of month
15182         while (test()){
15183             new_date.setUTCDate(--day);
15184             new_date.setUTCMonth(new_month);
15185         }
15186         return new_date;
15187     },
15188
15189     moveYear: function(date, dir)
15190     {
15191         return this.moveMonth(date, dir*12);
15192     },
15193
15194     dateWithinRange: function(date)
15195     {
15196         return date >= this.startDate && date <= this.endDate;
15197     },
15198
15199     
15200     remove: function() 
15201     {
15202         this.picker().remove();
15203     }
15204    
15205 });
15206
15207 Roo.apply(Roo.bootstrap.DateField,  {
15208     
15209     head : {
15210         tag: 'thead',
15211         cn: [
15212         {
15213             tag: 'tr',
15214             cn: [
15215             {
15216                 tag: 'th',
15217                 cls: 'prev',
15218                 html: '<i class="fa fa-arrow-left"/>'
15219             },
15220             {
15221                 tag: 'th',
15222                 cls: 'switch',
15223                 colspan: '5'
15224             },
15225             {
15226                 tag: 'th',
15227                 cls: 'next',
15228                 html: '<i class="fa fa-arrow-right"/>'
15229             }
15230
15231             ]
15232         }
15233         ]
15234     },
15235     
15236     content : {
15237         tag: 'tbody',
15238         cn: [
15239         {
15240             tag: 'tr',
15241             cn: [
15242             {
15243                 tag: 'td',
15244                 colspan: '7'
15245             }
15246             ]
15247         }
15248         ]
15249     },
15250     
15251     footer : {
15252         tag: 'tfoot',
15253         cn: [
15254         {
15255             tag: 'tr',
15256             cn: [
15257             {
15258                 tag: 'th',
15259                 colspan: '7',
15260                 cls: 'today'
15261             }
15262                     
15263             ]
15264         }
15265         ]
15266     },
15267     
15268     dates:{
15269         en: {
15270             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15271             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15272             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15273             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15274             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15275             today: "Today"
15276         }
15277     },
15278     
15279     modes: [
15280     {
15281         clsName: 'days',
15282         navFnc: 'Month',
15283         navStep: 1
15284     },
15285     {
15286         clsName: 'months',
15287         navFnc: 'FullYear',
15288         navStep: 1
15289     },
15290     {
15291         clsName: 'years',
15292         navFnc: 'FullYear',
15293         navStep: 10
15294     }]
15295 });
15296
15297 Roo.apply(Roo.bootstrap.DateField,  {
15298   
15299     template : {
15300         tag: 'div',
15301         cls: 'datepicker dropdown-menu',
15302         cn: [
15303         {
15304             tag: 'div',
15305             cls: 'datepicker-days',
15306             cn: [
15307             {
15308                 tag: 'table',
15309                 cls: 'table-condensed',
15310                 cn:[
15311                 Roo.bootstrap.DateField.head,
15312                 {
15313                     tag: 'tbody'
15314                 },
15315                 Roo.bootstrap.DateField.footer
15316                 ]
15317             }
15318             ]
15319         },
15320         {
15321             tag: 'div',
15322             cls: 'datepicker-months',
15323             cn: [
15324             {
15325                 tag: 'table',
15326                 cls: 'table-condensed',
15327                 cn:[
15328                 Roo.bootstrap.DateField.head,
15329                 Roo.bootstrap.DateField.content,
15330                 Roo.bootstrap.DateField.footer
15331                 ]
15332             }
15333             ]
15334         },
15335         {
15336             tag: 'div',
15337             cls: 'datepicker-years',
15338             cn: [
15339             {
15340                 tag: 'table',
15341                 cls: 'table-condensed',
15342                 cn:[
15343                 Roo.bootstrap.DateField.head,
15344                 Roo.bootstrap.DateField.content,
15345                 Roo.bootstrap.DateField.footer
15346                 ]
15347             }
15348             ]
15349         }
15350         ]
15351     }
15352 });
15353
15354  
15355
15356  /*
15357  * - LGPL
15358  *
15359  * TimeField
15360  * 
15361  */
15362
15363 /**
15364  * @class Roo.bootstrap.TimeField
15365  * @extends Roo.bootstrap.Input
15366  * Bootstrap DateField class
15367  * 
15368  * 
15369  * @constructor
15370  * Create a new TimeField
15371  * @param {Object} config The config object
15372  */
15373
15374 Roo.bootstrap.TimeField = function(config){
15375     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15376     this.addEvents({
15377             /**
15378              * @event show
15379              * Fires when this field show.
15380              * @param {Roo.bootstrap.DateField} this
15381              * @param {Mixed} date The date value
15382              */
15383             show : true,
15384             /**
15385              * @event show
15386              * Fires when this field hide.
15387              * @param {Roo.bootstrap.DateField} this
15388              * @param {Mixed} date The date value
15389              */
15390             hide : true,
15391             /**
15392              * @event select
15393              * Fires when select a date.
15394              * @param {Roo.bootstrap.DateField} this
15395              * @param {Mixed} date The date value
15396              */
15397             select : true
15398         });
15399 };
15400
15401 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15402     
15403     /**
15404      * @cfg {String} format
15405      * The default time format string which can be overriden for localization support.  The format must be
15406      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15407      */
15408     format : "H:i",
15409        
15410     onRender: function(ct, position)
15411     {
15412         
15413         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15414                 
15415         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15416         
15417         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15418         
15419         this.pop = this.picker().select('>.datepicker-time',true).first();
15420         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15421         
15422         this.picker().on('mousedown', this.onMousedown, this);
15423         this.picker().on('click', this.onClick, this);
15424         
15425         this.picker().addClass('datepicker-dropdown');
15426     
15427         this.fillTime();
15428         this.update();
15429             
15430         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15431         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15432         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15433         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15434         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15435         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15436
15437     },
15438     
15439     fireKey: function(e){
15440         if (!this.picker().isVisible()){
15441             if (e.keyCode == 27) // allow escape to hide and re-show picker
15442                 this.show();
15443             return;
15444         }
15445
15446         e.preventDefault();
15447         
15448         switch(e.keyCode){
15449             case 27: // escape
15450                 this.hide();
15451                 break;
15452             case 37: // left
15453             case 39: // right
15454                 this.onTogglePeriod();
15455                 break;
15456             case 38: // up
15457                 this.onIncrementMinutes();
15458                 break;
15459             case 40: // down
15460                 this.onDecrementMinutes();
15461                 break;
15462             case 13: // enter
15463             case 9: // tab
15464                 this.setTime();
15465                 break;
15466         }
15467     },
15468     
15469     onClick: function(e) {
15470         e.stopPropagation();
15471         e.preventDefault();
15472     },
15473     
15474     picker : function()
15475     {
15476         return this.el.select('.datepicker', true).first();
15477     },
15478     
15479     fillTime: function()
15480     {    
15481         var time = this.pop.select('tbody', true).first();
15482         
15483         time.dom.innerHTML = '';
15484         
15485         time.createChild({
15486             tag: 'tr',
15487             cn: [
15488                 {
15489                     tag: 'td',
15490                     cn: [
15491                         {
15492                             tag: 'a',
15493                             href: '#',
15494                             cls: 'btn',
15495                             cn: [
15496                                 {
15497                                     tag: 'span',
15498                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15499                                 }
15500                             ]
15501                         } 
15502                     ]
15503                 },
15504                 {
15505                     tag: 'td',
15506                     cls: 'separator'
15507                 },
15508                 {
15509                     tag: 'td',
15510                     cn: [
15511                         {
15512                             tag: 'a',
15513                             href: '#',
15514                             cls: 'btn',
15515                             cn: [
15516                                 {
15517                                     tag: 'span',
15518                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15519                                 }
15520                             ]
15521                         }
15522                     ]
15523                 },
15524                 {
15525                     tag: 'td',
15526                     cls: 'separator'
15527                 }
15528             ]
15529         });
15530         
15531         time.createChild({
15532             tag: 'tr',
15533             cn: [
15534                 {
15535                     tag: 'td',
15536                     cn: [
15537                         {
15538                             tag: 'span',
15539                             cls: 'timepicker-hour',
15540                             html: '00'
15541                         }  
15542                     ]
15543                 },
15544                 {
15545                     tag: 'td',
15546                     cls: 'separator',
15547                     html: ':'
15548                 },
15549                 {
15550                     tag: 'td',
15551                     cn: [
15552                         {
15553                             tag: 'span',
15554                             cls: 'timepicker-minute',
15555                             html: '00'
15556                         }  
15557                     ]
15558                 },
15559                 {
15560                     tag: 'td',
15561                     cls: 'separator'
15562                 },
15563                 {
15564                     tag: 'td',
15565                     cn: [
15566                         {
15567                             tag: 'button',
15568                             type: 'button',
15569                             cls: 'btn btn-primary period',
15570                             html: 'AM'
15571                             
15572                         }
15573                     ]
15574                 }
15575             ]
15576         });
15577         
15578         time.createChild({
15579             tag: 'tr',
15580             cn: [
15581                 {
15582                     tag: 'td',
15583                     cn: [
15584                         {
15585                             tag: 'a',
15586                             href: '#',
15587                             cls: 'btn',
15588                             cn: [
15589                                 {
15590                                     tag: 'span',
15591                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15592                                 }
15593                             ]
15594                         }
15595                     ]
15596                 },
15597                 {
15598                     tag: 'td',
15599                     cls: 'separator'
15600                 },
15601                 {
15602                     tag: 'td',
15603                     cn: [
15604                         {
15605                             tag: 'a',
15606                             href: '#',
15607                             cls: 'btn',
15608                             cn: [
15609                                 {
15610                                     tag: 'span',
15611                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15612                                 }
15613                             ]
15614                         }
15615                     ]
15616                 },
15617                 {
15618                     tag: 'td',
15619                     cls: 'separator'
15620                 }
15621             ]
15622         });
15623         
15624     },
15625     
15626     update: function()
15627     {
15628         
15629         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15630         
15631         this.fill();
15632     },
15633     
15634     fill: function() 
15635     {
15636         var hours = this.time.getHours();
15637         var minutes = this.time.getMinutes();
15638         var period = 'AM';
15639         
15640         if(hours > 11){
15641             period = 'PM';
15642         }
15643         
15644         if(hours == 0){
15645             hours = 12;
15646         }
15647         
15648         
15649         if(hours > 12){
15650             hours = hours - 12;
15651         }
15652         
15653         if(hours < 10){
15654             hours = '0' + hours;
15655         }
15656         
15657         if(minutes < 10){
15658             minutes = '0' + minutes;
15659         }
15660         
15661         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15662         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15663         this.pop.select('button', true).first().dom.innerHTML = period;
15664         
15665     },
15666     
15667     place: function()
15668     {   
15669         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15670         
15671         var cls = ['bottom'];
15672         
15673         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15674             cls.pop();
15675             cls.push('top');
15676         }
15677         
15678         cls.push('right');
15679         
15680         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15681             cls.pop();
15682             cls.push('left');
15683         }
15684         
15685         this.picker().addClass(cls.join('-'));
15686         
15687         var _this = this;
15688         
15689         Roo.each(cls, function(c){
15690             if(c == 'bottom'){
15691                 _this.picker().setTop(_this.inputEl().getHeight());
15692                 return;
15693             }
15694             if(c == 'top'){
15695                 _this.picker().setTop(0 - _this.picker().getHeight());
15696                 return;
15697             }
15698             
15699             if(c == 'left'){
15700                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15701                 return;
15702             }
15703             if(c == 'right'){
15704                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15705                 return;
15706             }
15707         });
15708         
15709     },
15710   
15711     onFocus : function()
15712     {
15713         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15714         this.show();
15715     },
15716     
15717     onBlur : function()
15718     {
15719         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15720         this.hide();
15721     },
15722     
15723     show : function()
15724     {
15725         this.picker().show();
15726         this.pop.show();
15727         this.update();
15728         this.place();
15729         
15730         this.fireEvent('show', this, this.date);
15731     },
15732     
15733     hide : function()
15734     {
15735         this.picker().hide();
15736         this.pop.hide();
15737         
15738         this.fireEvent('hide', this, this.date);
15739     },
15740     
15741     setTime : function()
15742     {
15743         this.hide();
15744         this.setValue(this.time.format(this.format));
15745         
15746         this.fireEvent('select', this, this.date);
15747         
15748         
15749     },
15750     
15751     onMousedown: function(e){
15752         e.stopPropagation();
15753         e.preventDefault();
15754     },
15755     
15756     onIncrementHours: function()
15757     {
15758         Roo.log('onIncrementHours');
15759         this.time = this.time.add(Date.HOUR, 1);
15760         this.update();
15761         
15762     },
15763     
15764     onDecrementHours: function()
15765     {
15766         Roo.log('onDecrementHours');
15767         this.time = this.time.add(Date.HOUR, -1);
15768         this.update();
15769     },
15770     
15771     onIncrementMinutes: function()
15772     {
15773         Roo.log('onIncrementMinutes');
15774         this.time = this.time.add(Date.MINUTE, 1);
15775         this.update();
15776     },
15777     
15778     onDecrementMinutes: function()
15779     {
15780         Roo.log('onDecrementMinutes');
15781         this.time = this.time.add(Date.MINUTE, -1);
15782         this.update();
15783     },
15784     
15785     onTogglePeriod: function()
15786     {
15787         Roo.log('onTogglePeriod');
15788         this.time = this.time.add(Date.HOUR, 12);
15789         this.update();
15790     }
15791     
15792    
15793 });
15794
15795 Roo.apply(Roo.bootstrap.TimeField,  {
15796     
15797     content : {
15798         tag: 'tbody',
15799         cn: [
15800             {
15801                 tag: 'tr',
15802                 cn: [
15803                 {
15804                     tag: 'td',
15805                     colspan: '7'
15806                 }
15807                 ]
15808             }
15809         ]
15810     },
15811     
15812     footer : {
15813         tag: 'tfoot',
15814         cn: [
15815             {
15816                 tag: 'tr',
15817                 cn: [
15818                 {
15819                     tag: 'th',
15820                     colspan: '7',
15821                     cls: '',
15822                     cn: [
15823                         {
15824                             tag: 'button',
15825                             cls: 'btn btn-info ok',
15826                             html: 'OK'
15827                         }
15828                     ]
15829                 }
15830
15831                 ]
15832             }
15833         ]
15834     }
15835 });
15836
15837 Roo.apply(Roo.bootstrap.TimeField,  {
15838   
15839     template : {
15840         tag: 'div',
15841         cls: 'datepicker dropdown-menu',
15842         cn: [
15843             {
15844                 tag: 'div',
15845                 cls: 'datepicker-time',
15846                 cn: [
15847                 {
15848                     tag: 'table',
15849                     cls: 'table-condensed',
15850                     cn:[
15851                     Roo.bootstrap.TimeField.content,
15852                     Roo.bootstrap.TimeField.footer
15853                     ]
15854                 }
15855                 ]
15856             }
15857         ]
15858     }
15859 });
15860
15861  
15862
15863  /*
15864  * - LGPL
15865  *
15866  * CheckBox
15867  * 
15868  */
15869
15870 /**
15871  * @class Roo.bootstrap.CheckBox
15872  * @extends Roo.bootstrap.Input
15873  * Bootstrap CheckBox class
15874  * 
15875  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15876  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15877  * @cfg {String} boxLabel The text that appears beside the checkbox
15878  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15879  * @cfg {Boolean} checked initnal the element
15880  * 
15881  * 
15882  * @constructor
15883  * Create a new CheckBox
15884  * @param {Object} config The config object
15885  */
15886
15887 Roo.bootstrap.CheckBox = function(config){
15888     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15889    
15890         this.addEvents({
15891             /**
15892             * @event check
15893             * Fires when the element is checked or unchecked.
15894             * @param {Roo.bootstrap.CheckBox} this This input
15895             * @param {Boolean} checked The new checked value
15896             */
15897            check : true
15898         });
15899 };
15900
15901 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15902     
15903     inputType: 'checkbox',
15904     inputValue: 1,
15905     valueOff: 0,
15906     boxLabel: false,
15907     checked: false,
15908     weight : false,
15909     
15910     getAutoCreate : function()
15911     {
15912         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15913         
15914         var id = Roo.id();
15915         
15916         var cfg = {};
15917         
15918         cfg.cls = 'form-group checkbox' //input-group
15919         
15920         
15921         
15922         
15923         var input =  {
15924             tag: 'input',
15925             id : id,
15926             type : this.inputType,
15927             value : (!this.checked) ? this.valueOff : this.inputValue,
15928             cls : 'roo-checkbox', //'form-box',
15929             placeholder : this.placeholder || ''
15930             
15931         };
15932         
15933         if (this.weight) { // Validity check?
15934             cfg.cls += " checkbox-" + this.weight;
15935         }
15936         
15937         if (this.disabled) {
15938             input.disabled=true;
15939         }
15940         
15941         if(this.checked){
15942             input.checked = this.checked;
15943         }
15944         
15945         if (this.name) {
15946             input.name = this.name;
15947         }
15948         
15949         if (this.size) {
15950             input.cls += ' input-' + this.size;
15951         }
15952         
15953         var settings=this;
15954         ['xs','sm','md','lg'].map(function(size){
15955             if (settings[size]) {
15956                 cfg.cls += ' col-' + size + '-' + settings[size];
15957             }
15958         });
15959         
15960        
15961         
15962         var inputblock = input;
15963         
15964         
15965         
15966         
15967         if (this.before || this.after) {
15968             
15969             inputblock = {
15970                 cls : 'input-group',
15971                 cn :  [] 
15972             };
15973             if (this.before) {
15974                 inputblock.cn.push({
15975                     tag :'span',
15976                     cls : 'input-group-addon',
15977                     html : this.before
15978                 });
15979             }
15980             inputblock.cn.push(input);
15981             if (this.after) {
15982                 inputblock.cn.push({
15983                     tag :'span',
15984                     cls : 'input-group-addon',
15985                     html : this.after
15986                 });
15987             }
15988             
15989         };
15990         
15991         if (align ==='left' && this.fieldLabel.length) {
15992                 Roo.log("left and has label");
15993                 cfg.cn = [
15994                     
15995                     {
15996                         tag: 'label',
15997                         'for' :  id,
15998                         cls : 'control-label col-md-' + this.labelWidth,
15999                         html : this.fieldLabel
16000                         
16001                     },
16002                     {
16003                         cls : "col-md-" + (12 - this.labelWidth), 
16004                         cn: [
16005                             inputblock
16006                         ]
16007                     }
16008                     
16009                 ];
16010         } else if ( this.fieldLabel.length) {
16011                 Roo.log(" label");
16012                 cfg.cn = [
16013                    
16014                     {
16015                         tag: this.boxLabel ? 'span' : 'label',
16016                         'for': id,
16017                         cls: 'control-label box-input-label',
16018                         //cls : 'input-group-addon',
16019                         html : this.fieldLabel
16020                         
16021                     },
16022                     
16023                     inputblock
16024                     
16025                 ];
16026
16027         } else {
16028             
16029                 Roo.log(" no label && no align");
16030                 cfg.cn = [  inputblock ] ;
16031                 
16032                 
16033         };
16034          if(this.boxLabel){
16035             cfg.cn.push( {
16036                 tag: 'label',
16037                 'for': id,
16038                 cls: 'box-label',
16039                 html: this.boxLabel
16040                 
16041             });
16042         }
16043         
16044         
16045        
16046         return cfg;
16047         
16048     },
16049     
16050     /**
16051      * return the real input element.
16052      */
16053     inputEl: function ()
16054     {
16055         return this.el.select('input.roo-checkbox',true).first();
16056     },
16057     
16058     label: function()
16059     {
16060         return this.el.select('label.control-label',true).first();
16061     },
16062     
16063     initEvents : function()
16064     {
16065 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16066         
16067         this.inputEl().on('click', this.onClick,  this);
16068         
16069     },
16070     
16071     onClick : function()
16072     {   
16073         this.setChecked(!this.checked);
16074     },
16075     
16076     setChecked : function(state,suppressEvent)
16077     {
16078         this.checked = state;
16079         
16080         this.inputEl().dom.checked = state;
16081         
16082         if(suppressEvent !== true){
16083             this.fireEvent('check', this, state);
16084         }
16085         
16086         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16087         
16088     },
16089     
16090     setValue : function(v,suppressEvent)
16091     {
16092         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16093     }
16094     
16095 });
16096
16097  
16098 /*
16099  * - LGPL
16100  *
16101  * Radio
16102  * 
16103  */
16104
16105 /**
16106  * @class Roo.bootstrap.Radio
16107  * @extends Roo.bootstrap.CheckBox
16108  * Bootstrap Radio class
16109
16110  * @constructor
16111  * Create a new Radio
16112  * @param {Object} config The config object
16113  */
16114
16115 Roo.bootstrap.Radio = function(config){
16116     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16117    
16118 };
16119
16120 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16121     
16122     inputType: 'radio',
16123     inputValue: '',
16124     valueOff: '',
16125     
16126     getAutoCreate : function()
16127     {
16128         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16129         
16130         var id = Roo.id();
16131         
16132         var cfg = {};
16133         
16134         cfg.cls = 'form-group radio' //input-group
16135         
16136         var input =  {
16137             tag: 'input',
16138             id : id,
16139             type : this.inputType,
16140             value : (!this.checked) ? this.valueOff : this.inputValue,
16141             cls : 'roo-radio',
16142             placeholder : this.placeholder || ''
16143             
16144         };
16145           if (this.weight) { // Validity check?
16146             cfg.cls += " radio-" + this.weight;
16147         }
16148         if (this.disabled) {
16149             input.disabled=true;
16150         }
16151         
16152         if(this.checked){
16153             input.checked = this.checked;
16154         }
16155         
16156         if (this.name) {
16157             input.name = this.name;
16158         }
16159         
16160         if (this.size) {
16161             input.cls += ' input-' + this.size;
16162         }
16163         
16164         var settings=this;
16165         ['xs','sm','md','lg'].map(function(size){
16166             if (settings[size]) {
16167                 cfg.cls += ' col-' + size + '-' + settings[size];
16168             }
16169         });
16170         
16171         var inputblock = input;
16172         
16173         if (this.before || this.after) {
16174             
16175             inputblock = {
16176                 cls : 'input-group',
16177                 cn :  [] 
16178             };
16179             if (this.before) {
16180                 inputblock.cn.push({
16181                     tag :'span',
16182                     cls : 'input-group-addon',
16183                     html : this.before
16184                 });
16185             }
16186             inputblock.cn.push(input);
16187             if (this.after) {
16188                 inputblock.cn.push({
16189                     tag :'span',
16190                     cls : 'input-group-addon',
16191                     html : this.after
16192                 });
16193             }
16194             
16195         };
16196         
16197         if (align ==='left' && this.fieldLabel.length) {
16198                 Roo.log("left and has label");
16199                 cfg.cn = [
16200                     
16201                     {
16202                         tag: 'label',
16203                         'for' :  id,
16204                         cls : 'control-label col-md-' + this.labelWidth,
16205                         html : this.fieldLabel
16206                         
16207                     },
16208                     {
16209                         cls : "col-md-" + (12 - this.labelWidth), 
16210                         cn: [
16211                             inputblock
16212                         ]
16213                     }
16214                     
16215                 ];
16216         } else if ( this.fieldLabel.length) {
16217                 Roo.log(" label");
16218                  cfg.cn = [
16219                    
16220                     {
16221                         tag: 'label',
16222                         'for': id,
16223                         cls: 'control-label box-input-label',
16224                         //cls : 'input-group-addon',
16225                         html : this.fieldLabel
16226                         
16227                     },
16228                     
16229                     inputblock
16230                     
16231                 ];
16232
16233         } else {
16234             
16235                    Roo.log(" no label && no align");
16236                 cfg.cn = [
16237                     
16238                         inputblock
16239                     
16240                 ];
16241                 
16242                 
16243         };
16244         
16245         if(this.boxLabel){
16246             cfg.cn.push({
16247                 tag: 'label',
16248                 'for': id,
16249                 cls: 'box-label',
16250                 html: this.boxLabel
16251             })
16252         }
16253         
16254         return cfg;
16255         
16256     },
16257     inputEl: function ()
16258     {
16259         return this.el.select('input.roo-radio',true).first();
16260     },
16261     onClick : function()
16262     {   
16263         this.setChecked(true);
16264     },
16265     
16266     setChecked : function(state,suppressEvent)
16267     {
16268         if(state){
16269             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16270                 v.dom.checked = false;
16271             });
16272         }
16273         
16274         this.checked = state;
16275         this.inputEl().dom.checked = state;
16276         
16277         if(suppressEvent !== true){
16278             this.fireEvent('check', this, state);
16279         }
16280         
16281         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16282         
16283     },
16284     
16285     getGroupValue : function()
16286     {
16287         var value = ''
16288         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16289             if(v.dom.checked == true){
16290                 value = v.dom.value;
16291             }
16292         });
16293         
16294         return value;
16295     },
16296     
16297     /**
16298      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16299      * @return {Mixed} value The field value
16300      */
16301     getValue : function(){
16302         return this.getGroupValue();
16303     }
16304     
16305 });
16306
16307  
16308 //<script type="text/javascript">
16309
16310 /*
16311  * Based  Ext JS Library 1.1.1
16312  * Copyright(c) 2006-2007, Ext JS, LLC.
16313  * LGPL
16314  *
16315  */
16316  
16317 /**
16318  * @class Roo.HtmlEditorCore
16319  * @extends Roo.Component
16320  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16321  *
16322  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16323  */
16324
16325 Roo.HtmlEditorCore = function(config){
16326     
16327     
16328     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16329     
16330     
16331     this.addEvents({
16332         /**
16333          * @event initialize
16334          * Fires when the editor is fully initialized (including the iframe)
16335          * @param {Roo.HtmlEditorCore} this
16336          */
16337         initialize: true,
16338         /**
16339          * @event activate
16340          * Fires when the editor is first receives the focus. Any insertion must wait
16341          * until after this event.
16342          * @param {Roo.HtmlEditorCore} this
16343          */
16344         activate: true,
16345          /**
16346          * @event beforesync
16347          * Fires before the textarea is updated with content from the editor iframe. Return false
16348          * to cancel the sync.
16349          * @param {Roo.HtmlEditorCore} this
16350          * @param {String} html
16351          */
16352         beforesync: true,
16353          /**
16354          * @event beforepush
16355          * Fires before the iframe editor is updated with content from the textarea. Return false
16356          * to cancel the push.
16357          * @param {Roo.HtmlEditorCore} this
16358          * @param {String} html
16359          */
16360         beforepush: true,
16361          /**
16362          * @event sync
16363          * Fires when the textarea is updated with content from the editor iframe.
16364          * @param {Roo.HtmlEditorCore} this
16365          * @param {String} html
16366          */
16367         sync: true,
16368          /**
16369          * @event push
16370          * Fires when the iframe editor is updated with content from the textarea.
16371          * @param {Roo.HtmlEditorCore} this
16372          * @param {String} html
16373          */
16374         push: true,
16375         
16376         /**
16377          * @event editorevent
16378          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16379          * @param {Roo.HtmlEditorCore} this
16380          */
16381         editorevent: true
16382     });
16383     
16384     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16385     
16386     // defaults : white / black...
16387     this.applyBlacklists();
16388     
16389     
16390     
16391 };
16392
16393
16394 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16395
16396
16397      /**
16398      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16399      */
16400     
16401     owner : false,
16402     
16403      /**
16404      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16405      *                        Roo.resizable.
16406      */
16407     resizable : false,
16408      /**
16409      * @cfg {Number} height (in pixels)
16410      */   
16411     height: 300,
16412    /**
16413      * @cfg {Number} width (in pixels)
16414      */   
16415     width: 500,
16416     
16417     /**
16418      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16419      * 
16420      */
16421     stylesheets: false,
16422     
16423     // id of frame..
16424     frameId: false,
16425     
16426     // private properties
16427     validationEvent : false,
16428     deferHeight: true,
16429     initialized : false,
16430     activated : false,
16431     sourceEditMode : false,
16432     onFocus : Roo.emptyFn,
16433     iframePad:3,
16434     hideMode:'offsets',
16435     
16436     clearUp: true,
16437     
16438     // blacklist + whitelisted elements..
16439     black: false,
16440     white: false,
16441      
16442     
16443
16444     /**
16445      * Protected method that will not generally be called directly. It
16446      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16447      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16448      */
16449     getDocMarkup : function(){
16450         // body styles..
16451         var st = '';
16452         Roo.log(this.stylesheets);
16453         
16454         // inherit styels from page...?? 
16455         if (this.stylesheets === false) {
16456             
16457             Roo.get(document.head).select('style').each(function(node) {
16458                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16459             });
16460             
16461             Roo.get(document.head).select('link').each(function(node) { 
16462                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16463             });
16464             
16465         } else if (!this.stylesheets.length) {
16466                 // simple..
16467                 st = '<style type="text/css">' +
16468                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16469                    '</style>';
16470         } else {
16471             Roo.each(this.stylesheets, function(s) {
16472                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16473             });
16474             
16475         }
16476         
16477         st +=  '<style type="text/css">' +
16478             'IMG { cursor: pointer } ' +
16479         '</style>';
16480
16481         
16482         return '<html><head>' + st  +
16483             //<style type="text/css">' +
16484             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16485             //'</style>' +
16486             ' </head><body class="roo-htmleditor-body"></body></html>';
16487     },
16488
16489     // private
16490     onRender : function(ct, position)
16491     {
16492         var _t = this;
16493         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16494         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16495         
16496         
16497         this.el.dom.style.border = '0 none';
16498         this.el.dom.setAttribute('tabIndex', -1);
16499         this.el.addClass('x-hidden hide');
16500         
16501         
16502         
16503         if(Roo.isIE){ // fix IE 1px bogus margin
16504             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16505         }
16506        
16507         
16508         this.frameId = Roo.id();
16509         
16510          
16511         
16512         var iframe = this.owner.wrap.createChild({
16513             tag: 'iframe',
16514             cls: 'form-control', // bootstrap..
16515             id: this.frameId,
16516             name: this.frameId,
16517             frameBorder : 'no',
16518             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16519         }, this.el
16520         );
16521         
16522         
16523         this.iframe = iframe.dom;
16524
16525          this.assignDocWin();
16526         
16527         this.doc.designMode = 'on';
16528        
16529         this.doc.open();
16530         this.doc.write(this.getDocMarkup());
16531         this.doc.close();
16532
16533         
16534         var task = { // must defer to wait for browser to be ready
16535             run : function(){
16536                 //console.log("run task?" + this.doc.readyState);
16537                 this.assignDocWin();
16538                 if(this.doc.body || this.doc.readyState == 'complete'){
16539                     try {
16540                         this.doc.designMode="on";
16541                     } catch (e) {
16542                         return;
16543                     }
16544                     Roo.TaskMgr.stop(task);
16545                     this.initEditor.defer(10, this);
16546                 }
16547             },
16548             interval : 10,
16549             duration: 10000,
16550             scope: this
16551         };
16552         Roo.TaskMgr.start(task);
16553
16554         
16555          
16556     },
16557
16558     // private
16559     onResize : function(w, h)
16560     {
16561          Roo.log('resize: ' +w + ',' + h );
16562         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16563         if(!this.iframe){
16564             return;
16565         }
16566         if(typeof w == 'number'){
16567             
16568             this.iframe.style.width = w + 'px';
16569         }
16570         if(typeof h == 'number'){
16571             
16572             this.iframe.style.height = h + 'px';
16573             if(this.doc){
16574                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16575             }
16576         }
16577         
16578     },
16579
16580     /**
16581      * Toggles the editor between standard and source edit mode.
16582      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16583      */
16584     toggleSourceEdit : function(sourceEditMode){
16585         
16586         this.sourceEditMode = sourceEditMode === true;
16587         
16588         if(this.sourceEditMode){
16589  
16590             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16591             
16592         }else{
16593             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16594             //this.iframe.className = '';
16595             this.deferFocus();
16596         }
16597         //this.setSize(this.owner.wrap.getSize());
16598         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16599     },
16600
16601     
16602   
16603
16604     /**
16605      * Protected method that will not generally be called directly. If you need/want
16606      * custom HTML cleanup, this is the method you should override.
16607      * @param {String} html The HTML to be cleaned
16608      * return {String} The cleaned HTML
16609      */
16610     cleanHtml : function(html){
16611         html = String(html);
16612         if(html.length > 5){
16613             if(Roo.isSafari){ // strip safari nonsense
16614                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16615             }
16616         }
16617         if(html == '&nbsp;'){
16618             html = '';
16619         }
16620         return html;
16621     },
16622
16623     /**
16624      * HTML Editor -> Textarea
16625      * Protected method that will not generally be called directly. Syncs the contents
16626      * of the editor iframe with the textarea.
16627      */
16628     syncValue : function(){
16629         if(this.initialized){
16630             var bd = (this.doc.body || this.doc.documentElement);
16631             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16632             var html = bd.innerHTML;
16633             if(Roo.isSafari){
16634                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16635                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16636                 if(m && m[1]){
16637                     html = '<div style="'+m[0]+'">' + html + '</div>';
16638                 }
16639             }
16640             html = this.cleanHtml(html);
16641             // fix up the special chars.. normaly like back quotes in word...
16642             // however we do not want to do this with chinese..
16643             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16644                 var cc = b.charCodeAt();
16645                 if (
16646                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16647                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16648                     (cc >= 0xf900 && cc < 0xfb00 )
16649                 ) {
16650                         return b;
16651                 }
16652                 return "&#"+cc+";" 
16653             });
16654             if(this.owner.fireEvent('beforesync', this, html) !== false){
16655                 this.el.dom.value = html;
16656                 this.owner.fireEvent('sync', this, html);
16657             }
16658         }
16659     },
16660
16661     /**
16662      * Protected method that will not generally be called directly. Pushes the value of the textarea
16663      * into the iframe editor.
16664      */
16665     pushValue : function(){
16666         if(this.initialized){
16667             var v = this.el.dom.value.trim();
16668             
16669 //            if(v.length < 1){
16670 //                v = '&#160;';
16671 //            }
16672             
16673             if(this.owner.fireEvent('beforepush', this, v) !== false){
16674                 var d = (this.doc.body || this.doc.documentElement);
16675                 d.innerHTML = v;
16676                 this.cleanUpPaste();
16677                 this.el.dom.value = d.innerHTML;
16678                 this.owner.fireEvent('push', this, v);
16679             }
16680         }
16681     },
16682
16683     // private
16684     deferFocus : function(){
16685         this.focus.defer(10, this);
16686     },
16687
16688     // doc'ed in Field
16689     focus : function(){
16690         if(this.win && !this.sourceEditMode){
16691             this.win.focus();
16692         }else{
16693             this.el.focus();
16694         }
16695     },
16696     
16697     assignDocWin: function()
16698     {
16699         var iframe = this.iframe;
16700         
16701          if(Roo.isIE){
16702             this.doc = iframe.contentWindow.document;
16703             this.win = iframe.contentWindow;
16704         } else {
16705 //            if (!Roo.get(this.frameId)) {
16706 //                return;
16707 //            }
16708 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16709 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16710             
16711             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16712                 return;
16713             }
16714             
16715             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16716             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16717         }
16718     },
16719     
16720     // private
16721     initEditor : function(){
16722         //console.log("INIT EDITOR");
16723         this.assignDocWin();
16724         
16725         
16726         
16727         this.doc.designMode="on";
16728         this.doc.open();
16729         this.doc.write(this.getDocMarkup());
16730         this.doc.close();
16731         
16732         var dbody = (this.doc.body || this.doc.documentElement);
16733         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16734         // this copies styles from the containing element into thsi one..
16735         // not sure why we need all of this..
16736         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16737         
16738         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16739         //ss['background-attachment'] = 'fixed'; // w3c
16740         dbody.bgProperties = 'fixed'; // ie
16741         //Roo.DomHelper.applyStyles(dbody, ss);
16742         Roo.EventManager.on(this.doc, {
16743             //'mousedown': this.onEditorEvent,
16744             'mouseup': this.onEditorEvent,
16745             'dblclick': this.onEditorEvent,
16746             'click': this.onEditorEvent,
16747             'keyup': this.onEditorEvent,
16748             buffer:100,
16749             scope: this
16750         });
16751         if(Roo.isGecko){
16752             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16753         }
16754         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16755             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16756         }
16757         this.initialized = true;
16758
16759         this.owner.fireEvent('initialize', this);
16760         this.pushValue();
16761     },
16762
16763     // private
16764     onDestroy : function(){
16765         
16766         
16767         
16768         if(this.rendered){
16769             
16770             //for (var i =0; i < this.toolbars.length;i++) {
16771             //    // fixme - ask toolbars for heights?
16772             //    this.toolbars[i].onDestroy();
16773            // }
16774             
16775             //this.wrap.dom.innerHTML = '';
16776             //this.wrap.remove();
16777         }
16778     },
16779
16780     // private
16781     onFirstFocus : function(){
16782         
16783         this.assignDocWin();
16784         
16785         
16786         this.activated = true;
16787          
16788     
16789         if(Roo.isGecko){ // prevent silly gecko errors
16790             this.win.focus();
16791             var s = this.win.getSelection();
16792             if(!s.focusNode || s.focusNode.nodeType != 3){
16793                 var r = s.getRangeAt(0);
16794                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16795                 r.collapse(true);
16796                 this.deferFocus();
16797             }
16798             try{
16799                 this.execCmd('useCSS', true);
16800                 this.execCmd('styleWithCSS', false);
16801             }catch(e){}
16802         }
16803         this.owner.fireEvent('activate', this);
16804     },
16805
16806     // private
16807     adjustFont: function(btn){
16808         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16809         //if(Roo.isSafari){ // safari
16810         //    adjust *= 2;
16811        // }
16812         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16813         if(Roo.isSafari){ // safari
16814             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16815             v =  (v < 10) ? 10 : v;
16816             v =  (v > 48) ? 48 : v;
16817             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16818             
16819         }
16820         
16821         
16822         v = Math.max(1, v+adjust);
16823         
16824         this.execCmd('FontSize', v  );
16825     },
16826
16827     onEditorEvent : function(e){
16828         this.owner.fireEvent('editorevent', this, e);
16829       //  this.updateToolbar();
16830         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16831     },
16832
16833     insertTag : function(tg)
16834     {
16835         // could be a bit smarter... -> wrap the current selected tRoo..
16836         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16837             
16838             range = this.createRange(this.getSelection());
16839             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16840             wrappingNode.appendChild(range.extractContents());
16841             range.insertNode(wrappingNode);
16842
16843             return;
16844             
16845             
16846             
16847         }
16848         this.execCmd("formatblock",   tg);
16849         
16850     },
16851     
16852     insertText : function(txt)
16853     {
16854         
16855         
16856         var range = this.createRange();
16857         range.deleteContents();
16858                //alert(Sender.getAttribute('label'));
16859                
16860         range.insertNode(this.doc.createTextNode(txt));
16861     } ,
16862     
16863      
16864
16865     /**
16866      * Executes a Midas editor command on the editor document and performs necessary focus and
16867      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16868      * @param {String} cmd The Midas command
16869      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16870      */
16871     relayCmd : function(cmd, value){
16872         this.win.focus();
16873         this.execCmd(cmd, value);
16874         this.owner.fireEvent('editorevent', this);
16875         //this.updateToolbar();
16876         this.owner.deferFocus();
16877     },
16878
16879     /**
16880      * Executes a Midas editor command directly on the editor document.
16881      * For visual commands, you should use {@link #relayCmd} instead.
16882      * <b>This should only be called after the editor is initialized.</b>
16883      * @param {String} cmd The Midas command
16884      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16885      */
16886     execCmd : function(cmd, value){
16887         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16888         this.syncValue();
16889     },
16890  
16891  
16892    
16893     /**
16894      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16895      * to insert tRoo.
16896      * @param {String} text | dom node.. 
16897      */
16898     insertAtCursor : function(text)
16899     {
16900         
16901         
16902         
16903         if(!this.activated){
16904             return;
16905         }
16906         /*
16907         if(Roo.isIE){
16908             this.win.focus();
16909             var r = this.doc.selection.createRange();
16910             if(r){
16911                 r.collapse(true);
16912                 r.pasteHTML(text);
16913                 this.syncValue();
16914                 this.deferFocus();
16915             
16916             }
16917             return;
16918         }
16919         */
16920         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16921             this.win.focus();
16922             
16923             
16924             // from jquery ui (MIT licenced)
16925             var range, node;
16926             var win = this.win;
16927             
16928             if (win.getSelection && win.getSelection().getRangeAt) {
16929                 range = win.getSelection().getRangeAt(0);
16930                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16931                 range.insertNode(node);
16932             } else if (win.document.selection && win.document.selection.createRange) {
16933                 // no firefox support
16934                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16935                 win.document.selection.createRange().pasteHTML(txt);
16936             } else {
16937                 // no firefox support
16938                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16939                 this.execCmd('InsertHTML', txt);
16940             } 
16941             
16942             this.syncValue();
16943             
16944             this.deferFocus();
16945         }
16946     },
16947  // private
16948     mozKeyPress : function(e){
16949         if(e.ctrlKey){
16950             var c = e.getCharCode(), cmd;
16951           
16952             if(c > 0){
16953                 c = String.fromCharCode(c).toLowerCase();
16954                 switch(c){
16955                     case 'b':
16956                         cmd = 'bold';
16957                         break;
16958                     case 'i':
16959                         cmd = 'italic';
16960                         break;
16961                     
16962                     case 'u':
16963                         cmd = 'underline';
16964                         break;
16965                     
16966                     case 'v':
16967                         this.cleanUpPaste.defer(100, this);
16968                         return;
16969                         
16970                 }
16971                 if(cmd){
16972                     this.win.focus();
16973                     this.execCmd(cmd);
16974                     this.deferFocus();
16975                     e.preventDefault();
16976                 }
16977                 
16978             }
16979         }
16980     },
16981
16982     // private
16983     fixKeys : function(){ // load time branching for fastest keydown performance
16984         if(Roo.isIE){
16985             return function(e){
16986                 var k = e.getKey(), r;
16987                 if(k == e.TAB){
16988                     e.stopEvent();
16989                     r = this.doc.selection.createRange();
16990                     if(r){
16991                         r.collapse(true);
16992                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16993                         this.deferFocus();
16994                     }
16995                     return;
16996                 }
16997                 
16998                 if(k == e.ENTER){
16999                     r = this.doc.selection.createRange();
17000                     if(r){
17001                         var target = r.parentElement();
17002                         if(!target || target.tagName.toLowerCase() != 'li'){
17003                             e.stopEvent();
17004                             r.pasteHTML('<br />');
17005                             r.collapse(false);
17006                             r.select();
17007                         }
17008                     }
17009                 }
17010                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17011                     this.cleanUpPaste.defer(100, this);
17012                     return;
17013                 }
17014                 
17015                 
17016             };
17017         }else if(Roo.isOpera){
17018             return function(e){
17019                 var k = e.getKey();
17020                 if(k == e.TAB){
17021                     e.stopEvent();
17022                     this.win.focus();
17023                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17024                     this.deferFocus();
17025                 }
17026                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17027                     this.cleanUpPaste.defer(100, this);
17028                     return;
17029                 }
17030                 
17031             };
17032         }else if(Roo.isSafari){
17033             return function(e){
17034                 var k = e.getKey();
17035                 
17036                 if(k == e.TAB){
17037                     e.stopEvent();
17038                     this.execCmd('InsertText','\t');
17039                     this.deferFocus();
17040                     return;
17041                 }
17042                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17043                     this.cleanUpPaste.defer(100, this);
17044                     return;
17045                 }
17046                 
17047              };
17048         }
17049     }(),
17050     
17051     getAllAncestors: function()
17052     {
17053         var p = this.getSelectedNode();
17054         var a = [];
17055         if (!p) {
17056             a.push(p); // push blank onto stack..
17057             p = this.getParentElement();
17058         }
17059         
17060         
17061         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17062             a.push(p);
17063             p = p.parentNode;
17064         }
17065         a.push(this.doc.body);
17066         return a;
17067     },
17068     lastSel : false,
17069     lastSelNode : false,
17070     
17071     
17072     getSelection : function() 
17073     {
17074         this.assignDocWin();
17075         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17076     },
17077     
17078     getSelectedNode: function() 
17079     {
17080         // this may only work on Gecko!!!
17081         
17082         // should we cache this!!!!
17083         
17084         
17085         
17086          
17087         var range = this.createRange(this.getSelection()).cloneRange();
17088         
17089         if (Roo.isIE) {
17090             var parent = range.parentElement();
17091             while (true) {
17092                 var testRange = range.duplicate();
17093                 testRange.moveToElementText(parent);
17094                 if (testRange.inRange(range)) {
17095                     break;
17096                 }
17097                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17098                     break;
17099                 }
17100                 parent = parent.parentElement;
17101             }
17102             return parent;
17103         }
17104         
17105         // is ancestor a text element.
17106         var ac =  range.commonAncestorContainer;
17107         if (ac.nodeType == 3) {
17108             ac = ac.parentNode;
17109         }
17110         
17111         var ar = ac.childNodes;
17112          
17113         var nodes = [];
17114         var other_nodes = [];
17115         var has_other_nodes = false;
17116         for (var i=0;i<ar.length;i++) {
17117             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17118                 continue;
17119             }
17120             // fullly contained node.
17121             
17122             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17123                 nodes.push(ar[i]);
17124                 continue;
17125             }
17126             
17127             // probably selected..
17128             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17129                 other_nodes.push(ar[i]);
17130                 continue;
17131             }
17132             // outer..
17133             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17134                 continue;
17135             }
17136             
17137             
17138             has_other_nodes = true;
17139         }
17140         if (!nodes.length && other_nodes.length) {
17141             nodes= other_nodes;
17142         }
17143         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17144             return false;
17145         }
17146         
17147         return nodes[0];
17148     },
17149     createRange: function(sel)
17150     {
17151         // this has strange effects when using with 
17152         // top toolbar - not sure if it's a great idea.
17153         //this.editor.contentWindow.focus();
17154         if (typeof sel != "undefined") {
17155             try {
17156                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17157             } catch(e) {
17158                 return this.doc.createRange();
17159             }
17160         } else {
17161             return this.doc.createRange();
17162         }
17163     },
17164     getParentElement: function()
17165     {
17166         
17167         this.assignDocWin();
17168         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17169         
17170         var range = this.createRange(sel);
17171          
17172         try {
17173             var p = range.commonAncestorContainer;
17174             while (p.nodeType == 3) { // text node
17175                 p = p.parentNode;
17176             }
17177             return p;
17178         } catch (e) {
17179             return null;
17180         }
17181     
17182     },
17183     /***
17184      *
17185      * Range intersection.. the hard stuff...
17186      *  '-1' = before
17187      *  '0' = hits..
17188      *  '1' = after.
17189      *         [ -- selected range --- ]
17190      *   [fail]                        [fail]
17191      *
17192      *    basically..
17193      *      if end is before start or  hits it. fail.
17194      *      if start is after end or hits it fail.
17195      *
17196      *   if either hits (but other is outside. - then it's not 
17197      *   
17198      *    
17199      **/
17200     
17201     
17202     // @see http://www.thismuchiknow.co.uk/?p=64.
17203     rangeIntersectsNode : function(range, node)
17204     {
17205         var nodeRange = node.ownerDocument.createRange();
17206         try {
17207             nodeRange.selectNode(node);
17208         } catch (e) {
17209             nodeRange.selectNodeContents(node);
17210         }
17211     
17212         var rangeStartRange = range.cloneRange();
17213         rangeStartRange.collapse(true);
17214     
17215         var rangeEndRange = range.cloneRange();
17216         rangeEndRange.collapse(false);
17217     
17218         var nodeStartRange = nodeRange.cloneRange();
17219         nodeStartRange.collapse(true);
17220     
17221         var nodeEndRange = nodeRange.cloneRange();
17222         nodeEndRange.collapse(false);
17223     
17224         return rangeStartRange.compareBoundaryPoints(
17225                  Range.START_TO_START, nodeEndRange) == -1 &&
17226                rangeEndRange.compareBoundaryPoints(
17227                  Range.START_TO_START, nodeStartRange) == 1;
17228         
17229          
17230     },
17231     rangeCompareNode : function(range, node)
17232     {
17233         var nodeRange = node.ownerDocument.createRange();
17234         try {
17235             nodeRange.selectNode(node);
17236         } catch (e) {
17237             nodeRange.selectNodeContents(node);
17238         }
17239         
17240         
17241         range.collapse(true);
17242     
17243         nodeRange.collapse(true);
17244      
17245         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17246         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17247          
17248         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17249         
17250         var nodeIsBefore   =  ss == 1;
17251         var nodeIsAfter    = ee == -1;
17252         
17253         if (nodeIsBefore && nodeIsAfter)
17254             return 0; // outer
17255         if (!nodeIsBefore && nodeIsAfter)
17256             return 1; //right trailed.
17257         
17258         if (nodeIsBefore && !nodeIsAfter)
17259             return 2;  // left trailed.
17260         // fully contined.
17261         return 3;
17262     },
17263
17264     // private? - in a new class?
17265     cleanUpPaste :  function()
17266     {
17267         // cleans up the whole document..
17268         Roo.log('cleanuppaste');
17269         
17270         this.cleanUpChildren(this.doc.body);
17271         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17272         if (clean != this.doc.body.innerHTML) {
17273             this.doc.body.innerHTML = clean;
17274         }
17275         
17276     },
17277     
17278     cleanWordChars : function(input) {// change the chars to hex code
17279         var he = Roo.HtmlEditorCore;
17280         
17281         var output = input;
17282         Roo.each(he.swapCodes, function(sw) { 
17283             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17284             
17285             output = output.replace(swapper, sw[1]);
17286         });
17287         
17288         return output;
17289     },
17290     
17291     
17292     cleanUpChildren : function (n)
17293     {
17294         if (!n.childNodes.length) {
17295             return;
17296         }
17297         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17298            this.cleanUpChild(n.childNodes[i]);
17299         }
17300     },
17301     
17302     
17303         
17304     
17305     cleanUpChild : function (node)
17306     {
17307         var ed = this;
17308         //console.log(node);
17309         if (node.nodeName == "#text") {
17310             // clean up silly Windows -- stuff?
17311             return; 
17312         }
17313         if (node.nodeName == "#comment") {
17314             node.parentNode.removeChild(node);
17315             // clean up silly Windows -- stuff?
17316             return; 
17317         }
17318         var lcname = node.tagName.toLowerCase();
17319         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17320         // whitelist of tags..
17321         
17322         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17323             // remove node.
17324             node.parentNode.removeChild(node);
17325             return;
17326             
17327         }
17328         
17329         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17330         
17331         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17332         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17333         
17334         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17335         //    remove_keep_children = true;
17336         //}
17337         
17338         if (remove_keep_children) {
17339             this.cleanUpChildren(node);
17340             // inserts everything just before this node...
17341             while (node.childNodes.length) {
17342                 var cn = node.childNodes[0];
17343                 node.removeChild(cn);
17344                 node.parentNode.insertBefore(cn, node);
17345             }
17346             node.parentNode.removeChild(node);
17347             return;
17348         }
17349         
17350         if (!node.attributes || !node.attributes.length) {
17351             this.cleanUpChildren(node);
17352             return;
17353         }
17354         
17355         function cleanAttr(n,v)
17356         {
17357             
17358             if (v.match(/^\./) || v.match(/^\//)) {
17359                 return;
17360             }
17361             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17362                 return;
17363             }
17364             if (v.match(/^#/)) {
17365                 return;
17366             }
17367 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17368             node.removeAttribute(n);
17369             
17370         }
17371         
17372         var cwhite = this.cwhite;
17373         var cblack = this.cblack;
17374             
17375         function cleanStyle(n,v)
17376         {
17377             if (v.match(/expression/)) { //XSS?? should we even bother..
17378                 node.removeAttribute(n);
17379                 return;
17380             }
17381             
17382             var parts = v.split(/;/);
17383             var clean = [];
17384             
17385             Roo.each(parts, function(p) {
17386                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17387                 if (!p.length) {
17388                     return true;
17389                 }
17390                 var l = p.split(':').shift().replace(/\s+/g,'');
17391                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17392                 
17393                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17394 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17395                     //node.removeAttribute(n);
17396                     return true;
17397                 }
17398                 //Roo.log()
17399                 // only allow 'c whitelisted system attributes'
17400                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17401 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17402                     //node.removeAttribute(n);
17403                     return true;
17404                 }
17405                 
17406                 
17407                  
17408                 
17409                 clean.push(p);
17410                 return true;
17411             });
17412             if (clean.length) { 
17413                 node.setAttribute(n, clean.join(';'));
17414             } else {
17415                 node.removeAttribute(n);
17416             }
17417             
17418         }
17419         
17420         
17421         for (var i = node.attributes.length-1; i > -1 ; i--) {
17422             var a = node.attributes[i];
17423             //console.log(a);
17424             
17425             if (a.name.toLowerCase().substr(0,2)=='on')  {
17426                 node.removeAttribute(a.name);
17427                 continue;
17428             }
17429             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17430                 node.removeAttribute(a.name);
17431                 continue;
17432             }
17433             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17434                 cleanAttr(a.name,a.value); // fixme..
17435                 continue;
17436             }
17437             if (a.name == 'style') {
17438                 cleanStyle(a.name,a.value);
17439                 continue;
17440             }
17441             /// clean up MS crap..
17442             // tecnically this should be a list of valid class'es..
17443             
17444             
17445             if (a.name == 'class') {
17446                 if (a.value.match(/^Mso/)) {
17447                     node.className = '';
17448                 }
17449                 
17450                 if (a.value.match(/body/)) {
17451                     node.className = '';
17452                 }
17453                 continue;
17454             }
17455             
17456             // style cleanup!?
17457             // class cleanup?
17458             
17459         }
17460         
17461         
17462         this.cleanUpChildren(node);
17463         
17464         
17465     },
17466     /**
17467      * Clean up MS wordisms...
17468      */
17469     cleanWord : function(node)
17470     {
17471         var _t = this;
17472         var cleanWordChildren = function()
17473         {
17474             if (!node.childNodes.length) {
17475                 return;
17476             }
17477             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17478                _t.cleanWord(node.childNodes[i]);
17479             }
17480         }
17481         
17482         
17483         if (!node) {
17484             this.cleanWord(this.doc.body);
17485             return;
17486         }
17487         if (node.nodeName == "#text") {
17488             // clean up silly Windows -- stuff?
17489             return; 
17490         }
17491         if (node.nodeName == "#comment") {
17492             node.parentNode.removeChild(node);
17493             // clean up silly Windows -- stuff?
17494             return; 
17495         }
17496         
17497         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17498             node.parentNode.removeChild(node);
17499             return;
17500         }
17501         
17502         // remove - but keep children..
17503         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17504             while (node.childNodes.length) {
17505                 var cn = node.childNodes[0];
17506                 node.removeChild(cn);
17507                 node.parentNode.insertBefore(cn, node);
17508             }
17509             node.parentNode.removeChild(node);
17510             cleanWordChildren();
17511             return;
17512         }
17513         // clean styles
17514         if (node.className.length) {
17515             
17516             var cn = node.className.split(/\W+/);
17517             var cna = [];
17518             Roo.each(cn, function(cls) {
17519                 if (cls.match(/Mso[a-zA-Z]+/)) {
17520                     return;
17521                 }
17522                 cna.push(cls);
17523             });
17524             node.className = cna.length ? cna.join(' ') : '';
17525             if (!cna.length) {
17526                 node.removeAttribute("class");
17527             }
17528         }
17529         
17530         if (node.hasAttribute("lang")) {
17531             node.removeAttribute("lang");
17532         }
17533         
17534         if (node.hasAttribute("style")) {
17535             
17536             var styles = node.getAttribute("style").split(";");
17537             var nstyle = [];
17538             Roo.each(styles, function(s) {
17539                 if (!s.match(/:/)) {
17540                     return;
17541                 }
17542                 var kv = s.split(":");
17543                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17544                     return;
17545                 }
17546                 // what ever is left... we allow.
17547                 nstyle.push(s);
17548             });
17549             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17550             if (!nstyle.length) {
17551                 node.removeAttribute('style');
17552             }
17553         }
17554         
17555         cleanWordChildren();
17556         
17557         
17558     },
17559     domToHTML : function(currentElement, depth, nopadtext) {
17560         
17561         depth = depth || 0;
17562         nopadtext = nopadtext || false;
17563     
17564         if (!currentElement) {
17565             return this.domToHTML(this.doc.body);
17566         }
17567         
17568         //Roo.log(currentElement);
17569         var j;
17570         var allText = false;
17571         var nodeName = currentElement.nodeName;
17572         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17573         
17574         if  (nodeName == '#text') {
17575             return currentElement.nodeValue;
17576         }
17577         
17578         
17579         var ret = '';
17580         if (nodeName != 'BODY') {
17581              
17582             var i = 0;
17583             // Prints the node tagName, such as <A>, <IMG>, etc
17584             if (tagName) {
17585                 var attr = [];
17586                 for(i = 0; i < currentElement.attributes.length;i++) {
17587                     // quoting?
17588                     var aname = currentElement.attributes.item(i).name;
17589                     if (!currentElement.attributes.item(i).value.length) {
17590                         continue;
17591                     }
17592                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17593                 }
17594                 
17595                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17596             } 
17597             else {
17598                 
17599                 // eack
17600             }
17601         } else {
17602             tagName = false;
17603         }
17604         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17605             return ret;
17606         }
17607         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17608             nopadtext = true;
17609         }
17610         
17611         
17612         // Traverse the tree
17613         i = 0;
17614         var currentElementChild = currentElement.childNodes.item(i);
17615         var allText = true;
17616         var innerHTML  = '';
17617         lastnode = '';
17618         while (currentElementChild) {
17619             // Formatting code (indent the tree so it looks nice on the screen)
17620             var nopad = nopadtext;
17621             if (lastnode == 'SPAN') {
17622                 nopad  = true;
17623             }
17624             // text
17625             if  (currentElementChild.nodeName == '#text') {
17626                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17627                 if (!nopad && toadd.length > 80) {
17628                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17629                 }
17630                 innerHTML  += toadd;
17631                 
17632                 i++;
17633                 currentElementChild = currentElement.childNodes.item(i);
17634                 lastNode = '';
17635                 continue;
17636             }
17637             allText = false;
17638             
17639             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17640                 
17641             // Recursively traverse the tree structure of the child node
17642             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17643             lastnode = currentElementChild.nodeName;
17644             i++;
17645             currentElementChild=currentElement.childNodes.item(i);
17646         }
17647         
17648         ret += innerHTML;
17649         
17650         if (!allText) {
17651                 // The remaining code is mostly for formatting the tree
17652             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17653         }
17654         
17655         
17656         if (tagName) {
17657             ret+= "</"+tagName+">";
17658         }
17659         return ret;
17660         
17661     },
17662         
17663     applyBlacklists : function()
17664     {
17665         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17666         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17667         
17668         this.white = [];
17669         this.black = [];
17670         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17671             if (b.indexOf(tag) > -1) {
17672                 return;
17673             }
17674             this.white.push(tag);
17675             
17676         }, this);
17677         
17678         Roo.each(w, function(tag) {
17679             if (b.indexOf(tag) > -1) {
17680                 return;
17681             }
17682             if (this.white.indexOf(tag) > -1) {
17683                 return;
17684             }
17685             this.white.push(tag);
17686             
17687         }, this);
17688         
17689         
17690         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17691             if (w.indexOf(tag) > -1) {
17692                 return;
17693             }
17694             this.black.push(tag);
17695             
17696         }, this);
17697         
17698         Roo.each(b, function(tag) {
17699             if (w.indexOf(tag) > -1) {
17700                 return;
17701             }
17702             if (this.black.indexOf(tag) > -1) {
17703                 return;
17704             }
17705             this.black.push(tag);
17706             
17707         }, this);
17708         
17709         
17710         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17711         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17712         
17713         this.cwhite = [];
17714         this.cblack = [];
17715         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17716             if (b.indexOf(tag) > -1) {
17717                 return;
17718             }
17719             this.cwhite.push(tag);
17720             
17721         }, this);
17722         
17723         Roo.each(w, function(tag) {
17724             if (b.indexOf(tag) > -1) {
17725                 return;
17726             }
17727             if (this.cwhite.indexOf(tag) > -1) {
17728                 return;
17729             }
17730             this.cwhite.push(tag);
17731             
17732         }, this);
17733         
17734         
17735         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17736             if (w.indexOf(tag) > -1) {
17737                 return;
17738             }
17739             this.cblack.push(tag);
17740             
17741         }, this);
17742         
17743         Roo.each(b, function(tag) {
17744             if (w.indexOf(tag) > -1) {
17745                 return;
17746             }
17747             if (this.cblack.indexOf(tag) > -1) {
17748                 return;
17749             }
17750             this.cblack.push(tag);
17751             
17752         }, this);
17753     }
17754     
17755     // hide stuff that is not compatible
17756     /**
17757      * @event blur
17758      * @hide
17759      */
17760     /**
17761      * @event change
17762      * @hide
17763      */
17764     /**
17765      * @event focus
17766      * @hide
17767      */
17768     /**
17769      * @event specialkey
17770      * @hide
17771      */
17772     /**
17773      * @cfg {String} fieldClass @hide
17774      */
17775     /**
17776      * @cfg {String} focusClass @hide
17777      */
17778     /**
17779      * @cfg {String} autoCreate @hide
17780      */
17781     /**
17782      * @cfg {String} inputType @hide
17783      */
17784     /**
17785      * @cfg {String} invalidClass @hide
17786      */
17787     /**
17788      * @cfg {String} invalidText @hide
17789      */
17790     /**
17791      * @cfg {String} msgFx @hide
17792      */
17793     /**
17794      * @cfg {String} validateOnBlur @hide
17795      */
17796 });
17797
17798 Roo.HtmlEditorCore.white = [
17799         'area', 'br', 'img', 'input', 'hr', 'wbr',
17800         
17801        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17802        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17803        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17804        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17805        'table',   'ul',         'xmp', 
17806        
17807        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17808       'thead',   'tr', 
17809      
17810       'dir', 'menu', 'ol', 'ul', 'dl',
17811        
17812       'embed',  'object'
17813 ];
17814
17815
17816 Roo.HtmlEditorCore.black = [
17817     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17818         'applet', // 
17819         'base',   'basefont', 'bgsound', 'blink',  'body', 
17820         'frame',  'frameset', 'head',    'html',   'ilayer', 
17821         'iframe', 'layer',  'link',     'meta',    'object',   
17822         'script', 'style' ,'title',  'xml' // clean later..
17823 ];
17824 Roo.HtmlEditorCore.clean = [
17825     'script', 'style', 'title', 'xml'
17826 ];
17827 Roo.HtmlEditorCore.remove = [
17828     'font'
17829 ];
17830 // attributes..
17831
17832 Roo.HtmlEditorCore.ablack = [
17833     'on'
17834 ];
17835     
17836 Roo.HtmlEditorCore.aclean = [ 
17837     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17838 ];
17839
17840 // protocols..
17841 Roo.HtmlEditorCore.pwhite= [
17842         'http',  'https',  'mailto'
17843 ];
17844
17845 // white listed style attributes.
17846 Roo.HtmlEditorCore.cwhite= [
17847       //  'text-align', /// default is to allow most things..
17848       
17849          
17850 //        'font-size'//??
17851 ];
17852
17853 // black listed style attributes.
17854 Roo.HtmlEditorCore.cblack= [
17855       //  'font-size' -- this can be set by the project 
17856 ];
17857
17858
17859 Roo.HtmlEditorCore.swapCodes   =[ 
17860     [    8211, "--" ], 
17861     [    8212, "--" ], 
17862     [    8216,  "'" ],  
17863     [    8217, "'" ],  
17864     [    8220, '"' ],  
17865     [    8221, '"' ],  
17866     [    8226, "*" ],  
17867     [    8230, "..." ]
17868 ]; 
17869
17870     /*
17871  * - LGPL
17872  *
17873  * HtmlEditor
17874  * 
17875  */
17876
17877 /**
17878  * @class Roo.bootstrap.HtmlEditor
17879  * @extends Roo.bootstrap.TextArea
17880  * Bootstrap HtmlEditor class
17881
17882  * @constructor
17883  * Create a new HtmlEditor
17884  * @param {Object} config The config object
17885  */
17886
17887 Roo.bootstrap.HtmlEditor = function(config){
17888     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17889     if (!this.toolbars) {
17890         this.toolbars = [];
17891     }
17892     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17893     this.addEvents({
17894             /**
17895              * @event initialize
17896              * Fires when the editor is fully initialized (including the iframe)
17897              * @param {HtmlEditor} this
17898              */
17899             initialize: true,
17900             /**
17901              * @event activate
17902              * Fires when the editor is first receives the focus. Any insertion must wait
17903              * until after this event.
17904              * @param {HtmlEditor} this
17905              */
17906             activate: true,
17907              /**
17908              * @event beforesync
17909              * Fires before the textarea is updated with content from the editor iframe. Return false
17910              * to cancel the sync.
17911              * @param {HtmlEditor} this
17912              * @param {String} html
17913              */
17914             beforesync: true,
17915              /**
17916              * @event beforepush
17917              * Fires before the iframe editor is updated with content from the textarea. Return false
17918              * to cancel the push.
17919              * @param {HtmlEditor} this
17920              * @param {String} html
17921              */
17922             beforepush: true,
17923              /**
17924              * @event sync
17925              * Fires when the textarea is updated with content from the editor iframe.
17926              * @param {HtmlEditor} this
17927              * @param {String} html
17928              */
17929             sync: true,
17930              /**
17931              * @event push
17932              * Fires when the iframe editor is updated with content from the textarea.
17933              * @param {HtmlEditor} this
17934              * @param {String} html
17935              */
17936             push: true,
17937              /**
17938              * @event editmodechange
17939              * Fires when the editor switches edit modes
17940              * @param {HtmlEditor} this
17941              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17942              */
17943             editmodechange: true,
17944             /**
17945              * @event editorevent
17946              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17947              * @param {HtmlEditor} this
17948              */
17949             editorevent: true,
17950             /**
17951              * @event firstfocus
17952              * Fires when on first focus - needed by toolbars..
17953              * @param {HtmlEditor} this
17954              */
17955             firstfocus: true,
17956             /**
17957              * @event autosave
17958              * Auto save the htmlEditor value as a file into Events
17959              * @param {HtmlEditor} this
17960              */
17961             autosave: true,
17962             /**
17963              * @event savedpreview
17964              * preview the saved version of htmlEditor
17965              * @param {HtmlEditor} this
17966              */
17967             savedpreview: true
17968         });
17969 };
17970
17971
17972 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17973     
17974     
17975       /**
17976      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17977      */
17978     toolbars : false,
17979    
17980      /**
17981      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17982      *                        Roo.resizable.
17983      */
17984     resizable : false,
17985      /**
17986      * @cfg {Number} height (in pixels)
17987      */   
17988     height: 300,
17989    /**
17990      * @cfg {Number} width (in pixels)
17991      */   
17992     width: false,
17993     
17994     /**
17995      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17996      * 
17997      */
17998     stylesheets: false,
17999     
18000     // id of frame..
18001     frameId: false,
18002     
18003     // private properties
18004     validationEvent : false,
18005     deferHeight: true,
18006     initialized : false,
18007     activated : false,
18008     
18009     onFocus : Roo.emptyFn,
18010     iframePad:3,
18011     hideMode:'offsets',
18012     
18013     
18014     tbContainer : false,
18015     
18016     toolbarContainer :function() {
18017         return this.wrap.select('.x-html-editor-tb',true).first();
18018     },
18019
18020     /**
18021      * Protected method that will not generally be called directly. It
18022      * is called when the editor creates its toolbar. Override this method if you need to
18023      * add custom toolbar buttons.
18024      * @param {HtmlEditor} editor
18025      */
18026     createToolbar : function(){
18027         
18028         Roo.log("create toolbars");
18029         
18030         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18031         this.toolbars[0].render(this.toolbarContainer());
18032         
18033         return;
18034         
18035 //        if (!editor.toolbars || !editor.toolbars.length) {
18036 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18037 //        }
18038 //        
18039 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18040 //            editor.toolbars[i] = Roo.factory(
18041 //                    typeof(editor.toolbars[i]) == 'string' ?
18042 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18043 //                Roo.bootstrap.HtmlEditor);
18044 //            editor.toolbars[i].init(editor);
18045 //        }
18046     },
18047
18048      
18049     // private
18050     onRender : function(ct, position)
18051     {
18052        // Roo.log("Call onRender: " + this.xtype);
18053         var _t = this;
18054         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18055       
18056         this.wrap = this.inputEl().wrap({
18057             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18058         });
18059         
18060         this.editorcore.onRender(ct, position);
18061          
18062         if (this.resizable) {
18063             this.resizeEl = new Roo.Resizable(this.wrap, {
18064                 pinned : true,
18065                 wrap: true,
18066                 dynamic : true,
18067                 minHeight : this.height,
18068                 height: this.height,
18069                 handles : this.resizable,
18070                 width: this.width,
18071                 listeners : {
18072                     resize : function(r, w, h) {
18073                         _t.onResize(w,h); // -something
18074                     }
18075                 }
18076             });
18077             
18078         }
18079         this.createToolbar(this);
18080        
18081         
18082         if(!this.width && this.resizable){
18083             this.setSize(this.wrap.getSize());
18084         }
18085         if (this.resizeEl) {
18086             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18087             // should trigger onReize..
18088         }
18089         
18090     },
18091
18092     // private
18093     onResize : function(w, h)
18094     {
18095         Roo.log('resize: ' +w + ',' + h );
18096         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18097         var ew = false;
18098         var eh = false;
18099         
18100         if(this.inputEl() ){
18101             if(typeof w == 'number'){
18102                 var aw = w - this.wrap.getFrameWidth('lr');
18103                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18104                 ew = aw;
18105             }
18106             if(typeof h == 'number'){
18107                  var tbh = -11;  // fixme it needs to tool bar size!
18108                 for (var i =0; i < this.toolbars.length;i++) {
18109                     // fixme - ask toolbars for heights?
18110                     tbh += this.toolbars[i].el.getHeight();
18111                     //if (this.toolbars[i].footer) {
18112                     //    tbh += this.toolbars[i].footer.el.getHeight();
18113                     //}
18114                 }
18115               
18116                 
18117                 
18118                 
18119                 
18120                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18121                 ah -= 5; // knock a few pixes off for look..
18122                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18123                 var eh = ah;
18124             }
18125         }
18126         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18127         this.editorcore.onResize(ew,eh);
18128         
18129     },
18130
18131     /**
18132      * Toggles the editor between standard and source edit mode.
18133      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18134      */
18135     toggleSourceEdit : function(sourceEditMode)
18136     {
18137         this.editorcore.toggleSourceEdit(sourceEditMode);
18138         
18139         if(this.editorcore.sourceEditMode){
18140             Roo.log('editor - showing textarea');
18141             
18142 //            Roo.log('in');
18143 //            Roo.log(this.syncValue());
18144             this.syncValue();
18145             this.inputEl().removeClass(['hide', 'x-hidden']);
18146             this.inputEl().dom.removeAttribute('tabIndex');
18147             this.inputEl().focus();
18148         }else{
18149             Roo.log('editor - hiding textarea');
18150 //            Roo.log('out')
18151 //            Roo.log(this.pushValue()); 
18152             this.pushValue();
18153             
18154             this.inputEl().addClass(['hide', 'x-hidden']);
18155             this.inputEl().dom.setAttribute('tabIndex', -1);
18156             //this.deferFocus();
18157         }
18158          
18159         if(this.resizable){
18160             this.setSize(this.wrap.getSize());
18161         }
18162         
18163         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18164     },
18165  
18166     // private (for BoxComponent)
18167     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18168
18169     // private (for BoxComponent)
18170     getResizeEl : function(){
18171         return this.wrap;
18172     },
18173
18174     // private (for BoxComponent)
18175     getPositionEl : function(){
18176         return this.wrap;
18177     },
18178
18179     // private
18180     initEvents : function(){
18181         this.originalValue = this.getValue();
18182     },
18183
18184 //    /**
18185 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18186 //     * @method
18187 //     */
18188 //    markInvalid : Roo.emptyFn,
18189 //    /**
18190 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18191 //     * @method
18192 //     */
18193 //    clearInvalid : Roo.emptyFn,
18194
18195     setValue : function(v){
18196         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18197         this.editorcore.pushValue();
18198     },
18199
18200      
18201     // private
18202     deferFocus : function(){
18203         this.focus.defer(10, this);
18204     },
18205
18206     // doc'ed in Field
18207     focus : function(){
18208         this.editorcore.focus();
18209         
18210     },
18211       
18212
18213     // private
18214     onDestroy : function(){
18215         
18216         
18217         
18218         if(this.rendered){
18219             
18220             for (var i =0; i < this.toolbars.length;i++) {
18221                 // fixme - ask toolbars for heights?
18222                 this.toolbars[i].onDestroy();
18223             }
18224             
18225             this.wrap.dom.innerHTML = '';
18226             this.wrap.remove();
18227         }
18228     },
18229
18230     // private
18231     onFirstFocus : function(){
18232         //Roo.log("onFirstFocus");
18233         this.editorcore.onFirstFocus();
18234          for (var i =0; i < this.toolbars.length;i++) {
18235             this.toolbars[i].onFirstFocus();
18236         }
18237         
18238     },
18239     
18240     // private
18241     syncValue : function()
18242     {   
18243         this.editorcore.syncValue();
18244     },
18245     
18246     pushValue : function()
18247     {   
18248         this.editorcore.pushValue();
18249     }
18250      
18251     
18252     // hide stuff that is not compatible
18253     /**
18254      * @event blur
18255      * @hide
18256      */
18257     /**
18258      * @event change
18259      * @hide
18260      */
18261     /**
18262      * @event focus
18263      * @hide
18264      */
18265     /**
18266      * @event specialkey
18267      * @hide
18268      */
18269     /**
18270      * @cfg {String} fieldClass @hide
18271      */
18272     /**
18273      * @cfg {String} focusClass @hide
18274      */
18275     /**
18276      * @cfg {String} autoCreate @hide
18277      */
18278     /**
18279      * @cfg {String} inputType @hide
18280      */
18281     /**
18282      * @cfg {String} invalidClass @hide
18283      */
18284     /**
18285      * @cfg {String} invalidText @hide
18286      */
18287     /**
18288      * @cfg {String} msgFx @hide
18289      */
18290     /**
18291      * @cfg {String} validateOnBlur @hide
18292      */
18293 });
18294  
18295     
18296    
18297    
18298    
18299       
18300 Roo.namespace('Roo.bootstrap.htmleditor');
18301 /**
18302  * @class Roo.bootstrap.HtmlEditorToolbar1
18303  * Basic Toolbar
18304  * 
18305  * Usage:
18306  *
18307  new Roo.bootstrap.HtmlEditor({
18308     ....
18309     toolbars : [
18310         new Roo.bootstrap.HtmlEditorToolbar1({
18311             disable : { fonts: 1 , format: 1, ..., ... , ...],
18312             btns : [ .... ]
18313         })
18314     }
18315      
18316  * 
18317  * @cfg {Object} disable List of elements to disable..
18318  * @cfg {Array} btns List of additional buttons.
18319  * 
18320  * 
18321  * NEEDS Extra CSS? 
18322  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18323  */
18324  
18325 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18326 {
18327     
18328     Roo.apply(this, config);
18329     
18330     // default disabled, based on 'good practice'..
18331     this.disable = this.disable || {};
18332     Roo.applyIf(this.disable, {
18333         fontSize : true,
18334         colors : true,
18335         specialElements : true
18336     });
18337     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18338     
18339     this.editor = config.editor;
18340     this.editorcore = config.editor.editorcore;
18341     
18342     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18343     
18344     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18345     // dont call parent... till later.
18346 }
18347 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18348      
18349     bar : true,
18350     
18351     editor : false,
18352     editorcore : false,
18353     
18354     
18355     formats : [
18356         "p" ,  
18357         "h1","h2","h3","h4","h5","h6", 
18358         "pre", "code", 
18359         "abbr", "acronym", "address", "cite", "samp", "var",
18360         'div','span'
18361     ],
18362     
18363     onRender : function(ct, position)
18364     {
18365        // Roo.log("Call onRender: " + this.xtype);
18366         
18367        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18368        Roo.log(this.el);
18369        this.el.dom.style.marginBottom = '0';
18370        var _this = this;
18371        var editorcore = this.editorcore;
18372        var editor= this.editor;
18373        
18374        var children = [];
18375        var btn = function(id,cmd , toggle, handler){
18376        
18377             var  event = toggle ? 'toggle' : 'click';
18378        
18379             var a = {
18380                 size : 'sm',
18381                 xtype: 'Button',
18382                 xns: Roo.bootstrap,
18383                 glyphicon : id,
18384                 cmd : id || cmd,
18385                 enableToggle:toggle !== false,
18386                 //html : 'submit'
18387                 pressed : toggle ? false : null,
18388                 listeners : {}
18389             }
18390             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18391                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18392             }
18393             children.push(a);
18394             return a;
18395        }
18396         
18397         var style = {
18398                 xtype: 'Button',
18399                 size : 'sm',
18400                 xns: Roo.bootstrap,
18401                 glyphicon : 'font',
18402                 //html : 'submit'
18403                 menu : {
18404                     xtype: 'Menu',
18405                     xns: Roo.bootstrap,
18406                     items:  []
18407                 }
18408         };
18409         Roo.each(this.formats, function(f) {
18410             style.menu.items.push({
18411                 xtype :'MenuItem',
18412                 xns: Roo.bootstrap,
18413                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18414                 tagname : f,
18415                 listeners : {
18416                     click : function()
18417                     {
18418                         editorcore.insertTag(this.tagname);
18419                         editor.focus();
18420                     }
18421                 }
18422                 
18423             });
18424         });
18425          children.push(style);   
18426             
18427             
18428         btn('bold',false,true);
18429         btn('italic',false,true);
18430         btn('align-left', 'justifyleft',true);
18431         btn('align-center', 'justifycenter',true);
18432         btn('align-right' , 'justifyright',true);
18433         btn('link', false, false, function(btn) {
18434             //Roo.log("create link?");
18435             var url = prompt(this.createLinkText, this.defaultLinkValue);
18436             if(url && url != 'http:/'+'/'){
18437                 this.editorcore.relayCmd('createlink', url);
18438             }
18439         }),
18440         btn('list','insertunorderedlist',true);
18441         btn('pencil', false,true, function(btn){
18442                 Roo.log(this);
18443                 
18444                 this.toggleSourceEdit(btn.pressed);
18445         });
18446         /*
18447         var cog = {
18448                 xtype: 'Button',
18449                 size : 'sm',
18450                 xns: Roo.bootstrap,
18451                 glyphicon : 'cog',
18452                 //html : 'submit'
18453                 menu : {
18454                     xtype: 'Menu',
18455                     xns: Roo.bootstrap,
18456                     items:  []
18457                 }
18458         };
18459         
18460         cog.menu.items.push({
18461             xtype :'MenuItem',
18462             xns: Roo.bootstrap,
18463             html : Clean styles,
18464             tagname : f,
18465             listeners : {
18466                 click : function()
18467                 {
18468                     editorcore.insertTag(this.tagname);
18469                     editor.focus();
18470                 }
18471             }
18472             
18473         });
18474        */
18475         
18476          
18477        this.xtype = 'NavSimplebar';
18478         
18479         for(var i=0;i< children.length;i++) {
18480             
18481             this.buttons.add(this.addxtypeChild(children[i]));
18482             
18483         }
18484         
18485         editor.on('editorevent', this.updateToolbar, this);
18486     },
18487     onBtnClick : function(id)
18488     {
18489        this.editorcore.relayCmd(id);
18490        this.editorcore.focus();
18491     },
18492     
18493     /**
18494      * Protected method that will not generally be called directly. It triggers
18495      * a toolbar update by reading the markup state of the current selection in the editor.
18496      */
18497     updateToolbar: function(){
18498
18499         if(!this.editorcore.activated){
18500             this.editor.onFirstFocus(); // is this neeed?
18501             return;
18502         }
18503
18504         var btns = this.buttons; 
18505         var doc = this.editorcore.doc;
18506         btns.get('bold').setActive(doc.queryCommandState('bold'));
18507         btns.get('italic').setActive(doc.queryCommandState('italic'));
18508         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18509         
18510         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18511         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18512         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18513         
18514         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18515         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18516          /*
18517         
18518         var ans = this.editorcore.getAllAncestors();
18519         if (this.formatCombo) {
18520             
18521             
18522             var store = this.formatCombo.store;
18523             this.formatCombo.setValue("");
18524             for (var i =0; i < ans.length;i++) {
18525                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18526                     // select it..
18527                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18528                     break;
18529                 }
18530             }
18531         }
18532         
18533         
18534         
18535         // hides menus... - so this cant be on a menu...
18536         Roo.bootstrap.MenuMgr.hideAll();
18537         */
18538         Roo.bootstrap.MenuMgr.hideAll();
18539         //this.editorsyncValue();
18540     },
18541     onFirstFocus: function() {
18542         this.buttons.each(function(item){
18543            item.enable();
18544         });
18545     },
18546     toggleSourceEdit : function(sourceEditMode){
18547         
18548           
18549         if(sourceEditMode){
18550             Roo.log("disabling buttons");
18551            this.buttons.each( function(item){
18552                 if(item.cmd != 'pencil'){
18553                     item.disable();
18554                 }
18555             });
18556           
18557         }else{
18558             Roo.log("enabling buttons");
18559             if(this.editorcore.initialized){
18560                 this.buttons.each( function(item){
18561                     item.enable();
18562                 });
18563             }
18564             
18565         }
18566         Roo.log("calling toggole on editor");
18567         // tell the editor that it's been pressed..
18568         this.editor.toggleSourceEdit(sourceEditMode);
18569        
18570     }
18571 });
18572
18573
18574
18575
18576
18577 /**
18578  * @class Roo.bootstrap.Table.AbstractSelectionModel
18579  * @extends Roo.util.Observable
18580  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18581  * implemented by descendant classes.  This class should not be directly instantiated.
18582  * @constructor
18583  */
18584 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18585     this.locked = false;
18586     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18587 };
18588
18589
18590 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18591     /** @ignore Called by the grid automatically. Do not call directly. */
18592     init : function(grid){
18593         this.grid = grid;
18594         this.initEvents();
18595     },
18596
18597     /**
18598      * Locks the selections.
18599      */
18600     lock : function(){
18601         this.locked = true;
18602     },
18603
18604     /**
18605      * Unlocks the selections.
18606      */
18607     unlock : function(){
18608         this.locked = false;
18609     },
18610
18611     /**
18612      * Returns true if the selections are locked.
18613      * @return {Boolean}
18614      */
18615     isLocked : function(){
18616         return this.locked;
18617     }
18618 });
18619 /**
18620  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18621  * @class Roo.bootstrap.Table.RowSelectionModel
18622  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18623  * It supports multiple selections and keyboard selection/navigation. 
18624  * @constructor
18625  * @param {Object} config
18626  */
18627
18628 Roo.bootstrap.Table.RowSelectionModel = function(config){
18629     Roo.apply(this, config);
18630     this.selections = new Roo.util.MixedCollection(false, function(o){
18631         return o.id;
18632     });
18633
18634     this.last = false;
18635     this.lastActive = false;
18636
18637     this.addEvents({
18638         /**
18639              * @event selectionchange
18640              * Fires when the selection changes
18641              * @param {SelectionModel} this
18642              */
18643             "selectionchange" : true,
18644         /**
18645              * @event afterselectionchange
18646              * Fires after the selection changes (eg. by key press or clicking)
18647              * @param {SelectionModel} this
18648              */
18649             "afterselectionchange" : true,
18650         /**
18651              * @event beforerowselect
18652              * Fires when a row is selected being selected, return false to cancel.
18653              * @param {SelectionModel} this
18654              * @param {Number} rowIndex The selected index
18655              * @param {Boolean} keepExisting False if other selections will be cleared
18656              */
18657             "beforerowselect" : true,
18658         /**
18659              * @event rowselect
18660              * Fires when a row is selected.
18661              * @param {SelectionModel} this
18662              * @param {Number} rowIndex The selected index
18663              * @param {Roo.data.Record} r The record
18664              */
18665             "rowselect" : true,
18666         /**
18667              * @event rowdeselect
18668              * Fires when a row is deselected.
18669              * @param {SelectionModel} this
18670              * @param {Number} rowIndex The selected index
18671              */
18672         "rowdeselect" : true
18673     });
18674     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18675     this.locked = false;
18676 };
18677
18678 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18679     /**
18680      * @cfg {Boolean} singleSelect
18681      * True to allow selection of only one row at a time (defaults to false)
18682      */
18683     singleSelect : false,
18684
18685     // private
18686     initEvents : function(){
18687
18688         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18689             this.grid.on("mousedown", this.handleMouseDown, this);
18690         }else{ // allow click to work like normal
18691             this.grid.on("rowclick", this.handleDragableRowClick, this);
18692         }
18693
18694         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18695             "up" : function(e){
18696                 if(!e.shiftKey){
18697                     this.selectPrevious(e.shiftKey);
18698                 }else if(this.last !== false && this.lastActive !== false){
18699                     var last = this.last;
18700                     this.selectRange(this.last,  this.lastActive-1);
18701                     this.grid.getView().focusRow(this.lastActive);
18702                     if(last !== false){
18703                         this.last = last;
18704                     }
18705                 }else{
18706                     this.selectFirstRow();
18707                 }
18708                 this.fireEvent("afterselectionchange", this);
18709             },
18710             "down" : function(e){
18711                 if(!e.shiftKey){
18712                     this.selectNext(e.shiftKey);
18713                 }else if(this.last !== false && this.lastActive !== false){
18714                     var last = this.last;
18715                     this.selectRange(this.last,  this.lastActive+1);
18716                     this.grid.getView().focusRow(this.lastActive);
18717                     if(last !== false){
18718                         this.last = last;
18719                     }
18720                 }else{
18721                     this.selectFirstRow();
18722                 }
18723                 this.fireEvent("afterselectionchange", this);
18724             },
18725             scope: this
18726         });
18727
18728         var view = this.grid.view;
18729         view.on("refresh", this.onRefresh, this);
18730         view.on("rowupdated", this.onRowUpdated, this);
18731         view.on("rowremoved", this.onRemove, this);
18732     },
18733
18734     // private
18735     onRefresh : function(){
18736         var ds = this.grid.dataSource, i, v = this.grid.view;
18737         var s = this.selections;
18738         s.each(function(r){
18739             if((i = ds.indexOfId(r.id)) != -1){
18740                 v.onRowSelect(i);
18741             }else{
18742                 s.remove(r);
18743             }
18744         });
18745     },
18746
18747     // private
18748     onRemove : function(v, index, r){
18749         this.selections.remove(r);
18750     },
18751
18752     // private
18753     onRowUpdated : function(v, index, r){
18754         if(this.isSelected(r)){
18755             v.onRowSelect(index);
18756         }
18757     },
18758
18759     /**
18760      * Select records.
18761      * @param {Array} records The records to select
18762      * @param {Boolean} keepExisting (optional) True to keep existing selections
18763      */
18764     selectRecords : function(records, keepExisting){
18765         if(!keepExisting){
18766             this.clearSelections();
18767         }
18768         var ds = this.grid.dataSource;
18769         for(var i = 0, len = records.length; i < len; i++){
18770             this.selectRow(ds.indexOf(records[i]), true);
18771         }
18772     },
18773
18774     /**
18775      * Gets the number of selected rows.
18776      * @return {Number}
18777      */
18778     getCount : function(){
18779         return this.selections.length;
18780     },
18781
18782     /**
18783      * Selects the first row in the grid.
18784      */
18785     selectFirstRow : function(){
18786         this.selectRow(0);
18787     },
18788
18789     /**
18790      * Select the last row.
18791      * @param {Boolean} keepExisting (optional) True to keep existing selections
18792      */
18793     selectLastRow : function(keepExisting){
18794         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18795     },
18796
18797     /**
18798      * Selects the row immediately following the last selected row.
18799      * @param {Boolean} keepExisting (optional) True to keep existing selections
18800      */
18801     selectNext : function(keepExisting){
18802         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18803             this.selectRow(this.last+1, keepExisting);
18804             this.grid.getView().focusRow(this.last);
18805         }
18806     },
18807
18808     /**
18809      * Selects the row that precedes the last selected row.
18810      * @param {Boolean} keepExisting (optional) True to keep existing selections
18811      */
18812     selectPrevious : function(keepExisting){
18813         if(this.last){
18814             this.selectRow(this.last-1, keepExisting);
18815             this.grid.getView().focusRow(this.last);
18816         }
18817     },
18818
18819     /**
18820      * Returns the selected records
18821      * @return {Array} Array of selected records
18822      */
18823     getSelections : function(){
18824         return [].concat(this.selections.items);
18825     },
18826
18827     /**
18828      * Returns the first selected record.
18829      * @return {Record}
18830      */
18831     getSelected : function(){
18832         return this.selections.itemAt(0);
18833     },
18834
18835
18836     /**
18837      * Clears all selections.
18838      */
18839     clearSelections : function(fast){
18840         if(this.locked) return;
18841         if(fast !== true){
18842             var ds = this.grid.dataSource;
18843             var s = this.selections;
18844             s.each(function(r){
18845                 this.deselectRow(ds.indexOfId(r.id));
18846             }, this);
18847             s.clear();
18848         }else{
18849             this.selections.clear();
18850         }
18851         this.last = false;
18852     },
18853
18854
18855     /**
18856      * Selects all rows.
18857      */
18858     selectAll : function(){
18859         if(this.locked) return;
18860         this.selections.clear();
18861         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18862             this.selectRow(i, true);
18863         }
18864     },
18865
18866     /**
18867      * Returns True if there is a selection.
18868      * @return {Boolean}
18869      */
18870     hasSelection : function(){
18871         return this.selections.length > 0;
18872     },
18873
18874     /**
18875      * Returns True if the specified row is selected.
18876      * @param {Number/Record} record The record or index of the record to check
18877      * @return {Boolean}
18878      */
18879     isSelected : function(index){
18880         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18881         return (r && this.selections.key(r.id) ? true : false);
18882     },
18883
18884     /**
18885      * Returns True if the specified record id is selected.
18886      * @param {String} id The id of record to check
18887      * @return {Boolean}
18888      */
18889     isIdSelected : function(id){
18890         return (this.selections.key(id) ? true : false);
18891     },
18892
18893     // private
18894     handleMouseDown : function(e, t){
18895         var view = this.grid.getView(), rowIndex;
18896         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18897             return;
18898         };
18899         if(e.shiftKey && this.last !== false){
18900             var last = this.last;
18901             this.selectRange(last, rowIndex, e.ctrlKey);
18902             this.last = last; // reset the last
18903             view.focusRow(rowIndex);
18904         }else{
18905             var isSelected = this.isSelected(rowIndex);
18906             if(e.button !== 0 && isSelected){
18907                 view.focusRow(rowIndex);
18908             }else if(e.ctrlKey && isSelected){
18909                 this.deselectRow(rowIndex);
18910             }else if(!isSelected){
18911                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18912                 view.focusRow(rowIndex);
18913             }
18914         }
18915         this.fireEvent("afterselectionchange", this);
18916     },
18917     // private
18918     handleDragableRowClick :  function(grid, rowIndex, e) 
18919     {
18920         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18921             this.selectRow(rowIndex, false);
18922             grid.view.focusRow(rowIndex);
18923              this.fireEvent("afterselectionchange", this);
18924         }
18925     },
18926     
18927     /**
18928      * Selects multiple rows.
18929      * @param {Array} rows Array of the indexes of the row to select
18930      * @param {Boolean} keepExisting (optional) True to keep existing selections
18931      */
18932     selectRows : function(rows, keepExisting){
18933         if(!keepExisting){
18934             this.clearSelections();
18935         }
18936         for(var i = 0, len = rows.length; i < len; i++){
18937             this.selectRow(rows[i], true);
18938         }
18939     },
18940
18941     /**
18942      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18943      * @param {Number} startRow The index of the first row in the range
18944      * @param {Number} endRow The index of the last row in the range
18945      * @param {Boolean} keepExisting (optional) True to retain existing selections
18946      */
18947     selectRange : function(startRow, endRow, keepExisting){
18948         if(this.locked) return;
18949         if(!keepExisting){
18950             this.clearSelections();
18951         }
18952         if(startRow <= endRow){
18953             for(var i = startRow; i <= endRow; i++){
18954                 this.selectRow(i, true);
18955             }
18956         }else{
18957             for(var i = startRow; i >= endRow; i--){
18958                 this.selectRow(i, true);
18959             }
18960         }
18961     },
18962
18963     /**
18964      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
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      */
18968     deselectRange : function(startRow, endRow, preventViewNotify){
18969         if(this.locked) return;
18970         for(var i = startRow; i <= endRow; i++){
18971             this.deselectRow(i, preventViewNotify);
18972         }
18973     },
18974
18975     /**
18976      * Selects a row.
18977      * @param {Number} row The index of the row to select
18978      * @param {Boolean} keepExisting (optional) True to keep existing selections
18979      */
18980     selectRow : function(index, keepExisting, preventViewNotify){
18981         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18982         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18983             if(!keepExisting || this.singleSelect){
18984                 this.clearSelections();
18985             }
18986             var r = this.grid.dataSource.getAt(index);
18987             this.selections.add(r);
18988             this.last = this.lastActive = index;
18989             if(!preventViewNotify){
18990                 this.grid.getView().onRowSelect(index);
18991             }
18992             this.fireEvent("rowselect", this, index, r);
18993             this.fireEvent("selectionchange", this);
18994         }
18995     },
18996
18997     /**
18998      * Deselects a row.
18999      * @param {Number} row The index of the row to deselect
19000      */
19001     deselectRow : function(index, preventViewNotify){
19002         if(this.locked) return;
19003         if(this.last == index){
19004             this.last = false;
19005         }
19006         if(this.lastActive == index){
19007             this.lastActive = false;
19008         }
19009         var r = this.grid.dataSource.getAt(index);
19010         this.selections.remove(r);
19011         if(!preventViewNotify){
19012             this.grid.getView().onRowDeselect(index);
19013         }
19014         this.fireEvent("rowdeselect", this, index);
19015         this.fireEvent("selectionchange", this);
19016     },
19017
19018     // private
19019     restoreLast : function(){
19020         if(this._last){
19021             this.last = this._last;
19022         }
19023     },
19024
19025     // private
19026     acceptsNav : function(row, col, cm){
19027         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19028     },
19029
19030     // private
19031     onEditorKey : function(field, e){
19032         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19033         if(k == e.TAB){
19034             e.stopEvent();
19035             ed.completeEdit();
19036             if(e.shiftKey){
19037                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19038             }else{
19039                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19040             }
19041         }else if(k == e.ENTER && !e.ctrlKey){
19042             e.stopEvent();
19043             ed.completeEdit();
19044             if(e.shiftKey){
19045                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19046             }else{
19047                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19048             }
19049         }else if(k == e.ESC){
19050             ed.cancelEdit();
19051         }
19052         if(newCell){
19053             g.startEditing(newCell[0], newCell[1]);
19054         }
19055     }
19056 });/*
19057  * Based on:
19058  * Ext JS Library 1.1.1
19059  * Copyright(c) 2006-2007, Ext JS, LLC.
19060  *
19061  * Originally Released Under LGPL - original licence link has changed is not relivant.
19062  *
19063  * Fork - LGPL
19064  * <script type="text/javascript">
19065  */
19066  
19067 /**
19068  * @class Roo.bootstrap.PagingToolbar
19069  * @extends Roo.Row
19070  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19071  * @constructor
19072  * Create a new PagingToolbar
19073  * @param {Object} config The config object
19074  */
19075 Roo.bootstrap.PagingToolbar = function(config)
19076 {
19077     // old args format still supported... - xtype is prefered..
19078         // created from xtype...
19079     var ds = config.dataSource;
19080     this.toolbarItems = [];
19081     if (config.items) {
19082         this.toolbarItems = config.items;
19083 //        config.items = [];
19084     }
19085     
19086     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19087     this.ds = ds;
19088     this.cursor = 0;
19089     if (ds) { 
19090         this.bind(ds);
19091     }
19092     
19093     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19094     
19095 };
19096
19097 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19098     /**
19099      * @cfg {Roo.data.Store} dataSource
19100      * The underlying data store providing the paged data
19101      */
19102     /**
19103      * @cfg {String/HTMLElement/Element} container
19104      * container The id or element that will contain the toolbar
19105      */
19106     /**
19107      * @cfg {Boolean} displayInfo
19108      * True to display the displayMsg (defaults to false)
19109      */
19110     /**
19111      * @cfg {Number} pageSize
19112      * The number of records to display per page (defaults to 20)
19113      */
19114     pageSize: 20,
19115     /**
19116      * @cfg {String} displayMsg
19117      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19118      */
19119     displayMsg : 'Displaying {0} - {1} of {2}',
19120     /**
19121      * @cfg {String} emptyMsg
19122      * The message to display when no records are found (defaults to "No data to display")
19123      */
19124     emptyMsg : 'No data to display',
19125     /**
19126      * Customizable piece of the default paging text (defaults to "Page")
19127      * @type String
19128      */
19129     beforePageText : "Page",
19130     /**
19131      * Customizable piece of the default paging text (defaults to "of %0")
19132      * @type String
19133      */
19134     afterPageText : "of {0}",
19135     /**
19136      * Customizable piece of the default paging text (defaults to "First Page")
19137      * @type String
19138      */
19139     firstText : "First Page",
19140     /**
19141      * Customizable piece of the default paging text (defaults to "Previous Page")
19142      * @type String
19143      */
19144     prevText : "Previous Page",
19145     /**
19146      * Customizable piece of the default paging text (defaults to "Next Page")
19147      * @type String
19148      */
19149     nextText : "Next Page",
19150     /**
19151      * Customizable piece of the default paging text (defaults to "Last Page")
19152      * @type String
19153      */
19154     lastText : "Last Page",
19155     /**
19156      * Customizable piece of the default paging text (defaults to "Refresh")
19157      * @type String
19158      */
19159     refreshText : "Refresh",
19160
19161     buttons : false,
19162     // private
19163     onRender : function(ct, position) 
19164     {
19165         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19166         this.navgroup.parentId = this.id;
19167         this.navgroup.onRender(this.el, null);
19168         // add the buttons to the navgroup
19169         
19170         if(this.displayInfo){
19171             Roo.log(this.el.select('ul.navbar-nav',true).first());
19172             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19173             this.displayEl = this.el.select('.x-paging-info', true).first();
19174 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19175 //            this.displayEl = navel.el.select('span',true).first();
19176         }
19177         
19178         var _this = this;
19179         
19180         if(this.buttons){
19181             Roo.each(_this.buttons, function(e){
19182                Roo.factory(e).onRender(_this.el, null);
19183             });
19184         }
19185             
19186         Roo.each(_this.toolbarItems, function(e) {
19187             _this.navgroup.addItem(e);
19188         });
19189         
19190         this.first = this.navgroup.addItem({
19191             tooltip: this.firstText,
19192             cls: "prev",
19193             icon : 'fa fa-backward',
19194             disabled: true,
19195             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19196         });
19197         
19198         this.prev =  this.navgroup.addItem({
19199             tooltip: this.prevText,
19200             cls: "prev",
19201             icon : 'fa fa-step-backward',
19202             disabled: true,
19203             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19204         });
19205     //this.addSeparator();
19206         
19207         
19208         var field = this.navgroup.addItem( {
19209             tagtype : 'span',
19210             cls : 'x-paging-position',
19211             
19212             html : this.beforePageText  +
19213                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19214                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19215          } ); //?? escaped?
19216         
19217         this.field = field.el.select('input', true).first();
19218         this.field.on("keydown", this.onPagingKeydown, this);
19219         this.field.on("focus", function(){this.dom.select();});
19220     
19221     
19222         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19223         //this.field.setHeight(18);
19224         //this.addSeparator();
19225         this.next = this.navgroup.addItem({
19226             tooltip: this.nextText,
19227             cls: "next",
19228             html : ' <i class="fa fa-step-forward">',
19229             disabled: true,
19230             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19231         });
19232         this.last = this.navgroup.addItem({
19233             tooltip: this.lastText,
19234             icon : 'fa fa-forward',
19235             cls: "next",
19236             disabled: true,
19237             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19238         });
19239     //this.addSeparator();
19240         this.loading = this.navgroup.addItem({
19241             tooltip: this.refreshText,
19242             icon: 'fa fa-refresh',
19243             
19244             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19245         });
19246
19247     },
19248
19249     // private
19250     updateInfo : function(){
19251         if(this.displayEl){
19252             var count = this.ds.getCount();
19253             var msg = count == 0 ?
19254                 this.emptyMsg :
19255                 String.format(
19256                     this.displayMsg,
19257                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19258                 );
19259             this.displayEl.update(msg);
19260         }
19261     },
19262
19263     // private
19264     onLoad : function(ds, r, o){
19265        this.cursor = o.params ? o.params.start : 0;
19266        var d = this.getPageData(),
19267             ap = d.activePage,
19268             ps = d.pages;
19269         
19270        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19271        this.field.dom.value = ap;
19272        this.first.setDisabled(ap == 1);
19273        this.prev.setDisabled(ap == 1);
19274        this.next.setDisabled(ap == ps);
19275        this.last.setDisabled(ap == ps);
19276        this.loading.enable();
19277        this.updateInfo();
19278     },
19279
19280     // private
19281     getPageData : function(){
19282         var total = this.ds.getTotalCount();
19283         return {
19284             total : total,
19285             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19286             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19287         };
19288     },
19289
19290     // private
19291     onLoadError : function(){
19292         this.loading.enable();
19293     },
19294
19295     // private
19296     onPagingKeydown : function(e){
19297         var k = e.getKey();
19298         var d = this.getPageData();
19299         if(k == e.RETURN){
19300             var v = this.field.dom.value, pageNum;
19301             if(!v || isNaN(pageNum = parseInt(v, 10))){
19302                 this.field.dom.value = d.activePage;
19303                 return;
19304             }
19305             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19306             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19307             e.stopEvent();
19308         }
19309         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))
19310         {
19311           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19312           this.field.dom.value = pageNum;
19313           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19314           e.stopEvent();
19315         }
19316         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19317         {
19318           var v = this.field.dom.value, pageNum; 
19319           var increment = (e.shiftKey) ? 10 : 1;
19320           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19321             increment *= -1;
19322           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19323             this.field.dom.value = d.activePage;
19324             return;
19325           }
19326           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19327           {
19328             this.field.dom.value = parseInt(v, 10) + increment;
19329             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19330             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19331           }
19332           e.stopEvent();
19333         }
19334     },
19335
19336     // private
19337     beforeLoad : function(){
19338         if(this.loading){
19339             this.loading.disable();
19340         }
19341     },
19342
19343     // private
19344     onClick : function(which){
19345         var ds = this.ds;
19346         if (!ds) {
19347             return;
19348         }
19349         switch(which){
19350             case "first":
19351                 ds.load({params:{start: 0, limit: this.pageSize}});
19352             break;
19353             case "prev":
19354                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19355             break;
19356             case "next":
19357                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19358             break;
19359             case "last":
19360                 var total = ds.getTotalCount();
19361                 var extra = total % this.pageSize;
19362                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19363                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19364             break;
19365             case "refresh":
19366                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19367             break;
19368         }
19369     },
19370
19371     /**
19372      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19373      * @param {Roo.data.Store} store The data store to unbind
19374      */
19375     unbind : function(ds){
19376         ds.un("beforeload", this.beforeLoad, this);
19377         ds.un("load", this.onLoad, this);
19378         ds.un("loadexception", this.onLoadError, this);
19379         ds.un("remove", this.updateInfo, this);
19380         ds.un("add", this.updateInfo, this);
19381         this.ds = undefined;
19382     },
19383
19384     /**
19385      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19386      * @param {Roo.data.Store} store The data store to bind
19387      */
19388     bind : function(ds){
19389         ds.on("beforeload", this.beforeLoad, this);
19390         ds.on("load", this.onLoad, this);
19391         ds.on("loadexception", this.onLoadError, this);
19392         ds.on("remove", this.updateInfo, this);
19393         ds.on("add", this.updateInfo, this);
19394         this.ds = ds;
19395     }
19396 });/*
19397  * - LGPL
19398  *
19399  * element
19400  * 
19401  */
19402
19403 /**
19404  * @class Roo.bootstrap.MessageBar
19405  * @extends Roo.bootstrap.Component
19406  * Bootstrap MessageBar class
19407  * @cfg {String} html contents of the MessageBar
19408  * @cfg {String} weight (info | success | warning | danger) default info
19409  * @cfg {String} beforeClass insert the bar before the given class
19410  * @cfg {Boolean} closable (true | false) default false
19411  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19412  * 
19413  * @constructor
19414  * Create a new Element
19415  * @param {Object} config The config object
19416  */
19417
19418 Roo.bootstrap.MessageBar = function(config){
19419     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19420 };
19421
19422 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19423     
19424     html: '',
19425     weight: 'info',
19426     closable: false,
19427     fixed: false,
19428     beforeClass: 'bootstrap-sticky-wrap',
19429     
19430     getAutoCreate : function(){
19431         
19432         var cfg = {
19433             tag: 'div',
19434             cls: 'alert alert-dismissable alert-' + this.weight,
19435             cn: [
19436                 {
19437                     tag: 'span',
19438                     cls: 'message',
19439                     html: this.html || ''
19440                 }
19441             ]
19442         }
19443         
19444         if(this.fixed){
19445             cfg.cls += ' alert-messages-fixed';
19446         }
19447         
19448         if(this.closable){
19449             cfg.cn.push({
19450                 tag: 'button',
19451                 cls: 'close',
19452                 html: 'x'
19453             });
19454         }
19455         
19456         return cfg;
19457     },
19458     
19459     onRender : function(ct, position)
19460     {
19461         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19462         
19463         if(!this.el){
19464             var cfg = Roo.apply({},  this.getAutoCreate());
19465             cfg.id = Roo.id();
19466             
19467             if (this.cls) {
19468                 cfg.cls += ' ' + this.cls;
19469             }
19470             if (this.style) {
19471                 cfg.style = this.style;
19472             }
19473             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19474             
19475             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19476         }
19477         
19478         this.el.select('>button.close').on('click', this.hide, this);
19479         
19480     },
19481     
19482     show : function()
19483     {
19484         if (!this.rendered) {
19485             this.render();
19486         }
19487         
19488         this.el.show();
19489         
19490         this.fireEvent('show', this);
19491         
19492     },
19493     
19494     hide : function()
19495     {
19496         if (!this.rendered) {
19497             this.render();
19498         }
19499         
19500         this.el.hide();
19501         
19502         this.fireEvent('hide', this);
19503     },
19504     
19505     update : function()
19506     {
19507 //        var e = this.el.dom.firstChild;
19508 //        
19509 //        if(this.closable){
19510 //            e = e.nextSibling;
19511 //        }
19512 //        
19513 //        e.data = this.html || '';
19514
19515         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19516     }
19517    
19518 });
19519
19520  
19521
19522      /*
19523  * - LGPL
19524  *
19525  * Graph
19526  * 
19527  */
19528
19529
19530 /**
19531  * @class Roo.bootstrap.Graph
19532  * @extends Roo.bootstrap.Component
19533  * Bootstrap Graph class
19534 > Prameters
19535  -sm {number} sm 4
19536  -md {number} md 5
19537  @cfg {String} graphtype  bar | vbar | pie
19538  @cfg {number} g_x coodinator | centre x (pie)
19539  @cfg {number} g_y coodinator | centre y (pie)
19540  @cfg {number} g_r radius (pie)
19541  @cfg {number} g_height height of the chart (respected by all elements in the set)
19542  @cfg {number} g_width width of the chart (respected by all elements in the set)
19543  @cfg {Object} title The title of the chart
19544     
19545  -{Array}  values
19546  -opts (object) options for the chart 
19547      o {
19548      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19549      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19550      o vgutter (number)
19551      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.
19552      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19553      o to
19554      o stretch (boolean)
19555      o }
19556  -opts (object) options for the pie
19557      o{
19558      o cut
19559      o startAngle (number)
19560      o endAngle (number)
19561      } 
19562  *
19563  * @constructor
19564  * Create a new Input
19565  * @param {Object} config The config object
19566  */
19567
19568 Roo.bootstrap.Graph = function(config){
19569     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19570     
19571     this.addEvents({
19572         // img events
19573         /**
19574          * @event click
19575          * The img click event for the img.
19576          * @param {Roo.EventObject} e
19577          */
19578         "click" : true
19579     });
19580 };
19581
19582 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19583     
19584     sm: 4,
19585     md: 5,
19586     graphtype: 'bar',
19587     g_height: 250,
19588     g_width: 400,
19589     g_x: 50,
19590     g_y: 50,
19591     g_r: 30,
19592     opts:{
19593         //g_colors: this.colors,
19594         g_type: 'soft',
19595         g_gutter: '20%'
19596
19597     },
19598     title : false,
19599
19600     getAutoCreate : function(){
19601         
19602         var cfg = {
19603             tag: 'div',
19604             html : null
19605         }
19606         
19607         
19608         return  cfg;
19609     },
19610
19611     onRender : function(ct,position){
19612         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19613         this.raphael = Raphael(this.el.dom);
19614         
19615                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19616                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19617                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19618                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19619                 /*
19620                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19621                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19622                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19623                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19624                 
19625                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19626                 r.barchart(330, 10, 300, 220, data1);
19627                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19628                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19629                 */
19630                 
19631                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19632                 // r.barchart(30, 30, 560, 250,  xdata, {
19633                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19634                 //     axis : "0 0 1 1",
19635                 //     axisxlabels :  xdata
19636                 //     //yvalues : cols,
19637                    
19638                 // });
19639 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19640 //        
19641 //        this.load(null,xdata,{
19642 //                axis : "0 0 1 1",
19643 //                axisxlabels :  xdata
19644 //                });
19645
19646     },
19647
19648     load : function(graphtype,xdata,opts){
19649         this.raphael.clear();
19650         if(!graphtype) {
19651             graphtype = this.graphtype;
19652         }
19653         if(!opts){
19654             opts = this.opts;
19655         }
19656         var r = this.raphael,
19657             fin = function () {
19658                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19659             },
19660             fout = function () {
19661                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19662             },
19663             pfin = function() {
19664                 this.sector.stop();
19665                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19666
19667                 if (this.label) {
19668                     this.label[0].stop();
19669                     this.label[0].attr({ r: 7.5 });
19670                     this.label[1].attr({ "font-weight": 800 });
19671                 }
19672             },
19673             pfout = function() {
19674                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19675
19676                 if (this.label) {
19677                     this.label[0].animate({ r: 5 }, 500, "bounce");
19678                     this.label[1].attr({ "font-weight": 400 });
19679                 }
19680             };
19681
19682         switch(graphtype){
19683             case 'bar':
19684                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19685                 break;
19686             case 'hbar':
19687                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19688                 break;
19689             case 'pie':
19690 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19691 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19692 //            
19693                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19694                 
19695                 break;
19696
19697         }
19698         
19699         if(this.title){
19700             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19701         }
19702         
19703     },
19704     
19705     setTitle: function(o)
19706     {
19707         this.title = o;
19708     },
19709     
19710     initEvents: function() {
19711         
19712         if(!this.href){
19713             this.el.on('click', this.onClick, this);
19714         }
19715     },
19716     
19717     onClick : function(e)
19718     {
19719         Roo.log('img onclick');
19720         this.fireEvent('click', this, e);
19721     }
19722    
19723 });
19724
19725  
19726 /*
19727  * - LGPL
19728  *
19729  * numberBox
19730  * 
19731  */
19732 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19733
19734 /**
19735  * @class Roo.bootstrap.dash.NumberBox
19736  * @extends Roo.bootstrap.Component
19737  * Bootstrap NumberBox class
19738  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19739  * @cfg {String} headline Box headline
19740  * @cfg {String} content Box content
19741  * @cfg {String} icon Box icon
19742  * @cfg {String} footer Footer text
19743  * @cfg {String} fhref Footer href
19744  * 
19745  * @constructor
19746  * Create a new NumberBox
19747  * @param {Object} config The config object
19748  */
19749
19750
19751 Roo.bootstrap.dash.NumberBox = function(config){
19752     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19753     
19754 };
19755
19756 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19757     
19758     bgcolor : 'aqua',
19759     headline : '',
19760     content : '',
19761     icon : '',
19762     footer : '',
19763     fhref : '',
19764     ficon : '',
19765     
19766     getAutoCreate : function(){
19767         
19768         var cfg = {
19769             tag : 'div',
19770             cls : 'small-box bg-' + this.bgcolor,
19771             cn : [
19772                 {
19773                     tag : 'div',
19774                     cls : 'inner',
19775                     cn :[
19776                         {
19777                             tag : 'h3',
19778                             cls : 'roo-headline',
19779                             html : this.headline
19780                         },
19781                         {
19782                             tag : 'p',
19783                             cls : 'roo-content',
19784                             html : this.content
19785                         }
19786                     ]
19787                 }
19788             ]
19789         }
19790         
19791         if(this.icon){
19792             cfg.cn.push({
19793                 tag : 'div',
19794                 cls : 'icon',
19795                 cn :[
19796                     {
19797                         tag : 'i',
19798                         cls : 'ion ' + this.icon
19799                     }
19800                 ]
19801             });
19802         }
19803         
19804         if(this.footer){
19805             var footer = {
19806                 tag : 'a',
19807                 cls : 'small-box-footer',
19808                 href : this.fhref || '#',
19809                 html : this.footer
19810             };
19811             
19812             cfg.cn.push(footer);
19813             
19814         }
19815         
19816         return  cfg;
19817     },
19818
19819     onRender : function(ct,position){
19820         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19821
19822
19823        
19824                 
19825     },
19826
19827     setHeadline: function (value)
19828     {
19829         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19830     },
19831     
19832     setFooter: function (value, href)
19833     {
19834         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19835         
19836         if(href){
19837             this.el.select('a.small-box-footer',true).first().attr('href', href);
19838         }
19839         
19840     },
19841
19842     setContent: function (value)
19843     {
19844         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19845     },
19846
19847     initEvents: function() 
19848     {   
19849         
19850     }
19851     
19852 });
19853
19854  
19855 /*
19856  * - LGPL
19857  *
19858  * TabBox
19859  * 
19860  */
19861 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19862
19863 /**
19864  * @class Roo.bootstrap.dash.TabBox
19865  * @extends Roo.bootstrap.Component
19866  * Bootstrap TabBox class
19867  * @cfg {String} title Title of the TabBox
19868  * @cfg {String} icon Icon of the TabBox
19869  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19870  * 
19871  * @constructor
19872  * Create a new TabBox
19873  * @param {Object} config The config object
19874  */
19875
19876
19877 Roo.bootstrap.dash.TabBox = function(config){
19878     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19879     this.addEvents({
19880         // raw events
19881         /**
19882          * @event addpane
19883          * When a pane is added
19884          * @param {Roo.bootstrap.dash.TabPane} pane
19885          */
19886         "addpane" : true
19887          
19888     });
19889 };
19890
19891 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19892
19893     title : '',
19894     icon : false,
19895     showtabs : true,
19896     
19897     getChildContainer : function()
19898     {
19899         return this.el.select('.tab-content', true).first();
19900     },
19901     
19902     getAutoCreate : function(){
19903         
19904         var header = {
19905             tag: 'li',
19906             cls: 'pull-left header',
19907             html: this.title,
19908             cn : []
19909         };
19910         
19911         if(this.icon){
19912             header.cn.push({
19913                 tag: 'i',
19914                 cls: 'fa ' + this.icon
19915             });
19916         }
19917         
19918         
19919         var cfg = {
19920             tag: 'div',
19921             cls: 'nav-tabs-custom',
19922             cn: [
19923                 {
19924                     tag: 'ul',
19925                     cls: 'nav nav-tabs pull-right',
19926                     cn: [
19927                         header
19928                     ]
19929                 },
19930                 {
19931                     tag: 'div',
19932                     cls: 'tab-content no-padding',
19933                     cn: []
19934                 }
19935             ]
19936         }
19937
19938         return  cfg;
19939     },
19940     initEvents : function()
19941     {
19942         //Roo.log('add add pane handler');
19943         this.on('addpane', this.onAddPane, this);
19944     },
19945      /**
19946      * Updates the box title
19947      * @param {String} html to set the title to.
19948      */
19949     setTitle : function(value)
19950     {
19951         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19952     },
19953     onAddPane : function(pane)
19954     {
19955         //Roo.log('addpane');
19956         //Roo.log(pane);
19957         // tabs are rendere left to right..
19958         if(!this.showtabs){
19959             return;
19960         }
19961         
19962         var ctr = this.el.select('.nav-tabs', true).first();
19963          
19964          
19965         var existing = ctr.select('.nav-tab',true);
19966         var qty = existing.getCount();;
19967         
19968         
19969         var tab = ctr.createChild({
19970             tag : 'li',
19971             cls : 'nav-tab' + (qty ? '' : ' active'),
19972             cn : [
19973                 {
19974                     tag : 'a',
19975                     href:'#',
19976                     html : pane.title
19977                 }
19978             ]
19979         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19980         pane.tab = tab;
19981         
19982         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19983         if (!qty) {
19984             pane.el.addClass('active');
19985         }
19986         
19987                 
19988     },
19989     onTabClick : function(ev,un,ob,pane)
19990     {
19991         //Roo.log('tab - prev default');
19992         ev.preventDefault();
19993         
19994         
19995         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19996         pane.tab.addClass('active');
19997         //Roo.log(pane.title);
19998         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19999         // technically we should have a deactivate event.. but maybe add later.
20000         // and it should not de-activate the selected tab...
20001         
20002         pane.el.addClass('active');
20003         pane.fireEvent('activate');
20004         
20005         
20006     }
20007     
20008     
20009 });
20010
20011  
20012 /*
20013  * - LGPL
20014  *
20015  * Tab pane
20016  * 
20017  */
20018 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20019 /**
20020  * @class Roo.bootstrap.TabPane
20021  * @extends Roo.bootstrap.Component
20022  * Bootstrap TabPane class
20023  * @cfg {Boolean} active (false | true) Default false
20024  * @cfg {String} title title of panel
20025
20026  * 
20027  * @constructor
20028  * Create a new TabPane
20029  * @param {Object} config The config object
20030  */
20031
20032 Roo.bootstrap.dash.TabPane = function(config){
20033     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20034     
20035     this.addEvents({
20036         // raw events
20037         /**
20038          * @event activate
20039          * When a pane is activated
20040          * @param {Roo.bootstrap.dash.TabPane} pane
20041          */
20042         "activate" : true
20043          
20044     });
20045 };
20046
20047 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20048     
20049     active : false,
20050     title : '',
20051     
20052     // the tabBox that this is attached to.
20053     tab : false,
20054      
20055     getAutoCreate : function() 
20056     {
20057         var cfg = {
20058             tag: 'div',
20059             cls: 'tab-pane'
20060         }
20061         
20062         if(this.active){
20063             cfg.cls += ' active';
20064         }
20065         
20066         return cfg;
20067     },
20068     initEvents  : function()
20069     {
20070         //Roo.log('trigger add pane handler');
20071         this.parent().fireEvent('addpane', this)
20072     },
20073     
20074      /**
20075      * Updates the tab title 
20076      * @param {String} html to set the title to.
20077      */
20078     setTitle: function(str)
20079     {
20080         if (!this.tab) {
20081             return;
20082         }
20083         this.title = str;
20084         this.tab.select('a', true).first().dom.innerHTML = str;
20085         
20086     }
20087     
20088     
20089     
20090 });
20091
20092  
20093
20094
20095  /*
20096  * - LGPL
20097  *
20098  * menu
20099  * 
20100  */
20101 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20102
20103 /**
20104  * @class Roo.bootstrap.menu.Menu
20105  * @extends Roo.bootstrap.Component
20106  * Bootstrap Menu class - container for Menu
20107  * @cfg {String} html Text of the menu
20108  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20109  * @cfg {String} icon Font awesome icon
20110  * @cfg {String} pos Menu align to (top | bottom) default bottom
20111  * 
20112  * 
20113  * @constructor
20114  * Create a new Menu
20115  * @param {Object} config The config object
20116  */
20117
20118
20119 Roo.bootstrap.menu.Menu = function(config){
20120     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20121     
20122     this.addEvents({
20123         /**
20124          * @event beforeshow
20125          * Fires before this menu is displayed
20126          * @param {Roo.bootstrap.menu.Menu} this
20127          */
20128         beforeshow : true,
20129         /**
20130          * @event beforehide
20131          * Fires before this menu is hidden
20132          * @param {Roo.bootstrap.menu.Menu} this
20133          */
20134         beforehide : true,
20135         /**
20136          * @event show
20137          * Fires after this menu is displayed
20138          * @param {Roo.bootstrap.menu.Menu} this
20139          */
20140         show : true,
20141         /**
20142          * @event hide
20143          * Fires after this menu is hidden
20144          * @param {Roo.bootstrap.menu.Menu} this
20145          */
20146         hide : true,
20147         /**
20148          * @event click
20149          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20150          * @param {Roo.bootstrap.menu.Menu} this
20151          * @param {Roo.EventObject} e
20152          */
20153         click : true
20154     });
20155     
20156 };
20157
20158 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20159     
20160     submenu : false,
20161     html : '',
20162     weight : 'default',
20163     icon : false,
20164     pos : 'bottom',
20165     
20166     
20167     getChildContainer : function() {
20168         if(this.isSubMenu){
20169             return this.el;
20170         }
20171         
20172         return this.el.select('ul.dropdown-menu', true).first();  
20173     },
20174     
20175     getAutoCreate : function()
20176     {
20177         var text = [
20178             {
20179                 tag : 'span',
20180                 cls : 'roo-menu-text',
20181                 html : this.html
20182             }
20183         ];
20184         
20185         if(this.icon){
20186             text.unshift({
20187                 tag : 'i',
20188                 cls : 'fa ' + this.icon
20189             })
20190         }
20191         
20192         
20193         var cfg = {
20194             tag : 'div',
20195             cls : 'btn-group',
20196             cn : [
20197                 {
20198                     tag : 'button',
20199                     cls : 'dropdown-button btn btn-' + this.weight,
20200                     cn : text
20201                 },
20202                 {
20203                     tag : 'button',
20204                     cls : 'dropdown-toggle btn btn-' + this.weight,
20205                     cn : [
20206                         {
20207                             tag : 'span',
20208                             cls : 'caret'
20209                         }
20210                     ]
20211                 },
20212                 {
20213                     tag : 'ul',
20214                     cls : 'dropdown-menu'
20215                 }
20216             ]
20217             
20218         };
20219         
20220         if(this.pos == 'top'){
20221             cfg.cls += ' dropup';
20222         }
20223         
20224         if(this.isSubMenu){
20225             cfg = {
20226                 tag : 'ul',
20227                 cls : 'dropdown-menu'
20228             }
20229         }
20230         
20231         return cfg;
20232     },
20233     
20234     onRender : function(ct, position)
20235     {
20236         this.isSubMenu = ct.hasClass('dropdown-submenu');
20237         
20238         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20239     },
20240     
20241     initEvents : function() 
20242     {
20243         if(this.isSubMenu){
20244             return;
20245         }
20246         
20247         this.hidden = true;
20248         
20249         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20250         this.triggerEl.on('click', this.onTriggerPress, this);
20251         
20252         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20253         this.buttonEl.on('click', this.onClick, this);
20254         
20255     },
20256     
20257     list : function()
20258     {
20259         if(this.isSubMenu){
20260             return this.el;
20261         }
20262         
20263         return this.el.select('ul.dropdown-menu', true).first();
20264     },
20265     
20266     onClick : function(e)
20267     {
20268         this.fireEvent("click", this, e);
20269     },
20270     
20271     onTriggerPress  : function(e)
20272     {   
20273         if (this.isVisible()) {
20274             this.hide();
20275         } else {
20276             this.show();
20277         }
20278     },
20279     
20280     isVisible : function(){
20281         return !this.hidden;
20282     },
20283     
20284     show : function()
20285     {
20286         this.fireEvent("beforeshow", this);
20287         
20288         this.hidden = false;
20289         this.el.addClass('open');
20290         
20291         Roo.get(document).on("mouseup", this.onMouseUp, this);
20292         
20293         this.fireEvent("show", this);
20294         
20295         
20296     },
20297     
20298     hide : function()
20299     {
20300         this.fireEvent("beforehide", this);
20301         
20302         this.hidden = true;
20303         this.el.removeClass('open');
20304         
20305         Roo.get(document).un("mouseup", this.onMouseUp);
20306         
20307         this.fireEvent("hide", this);
20308     },
20309     
20310     onMouseUp : function()
20311     {
20312         this.hide();
20313     }
20314     
20315 });
20316
20317  
20318  /*
20319  * - LGPL
20320  *
20321  * menu item
20322  * 
20323  */
20324 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20325
20326 /**
20327  * @class Roo.bootstrap.menu.Item
20328  * @extends Roo.bootstrap.Component
20329  * Bootstrap MenuItem class
20330  * @cfg {Boolean} submenu (true | false) default false
20331  * @cfg {String} html text of the item
20332  * @cfg {String} href the link
20333  * @cfg {Boolean} disable (true | false) default false
20334  * @cfg {Boolean} preventDefault (true | false) default true
20335  * @cfg {String} icon Font awesome icon
20336  * @cfg {String} pos Submenu align to (left | right) default right 
20337  * 
20338  * 
20339  * @constructor
20340  * Create a new Item
20341  * @param {Object} config The config object
20342  */
20343
20344
20345 Roo.bootstrap.menu.Item = function(config){
20346     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20347     this.addEvents({
20348         /**
20349          * @event mouseover
20350          * Fires when the mouse is hovering over this menu
20351          * @param {Roo.bootstrap.menu.Item} this
20352          * @param {Roo.EventObject} e
20353          */
20354         mouseover : true,
20355         /**
20356          * @event mouseout
20357          * Fires when the mouse exits this menu
20358          * @param {Roo.bootstrap.menu.Item} this
20359          * @param {Roo.EventObject} e
20360          */
20361         mouseout : true,
20362         // raw events
20363         /**
20364          * @event click
20365          * The raw click event for the entire grid.
20366          * @param {Roo.EventObject} e
20367          */
20368         click : true
20369     });
20370 };
20371
20372 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20373     
20374     submenu : false,
20375     href : '',
20376     html : '',
20377     preventDefault: true,
20378     disable : false,
20379     icon : false,
20380     pos : 'right',
20381     
20382     getAutoCreate : function()
20383     {
20384         var text = [
20385             {
20386                 tag : 'span',
20387                 cls : 'roo-menu-item-text',
20388                 html : this.html
20389             }
20390         ];
20391         
20392         if(this.icon){
20393             text.unshift({
20394                 tag : 'i',
20395                 cls : 'fa ' + this.icon
20396             })
20397         }
20398         
20399         var cfg = {
20400             tag : 'li',
20401             cn : [
20402                 {
20403                     tag : 'a',
20404                     href : this.href || '#',
20405                     cn : text
20406                 }
20407             ]
20408         };
20409         
20410         if(this.disable){
20411             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20412         }
20413         
20414         if(this.submenu){
20415             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20416             
20417             if(this.pos == 'left'){
20418                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20419             }
20420         }
20421         
20422         return cfg;
20423     },
20424     
20425     initEvents : function() 
20426     {
20427         this.el.on('mouseover', this.onMouseOver, this);
20428         this.el.on('mouseout', this.onMouseOut, this);
20429         
20430         this.el.select('a', true).first().on('click', this.onClick, this);
20431         
20432     },
20433     
20434     onClick : function(e)
20435     {
20436         if(this.preventDefault){
20437             e.preventDefault();
20438         }
20439         
20440         this.fireEvent("click", this, e);
20441     },
20442     
20443     onMouseOver : function(e)
20444     {
20445         if(this.submenu && this.pos == 'left'){
20446             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20447         }
20448         
20449         this.fireEvent("mouseover", this, e);
20450     },
20451     
20452     onMouseOut : function(e)
20453     {
20454         this.fireEvent("mouseout", this, e);
20455     }
20456 });
20457
20458  
20459
20460  /*
20461  * - LGPL
20462  *
20463  * menu separator
20464  * 
20465  */
20466 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20467
20468 /**
20469  * @class Roo.bootstrap.menu.Separator
20470  * @extends Roo.bootstrap.Component
20471  * Bootstrap Separator class
20472  * 
20473  * @constructor
20474  * Create a new Separator
20475  * @param {Object} config The config object
20476  */
20477
20478
20479 Roo.bootstrap.menu.Separator = function(config){
20480     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20481 };
20482
20483 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20484     
20485     getAutoCreate : function(){
20486         var cfg = {
20487             tag : 'li',
20488             cls: 'divider'
20489         };
20490         
20491         return cfg;
20492     }
20493    
20494 });
20495
20496  
20497
20498