examples/bootstrap/contact.bjs
[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  * 
2033  * @constructor
2034  * Create a new Modal Dialog
2035  * @param {Object} config The config object
2036  */
2037
2038 Roo.bootstrap.Modal = function(config){
2039     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2040     this.addEvents({
2041         // raw events
2042         /**
2043          * @event btnclick
2044          * The raw btnclick event for the button
2045          * @param {Roo.EventObject} e
2046          */
2047         "btnclick" : true
2048     });
2049     this.buttons = this.buttons || [];
2050 };
2051
2052 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2053     
2054     title : 'test dialog',
2055    
2056     buttons : false,
2057     
2058     // set on load...
2059     body:  false,
2060     
2061     specificTitle: false,
2062     
2063     buttonPosition: 'right',
2064     
2065     onRender : function(ct, position)
2066     {
2067         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2068      
2069         if(!this.el){
2070             var cfg = Roo.apply({},  this.getAutoCreate());
2071             cfg.id = Roo.id();
2072             //if(!cfg.name){
2073             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2074             //}
2075             //if (!cfg.name.length) {
2076             //    delete cfg.name;
2077            // }
2078             if (this.cls) {
2079                 cfg.cls += ' ' + this.cls;
2080             }
2081             if (this.style) {
2082                 cfg.style = this.style;
2083             }
2084             this.el = Roo.get(document.body).createChild(cfg, position);
2085         }
2086         //var type = this.el.dom.type;
2087         
2088         if(this.tabIndex !== undefined){
2089             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2090         }
2091         
2092         
2093         
2094         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2095         this.maskEl.enableDisplayMode("block");
2096         this.maskEl.hide();
2097         //this.el.addClass("x-dlg-modal");
2098     
2099         if (this.buttons.length) {
2100             Roo.each(this.buttons, function(bb) {
2101                 b = Roo.apply({}, bb);
2102                 b.xns = b.xns || Roo.bootstrap;
2103                 b.xtype = b.xtype || 'Button';
2104                 if (typeof(b.listeners) == 'undefined') {
2105                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2106                 }
2107                 
2108                 var btn = Roo.factory(b);
2109                 
2110                 btn.onRender(this.el.select('.modal-footer div').first());
2111                 
2112             },this);
2113         }
2114         // render the children.
2115         var nitems = [];
2116         
2117         if(typeof(this.items) != 'undefined'){
2118             var items = this.items;
2119             delete this.items;
2120
2121             for(var i =0;i < items.length;i++) {
2122                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2123             }
2124         }
2125         
2126         this.items = nitems;
2127         
2128         this.body = this.el.select('.modal-body',true).first();
2129         this.close = this.el.select('.modal-header .close', true).first();
2130         this.footer = this.el.select('.modal-footer',true).first();
2131         this.initEvents();
2132         //this.el.addClass([this.fieldClass, this.cls]);
2133         
2134     },
2135     getAutoCreate : function(){
2136         
2137         
2138         var bdy = {
2139                 cls : 'modal-body',
2140                 html : this.html || ''
2141         };
2142         
2143         var title = {
2144             tag: 'h4',
2145             cls : 'modal-title',
2146             html : this.title
2147         };
2148         
2149         if(this.specificTitle){
2150             title = this.title;
2151         };
2152         
2153         return modal = {
2154             cls: "modal fade",
2155             style : 'display: none',
2156             cn : [
2157                 {
2158                     cls: "modal-dialog",
2159                     cn : [
2160                         {
2161                             cls : "modal-content",
2162                             cn : [
2163                                 {
2164                                     cls : 'modal-header',
2165                                     cn : [
2166                                         {
2167                                             tag: 'button',
2168                                             cls : 'close',
2169                                             html : '&times'
2170                                         },
2171                                         title
2172                                     ]
2173                                 },
2174                                 bdy,
2175                                 {
2176                                     cls : 'modal-footer',
2177                                     cn : [
2178                                         {
2179                                             tag: 'div',
2180                                             cls: 'btn-' + this.buttonPosition
2181                                         }
2182                                     ]
2183                                     
2184                                 }
2185                                 
2186                                 
2187                             ]
2188                             
2189                         }
2190                     ]
2191                         
2192                 }
2193             ]
2194             
2195             
2196         };
2197           
2198     },
2199     getChildContainer : function() {
2200          
2201          return this.el.select('.modal-body',true).first();
2202         
2203     },
2204     getButtonContainer : function() {
2205          return this.el.select('.modal-footer div',true).first();
2206         
2207     },
2208     initEvents : function()
2209     {
2210         this.el.select('.modal-header .close').on('click', this.hide, this);
2211 //        
2212 //        this.addxtype(this);
2213     },
2214     show : function() {
2215         
2216         if (!this.rendered) {
2217             this.render();
2218         }
2219        
2220         this.el.addClass('on');
2221         this.el.removeClass('fade');
2222         this.el.setStyle('display', 'block');
2223         Roo.get(document.body).addClass("x-body-masked");
2224         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2225         this.maskEl.show();
2226         this.el.setStyle('zIndex', '10001');
2227         this.fireEvent('show', this);
2228         
2229         
2230     },
2231     hide : function()
2232     {
2233         Roo.log('Modal hide?!');
2234         this.maskEl.hide();
2235         Roo.get(document.body).removeClass("x-body-masked");
2236         this.el.removeClass('on');
2237         this.el.addClass('fade');
2238         this.el.setStyle('display', 'none');
2239         this.fireEvent('hide', this);
2240     },
2241     
2242     addButton : function(str, cb)
2243     {
2244          
2245         
2246         var b = Roo.apply({}, { html : str } );
2247         b.xns = b.xns || Roo.bootstrap;
2248         b.xtype = b.xtype || 'Button';
2249         if (typeof(b.listeners) == 'undefined') {
2250             b.listeners = { click : cb.createDelegate(this)  };
2251         }
2252         
2253         var btn = Roo.factory(b);
2254            
2255         btn.onRender(this.el.select('.modal-footer div').first());
2256         
2257         return btn;   
2258        
2259     },
2260     
2261     setDefaultButton : function(btn)
2262     {
2263         //this.el.select('.modal-footer').()
2264     },
2265     resizeTo: function(w,h)
2266     {
2267         // skip..
2268     },
2269     setContentSize  : function(w, h)
2270     {
2271         
2272     },
2273     onButtonClick: function(btn,e)
2274     {
2275         //Roo.log([a,b,c]);
2276         this.fireEvent('btnclick', btn.name, e);
2277     },
2278     setTitle: function(str) {
2279         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2280         
2281     }
2282 });
2283
2284
2285 Roo.apply(Roo.bootstrap.Modal,  {
2286     /**
2287          * Button config that displays a single OK button
2288          * @type Object
2289          */
2290         OK :  [{
2291             name : 'ok',
2292             weight : 'primary',
2293             html : 'OK'
2294         }], 
2295         /**
2296          * Button config that displays Yes and No buttons
2297          * @type Object
2298          */
2299         YESNO : [
2300             {
2301                 name  : 'no',
2302                 html : 'No'
2303             },
2304             {
2305                 name  :'yes',
2306                 weight : 'primary',
2307                 html : 'Yes'
2308             }
2309         ],
2310         
2311         /**
2312          * Button config that displays OK and Cancel buttons
2313          * @type Object
2314          */
2315         OKCANCEL : [
2316             {
2317                name : 'cancel',
2318                 html : 'Cancel'
2319             },
2320             {
2321                 name : 'ok',
2322                 weight : 'primary',
2323                 html : 'OK'
2324             }
2325         ],
2326         /**
2327          * Button config that displays Yes, No and Cancel buttons
2328          * @type Object
2329          */
2330         YESNOCANCEL : [
2331             {
2332                 name : 'yes',
2333                 weight : 'primary',
2334                 html : 'Yes'
2335             },
2336             {
2337                 name : 'no',
2338                 html : 'No'
2339             },
2340             {
2341                 name : 'cancel',
2342                 html : 'Cancel'
2343             }
2344         ]
2345 });
2346  /*
2347  * - LGPL
2348  *
2349  * messagebox - can be used as a replace
2350  * 
2351  */
2352 /**
2353  * @class Roo.MessageBox
2354  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2355  * Example usage:
2356  *<pre><code>
2357 // Basic alert:
2358 Roo.Msg.alert('Status', 'Changes saved successfully.');
2359
2360 // Prompt for user data:
2361 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2362     if (btn == 'ok'){
2363         // process text value...
2364     }
2365 });
2366
2367 // Show a dialog using config options:
2368 Roo.Msg.show({
2369    title:'Save Changes?',
2370    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2371    buttons: Roo.Msg.YESNOCANCEL,
2372    fn: processResult,
2373    animEl: 'elId'
2374 });
2375 </code></pre>
2376  * @singleton
2377  */
2378 Roo.bootstrap.MessageBox = function(){
2379     var dlg, opt, mask, waitTimer;
2380     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2381     var buttons, activeTextEl, bwidth;
2382
2383     
2384     // private
2385     var handleButton = function(button){
2386         dlg.hide();
2387         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2388     };
2389
2390     // private
2391     var handleHide = function(){
2392         if(opt && opt.cls){
2393             dlg.el.removeClass(opt.cls);
2394         }
2395         //if(waitTimer){
2396         //    Roo.TaskMgr.stop(waitTimer);
2397         //    waitTimer = null;
2398         //}
2399     };
2400
2401     // private
2402     var updateButtons = function(b){
2403         var width = 0;
2404         if(!b){
2405             buttons["ok"].hide();
2406             buttons["cancel"].hide();
2407             buttons["yes"].hide();
2408             buttons["no"].hide();
2409             //dlg.footer.dom.style.display = 'none';
2410             return width;
2411         }
2412         dlg.footer.dom.style.display = '';
2413         for(var k in buttons){
2414             if(typeof buttons[k] != "function"){
2415                 if(b[k]){
2416                     buttons[k].show();
2417                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2418                     width += buttons[k].el.getWidth()+15;
2419                 }else{
2420                     buttons[k].hide();
2421                 }
2422             }
2423         }
2424         return width;
2425     };
2426
2427     // private
2428     var handleEsc = function(d, k, e){
2429         if(opt && opt.closable !== false){
2430             dlg.hide();
2431         }
2432         if(e){
2433             e.stopEvent();
2434         }
2435     };
2436
2437     return {
2438         /**
2439          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2440          * @return {Roo.BasicDialog} The BasicDialog element
2441          */
2442         getDialog : function(){
2443            if(!dlg){
2444                 dlg = new Roo.bootstrap.Modal( {
2445                     //draggable: true,
2446                     //resizable:false,
2447                     //constraintoviewport:false,
2448                     //fixedcenter:true,
2449                     //collapsible : false,
2450                     //shim:true,
2451                     //modal: true,
2452                   //  width:400,
2453                   //  height:100,
2454                     //buttonAlign:"center",
2455                     closeClick : function(){
2456                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2457                             handleButton("no");
2458                         }else{
2459                             handleButton("cancel");
2460                         }
2461                     }
2462                 });
2463                 dlg.render();
2464                 dlg.on("hide", handleHide);
2465                 mask = dlg.mask;
2466                 //dlg.addKeyListener(27, handleEsc);
2467                 buttons = {};
2468                 this.buttons = buttons;
2469                 var bt = this.buttonText;
2470                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2471                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2472                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2473                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2474                 Roo.log(buttons)
2475                 bodyEl = dlg.body.createChild({
2476
2477                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2478                         '<textarea class="roo-mb-textarea"></textarea>' +
2479                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2480                 });
2481                 msgEl = bodyEl.dom.firstChild;
2482                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2483                 textboxEl.enableDisplayMode();
2484                 textboxEl.addKeyListener([10,13], function(){
2485                     if(dlg.isVisible() && opt && opt.buttons){
2486                         if(opt.buttons.ok){
2487                             handleButton("ok");
2488                         }else if(opt.buttons.yes){
2489                             handleButton("yes");
2490                         }
2491                     }
2492                 });
2493                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2494                 textareaEl.enableDisplayMode();
2495                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2496                 progressEl.enableDisplayMode();
2497                 var pf = progressEl.dom.firstChild;
2498                 if (pf) {
2499                     pp = Roo.get(pf.firstChild);
2500                     pp.setHeight(pf.offsetHeight);
2501                 }
2502                 
2503             }
2504             return dlg;
2505         },
2506
2507         /**
2508          * Updates the message box body text
2509          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2510          * the XHTML-compliant non-breaking space character '&amp;#160;')
2511          * @return {Roo.MessageBox} This message box
2512          */
2513         updateText : function(text){
2514             if(!dlg.isVisible() && !opt.width){
2515                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2516             }
2517             msgEl.innerHTML = text || '&#160;';
2518       
2519             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2520             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2521             var w = Math.max(
2522                     Math.min(opt.width || cw , this.maxWidth), 
2523                     Math.max(opt.minWidth || this.minWidth, bwidth)
2524             );
2525             if(opt.prompt){
2526                 activeTextEl.setWidth(w);
2527             }
2528             if(dlg.isVisible()){
2529                 dlg.fixedcenter = false;
2530             }
2531             // to big, make it scroll. = But as usual stupid IE does not support
2532             // !important..
2533             
2534             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2535                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2536                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2537             } else {
2538                 bodyEl.dom.style.height = '';
2539                 bodyEl.dom.style.overflowY = '';
2540             }
2541             if (cw > w) {
2542                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2543             } else {
2544                 bodyEl.dom.style.overflowX = '';
2545             }
2546             
2547             dlg.setContentSize(w, bodyEl.getHeight());
2548             if(dlg.isVisible()){
2549                 dlg.fixedcenter = true;
2550             }
2551             return this;
2552         },
2553
2554         /**
2555          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2556          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2557          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2558          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2559          * @return {Roo.MessageBox} This message box
2560          */
2561         updateProgress : function(value, text){
2562             if(text){
2563                 this.updateText(text);
2564             }
2565             if (pp) { // weird bug on my firefox - for some reason this is not defined
2566                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2567             }
2568             return this;
2569         },        
2570
2571         /**
2572          * Returns true if the message box is currently displayed
2573          * @return {Boolean} True if the message box is visible, else false
2574          */
2575         isVisible : function(){
2576             return dlg && dlg.isVisible();  
2577         },
2578
2579         /**
2580          * Hides the message box if it is displayed
2581          */
2582         hide : function(){
2583             if(this.isVisible()){
2584                 dlg.hide();
2585             }  
2586         },
2587
2588         /**
2589          * Displays a new message box, or reinitializes an existing message box, based on the config options
2590          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2591          * The following config object properties are supported:
2592          * <pre>
2593 Property    Type             Description
2594 ----------  ---------------  ------------------------------------------------------------------------------------
2595 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2596                                    closes (defaults to undefined)
2597 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2598                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2599 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2600                                    progress and wait dialogs will ignore this property and always hide the
2601                                    close button as they can only be closed programmatically.
2602 cls               String           A custom CSS class to apply to the message box element
2603 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2604                                    displayed (defaults to 75)
2605 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2606                                    function will be btn (the name of the button that was clicked, if applicable,
2607                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2608                                    Progress and wait dialogs will ignore this option since they do not respond to
2609                                    user actions and can only be closed programmatically, so any required function
2610                                    should be called by the same code after it closes the dialog.
2611 icon              String           A CSS class that provides a background image to be used as an icon for
2612                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2613 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2614 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2615 modal             Boolean          False to allow user interaction with the page while the message box is
2616                                    displayed (defaults to true)
2617 msg               String           A string that will replace the existing message box body text (defaults
2618                                    to the XHTML-compliant non-breaking space character '&#160;')
2619 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2620 progress          Boolean          True to display a progress bar (defaults to false)
2621 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2622 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2623 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2624 title             String           The title text
2625 value             String           The string value to set into the active textbox element if displayed
2626 wait              Boolean          True to display a progress bar (defaults to false)
2627 width             Number           The width of the dialog in pixels
2628 </pre>
2629          *
2630          * Example usage:
2631          * <pre><code>
2632 Roo.Msg.show({
2633    title: 'Address',
2634    msg: 'Please enter your address:',
2635    width: 300,
2636    buttons: Roo.MessageBox.OKCANCEL,
2637    multiline: true,
2638    fn: saveAddress,
2639    animEl: 'addAddressBtn'
2640 });
2641 </code></pre>
2642          * @param {Object} config Configuration options
2643          * @return {Roo.MessageBox} This message box
2644          */
2645         show : function(options)
2646         {
2647             
2648             // this causes nightmares if you show one dialog after another
2649             // especially on callbacks..
2650              
2651             if(this.isVisible()){
2652                 
2653                 this.hide();
2654                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2655                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2656                 Roo.log("New Dialog Message:" +  options.msg )
2657                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2658                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2659                 
2660             }
2661             var d = this.getDialog();
2662             opt = options;
2663             d.setTitle(opt.title || "&#160;");
2664             d.close.setDisplayed(opt.closable !== false);
2665             activeTextEl = textboxEl;
2666             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2667             if(opt.prompt){
2668                 if(opt.multiline){
2669                     textboxEl.hide();
2670                     textareaEl.show();
2671                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2672                         opt.multiline : this.defaultTextHeight);
2673                     activeTextEl = textareaEl;
2674                 }else{
2675                     textboxEl.show();
2676                     textareaEl.hide();
2677                 }
2678             }else{
2679                 textboxEl.hide();
2680                 textareaEl.hide();
2681             }
2682             progressEl.setDisplayed(opt.progress === true);
2683             this.updateProgress(0);
2684             activeTextEl.dom.value = opt.value || "";
2685             if(opt.prompt){
2686                 dlg.setDefaultButton(activeTextEl);
2687             }else{
2688                 var bs = opt.buttons;
2689                 var db = null;
2690                 if(bs && bs.ok){
2691                     db = buttons["ok"];
2692                 }else if(bs && bs.yes){
2693                     db = buttons["yes"];
2694                 }
2695                 dlg.setDefaultButton(db);
2696             }
2697             bwidth = updateButtons(opt.buttons);
2698             this.updateText(opt.msg);
2699             if(opt.cls){
2700                 d.el.addClass(opt.cls);
2701             }
2702             d.proxyDrag = opt.proxyDrag === true;
2703             d.modal = opt.modal !== false;
2704             d.mask = opt.modal !== false ? mask : false;
2705             if(!d.isVisible()){
2706                 // force it to the end of the z-index stack so it gets a cursor in FF
2707                 document.body.appendChild(dlg.el.dom);
2708                 d.animateTarget = null;
2709                 d.show(options.animEl);
2710             }
2711             return this;
2712         },
2713
2714         /**
2715          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2716          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2717          * and closing the message box when the process is complete.
2718          * @param {String} title The title bar text
2719          * @param {String} msg The message box body text
2720          * @return {Roo.MessageBox} This message box
2721          */
2722         progress : function(title, msg){
2723             this.show({
2724                 title : title,
2725                 msg : msg,
2726                 buttons: false,
2727                 progress:true,
2728                 closable:false,
2729                 minWidth: this.minProgressWidth,
2730                 modal : true
2731             });
2732             return this;
2733         },
2734
2735         /**
2736          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2737          * If a callback function is passed it will be called after the user clicks the button, and the
2738          * id of the button that was clicked will be passed as the only parameter to the callback
2739          * (could also be the top-right close button).
2740          * @param {String} title The title bar text
2741          * @param {String} msg The message box body text
2742          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2743          * @param {Object} scope (optional) The scope of the callback function
2744          * @return {Roo.MessageBox} This message box
2745          */
2746         alert : function(title, msg, fn, scope){
2747             this.show({
2748                 title : title,
2749                 msg : msg,
2750                 buttons: this.OK,
2751                 fn: fn,
2752                 scope : scope,
2753                 modal : true
2754             });
2755             return this;
2756         },
2757
2758         /**
2759          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2760          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2761          * You are responsible for closing the message box when the process is complete.
2762          * @param {String} msg The message box body text
2763          * @param {String} title (optional) The title bar text
2764          * @return {Roo.MessageBox} This message box
2765          */
2766         wait : function(msg, title){
2767             this.show({
2768                 title : title,
2769                 msg : msg,
2770                 buttons: false,
2771                 closable:false,
2772                 progress:true,
2773                 modal:true,
2774                 width:300,
2775                 wait:true
2776             });
2777             waitTimer = Roo.TaskMgr.start({
2778                 run: function(i){
2779                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2780                 },
2781                 interval: 1000
2782             });
2783             return this;
2784         },
2785
2786         /**
2787          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2788          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2789          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2790          * @param {String} title The title bar text
2791          * @param {String} msg The message box body text
2792          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2793          * @param {Object} scope (optional) The scope of the callback function
2794          * @return {Roo.MessageBox} This message box
2795          */
2796         confirm : function(title, msg, fn, scope){
2797             this.show({
2798                 title : title,
2799                 msg : msg,
2800                 buttons: this.YESNO,
2801                 fn: fn,
2802                 scope : scope,
2803                 modal : true
2804             });
2805             return this;
2806         },
2807
2808         /**
2809          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2810          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2811          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2812          * (could also be the top-right close button) and the text that was entered will be passed as the two
2813          * parameters to the callback.
2814          * @param {String} title The title bar text
2815          * @param {String} msg The message box body text
2816          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2817          * @param {Object} scope (optional) The scope of the callback function
2818          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2819          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2820          * @return {Roo.MessageBox} This message box
2821          */
2822         prompt : function(title, msg, fn, scope, multiline){
2823             this.show({
2824                 title : title,
2825                 msg : msg,
2826                 buttons: this.OKCANCEL,
2827                 fn: fn,
2828                 minWidth:250,
2829                 scope : scope,
2830                 prompt:true,
2831                 multiline: multiline,
2832                 modal : true
2833             });
2834             return this;
2835         },
2836
2837         /**
2838          * Button config that displays a single OK button
2839          * @type Object
2840          */
2841         OK : {ok:true},
2842         /**
2843          * Button config that displays Yes and No buttons
2844          * @type Object
2845          */
2846         YESNO : {yes:true, no:true},
2847         /**
2848          * Button config that displays OK and Cancel buttons
2849          * @type Object
2850          */
2851         OKCANCEL : {ok:true, cancel:true},
2852         /**
2853          * Button config that displays Yes, No and Cancel buttons
2854          * @type Object
2855          */
2856         YESNOCANCEL : {yes:true, no:true, cancel:true},
2857
2858         /**
2859          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2860          * @type Number
2861          */
2862         defaultTextHeight : 75,
2863         /**
2864          * The maximum width in pixels of the message box (defaults to 600)
2865          * @type Number
2866          */
2867         maxWidth : 600,
2868         /**
2869          * The minimum width in pixels of the message box (defaults to 100)
2870          * @type Number
2871          */
2872         minWidth : 100,
2873         /**
2874          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2875          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2876          * @type Number
2877          */
2878         minProgressWidth : 250,
2879         /**
2880          * An object containing the default button text strings that can be overriden for localized language support.
2881          * Supported properties are: ok, cancel, yes and no.
2882          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2883          * @type Object
2884          */
2885         buttonText : {
2886             ok : "OK",
2887             cancel : "Cancel",
2888             yes : "Yes",
2889             no : "No"
2890         }
2891     };
2892 }();
2893
2894 /**
2895  * Shorthand for {@link Roo.MessageBox}
2896  */
2897 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2898 Roo.Msg = Roo.Msg || Roo.MessageBox;
2899 /*
2900  * - LGPL
2901  *
2902  * navbar
2903  * 
2904  */
2905
2906 /**
2907  * @class Roo.bootstrap.Navbar
2908  * @extends Roo.bootstrap.Component
2909  * Bootstrap Navbar class
2910
2911  * @constructor
2912  * Create a new Navbar
2913  * @param {Object} config The config object
2914  */
2915
2916
2917 Roo.bootstrap.Navbar = function(config){
2918     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2919     
2920 };
2921
2922 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2923     
2924     
2925    
2926     // private
2927     navItems : false,
2928     loadMask : false,
2929     
2930     
2931     getAutoCreate : function(){
2932         
2933         
2934         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2935         
2936     },
2937     
2938     initEvents :function ()
2939     {
2940         //Roo.log(this.el.select('.navbar-toggle',true));
2941         this.el.select('.navbar-toggle',true).on('click', function() {
2942            // Roo.log('click');
2943             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2944         }, this);
2945         
2946         var mark = {
2947             tag: "div",
2948             cls:"x-dlg-mask"
2949         }
2950         
2951         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2952         
2953         var size = this.el.getSize();
2954         this.maskEl.setSize(size.width, size.height);
2955         this.maskEl.enableDisplayMode("block");
2956         this.maskEl.hide();
2957         
2958         if(this.loadMask){
2959             this.maskEl.show();
2960         }
2961     },
2962     
2963     
2964     getChildContainer : function()
2965     {
2966         if (this.el.select('.collapse').getCount()) {
2967             return this.el.select('.collapse',true).first();
2968         }
2969         
2970         return this.el;
2971     },
2972     
2973     mask : function()
2974     {
2975         this.maskEl.show();
2976     },
2977     
2978     unmask : function()
2979     {
2980         this.maskEl.hide();
2981     } 
2982     
2983     
2984     
2985     
2986 });
2987
2988
2989
2990  
2991
2992  /*
2993  * - LGPL
2994  *
2995  * navbar
2996  * 
2997  */
2998
2999 /**
3000  * @class Roo.bootstrap.NavSimplebar
3001  * @extends Roo.bootstrap.Navbar
3002  * Bootstrap Sidebar class
3003  *
3004  * @cfg {Boolean} inverse is inverted color
3005  * 
3006  * @cfg {String} type (nav | pills | tabs)
3007  * @cfg {Boolean} arrangement stacked | justified
3008  * @cfg {String} align (left | right) alignment
3009  * 
3010  * @cfg {Boolean} main (true|false) main nav bar? default false
3011  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3012  * 
3013  * @cfg {String} tag (header|footer|nav|div) default is nav 
3014
3015  * 
3016  * 
3017  * 
3018  * @constructor
3019  * Create a new Sidebar
3020  * @param {Object} config The config object
3021  */
3022
3023
3024 Roo.bootstrap.NavSimplebar = function(config){
3025     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3026 };
3027
3028 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3029     
3030     inverse: false,
3031     
3032     type: false,
3033     arrangement: '',
3034     align : false,
3035     
3036     
3037     
3038     main : false,
3039     
3040     
3041     tag : false,
3042     
3043     
3044     getAutoCreate : function(){
3045         
3046         
3047         var cfg = {
3048             tag : this.tag || 'div',
3049             cls : 'navbar'
3050         };
3051           
3052         
3053         cfg.cn = [
3054             {
3055                 cls: 'nav',
3056                 tag : 'ul'
3057             }
3058         ];
3059         
3060          
3061         this.type = this.type || 'nav';
3062         if (['tabs','pills'].indexOf(this.type)!==-1) {
3063             cfg.cn[0].cls += ' nav-' + this.type
3064         
3065         
3066         } else {
3067             if (this.type!=='nav') {
3068                 Roo.log('nav type must be nav/tabs/pills')
3069             }
3070             cfg.cn[0].cls += ' navbar-nav'
3071         }
3072         
3073         
3074         
3075         
3076         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3077             cfg.cn[0].cls += ' nav-' + this.arrangement;
3078         }
3079         
3080         
3081         if (this.align === 'right') {
3082             cfg.cn[0].cls += ' navbar-right';
3083         }
3084         
3085         if (this.inverse) {
3086             cfg.cls += ' navbar-inverse';
3087             
3088         }
3089         
3090         
3091         return cfg;
3092     
3093         
3094     }
3095     
3096     
3097     
3098 });
3099
3100
3101
3102  
3103
3104  
3105        /*
3106  * - LGPL
3107  *
3108  * navbar
3109  * 
3110  */
3111
3112 /**
3113  * @class Roo.bootstrap.NavHeaderbar
3114  * @extends Roo.bootstrap.NavSimplebar
3115  * Bootstrap Sidebar class
3116  *
3117  * @cfg {String} brand what is brand
3118  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3119  * @cfg {String} brand_href href of the brand
3120  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3121  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3122  * 
3123  * @constructor
3124  * Create a new Sidebar
3125  * @param {Object} config The config object
3126  */
3127
3128
3129 Roo.bootstrap.NavHeaderbar = function(config){
3130     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3131 };
3132
3133 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3134     
3135     position: '',
3136     brand: '',
3137     brand_href: false,
3138     srButton : true,
3139     autohide : false,
3140     
3141     getAutoCreate : function(){
3142         
3143         var   cfg = {
3144             tag: this.nav || 'nav',
3145             cls: 'navbar',
3146             role: 'navigation',
3147             cn: []
3148         };
3149         
3150         if(this.srButton){
3151             cfg.cn.push({
3152                 tag: 'div',
3153                 cls: 'navbar-header',
3154                 cn: [
3155                     {
3156                         tag: 'button',
3157                         type: 'button',
3158                         cls: 'navbar-toggle',
3159                         'data-toggle': 'collapse',
3160                         cn: [
3161                             {
3162                                 tag: 'span',
3163                                 cls: 'sr-only',
3164                                 html: 'Toggle navigation'
3165                             },
3166                             {
3167                                 tag: 'span',
3168                                 cls: 'icon-bar'
3169                             },
3170                             {
3171                                 tag: 'span',
3172                                 cls: 'icon-bar'
3173                             },
3174                             {
3175                                 tag: 'span',
3176                                 cls: 'icon-bar'
3177                             }
3178                         ]
3179                     }
3180                 ]
3181             });
3182         }
3183         
3184         cfg.cn.push({
3185             tag: 'div',
3186             cls: 'collapse navbar-collapse',
3187             cn : []
3188         });
3189         
3190         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3191         
3192         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3193             cfg.cls += ' navbar-' + this.position;
3194             
3195             // tag can override this..
3196             
3197             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3198         }
3199         
3200         if (this.brand !== '') {
3201             cfg.cn[0].cn.push({
3202                 tag: 'a',
3203                 href: this.brand_href ? this.brand_href : '#',
3204                 cls: 'navbar-brand',
3205                 cn: [
3206                 this.brand
3207                 ]
3208             });
3209         }
3210         
3211         if(this.main){
3212             cfg.cls += ' main-nav';
3213         }
3214         
3215         
3216         return cfg;
3217
3218         
3219     },
3220     initEvents : function()
3221     {
3222         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3223         
3224         if (this.autohide) {
3225             
3226             var prevScroll = 0;
3227             var ft = this.el;
3228             
3229             Roo.get(document).on('scroll',function(e) {
3230                 var ns = Roo.get(document).getScroll().top;
3231                 var os = prevScroll;
3232                 prevScroll = ns;
3233                 
3234                 if(ns > os){
3235                     ft.removeClass('slideDown');
3236                     ft.addClass('slideUp');
3237                     return;
3238                 }
3239                 ft.removeClass('slideUp');
3240                 ft.addClass('slideDown');
3241                  
3242               
3243           },this);
3244         }
3245     }    
3246           
3247       
3248     
3249     
3250 });
3251
3252
3253
3254  
3255
3256  /*
3257  * - LGPL
3258  *
3259  * navbar
3260  * 
3261  */
3262
3263 /**
3264  * @class Roo.bootstrap.NavSidebar
3265  * @extends Roo.bootstrap.Navbar
3266  * Bootstrap Sidebar class
3267  * 
3268  * @constructor
3269  * Create a new Sidebar
3270  * @param {Object} config The config object
3271  */
3272
3273
3274 Roo.bootstrap.NavSidebar = function(config){
3275     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3276 };
3277
3278 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3279     
3280     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3281     
3282     getAutoCreate : function(){
3283         
3284         
3285         return  {
3286             tag: 'div',
3287             cls: 'sidebar sidebar-nav'
3288         };
3289     
3290         
3291     }
3292     
3293     
3294     
3295 });
3296
3297
3298
3299  
3300
3301  /*
3302  * - LGPL
3303  *
3304  * nav group
3305  * 
3306  */
3307
3308 /**
3309  * @class Roo.bootstrap.NavGroup
3310  * @extends Roo.bootstrap.Component
3311  * Bootstrap NavGroup class
3312  * @cfg {String} align left | right
3313  * @cfg {Boolean} inverse false | true
3314  * @cfg {String} type (nav|pills|tab) default nav
3315  * @cfg {String} navId - reference Id for navbar.
3316
3317  * 
3318  * @constructor
3319  * Create a new nav group
3320  * @param {Object} config The config object
3321  */
3322
3323 Roo.bootstrap.NavGroup = function(config){
3324     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3325     this.navItems = [];
3326    
3327     Roo.bootstrap.NavGroup.register(this);
3328      this.addEvents({
3329         /**
3330              * @event changed
3331              * Fires when the active item changes
3332              * @param {Roo.bootstrap.NavGroup} this
3333              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3334              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3335          */
3336         'changed': true
3337      });
3338     
3339 };
3340
3341 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3342     
3343     align: '',
3344     inverse: false,
3345     form: false,
3346     type: 'nav',
3347     navId : '',
3348     // private
3349     
3350     navItems : false, 
3351     
3352     getAutoCreate : function()
3353     {
3354         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3355         
3356         cfg = {
3357             tag : 'ul',
3358             cls: 'nav' 
3359         }
3360         
3361         if (['tabs','pills'].indexOf(this.type)!==-1) {
3362             cfg.cls += ' nav-' + this.type
3363         } else {
3364             if (this.type!=='nav') {
3365                 Roo.log('nav type must be nav/tabs/pills')
3366             }
3367             cfg.cls += ' navbar-nav'
3368         }
3369         
3370         if (this.parent().sidebar) {
3371             cfg = {
3372                 tag: 'ul',
3373                 cls: 'dashboard-menu sidebar-menu'
3374             }
3375             
3376             return cfg;
3377         }
3378         
3379         if (this.form === true) {
3380             cfg = {
3381                 tag: 'form',
3382                 cls: 'navbar-form'
3383             }
3384             
3385             if (this.align === 'right') {
3386                 cfg.cls += ' navbar-right';
3387             } else {
3388                 cfg.cls += ' navbar-left';
3389             }
3390         }
3391         
3392         if (this.align === 'right') {
3393             cfg.cls += ' navbar-right';
3394         }
3395         
3396         if (this.inverse) {
3397             cfg.cls += ' navbar-inverse';
3398             
3399         }
3400         
3401         
3402         return cfg;
3403     },
3404     /**
3405     * sets the active Navigation item
3406     * @param {Roo.bootstrap.NavItem} the new current navitem
3407     */
3408     setActiveItem : function(item)
3409     {
3410         var prev = false;
3411         Roo.each(this.navItems, function(v){
3412             if (v == item) {
3413                 return ;
3414             }
3415             if (v.isActive()) {
3416                 v.setActive(false, true);
3417                 prev = v;
3418                 
3419             }
3420             
3421         });
3422
3423         item.setActive(true, true);
3424         this.fireEvent('changed', this, item, prev);
3425         
3426         
3427     },
3428     /**
3429     * gets the active Navigation item
3430     * @return {Roo.bootstrap.NavItem} the current navitem
3431     */
3432     getActive : function()
3433     {
3434         
3435         var prev = false;
3436         Roo.each(this.navItems, function(v){
3437             
3438             if (v.isActive()) {
3439                 prev = v;
3440                 
3441             }
3442             
3443         });
3444         return prev;
3445     },
3446     
3447     indexOfNav : function()
3448     {
3449         
3450         var prev = false;
3451         Roo.each(this.navItems, function(v,i){
3452             
3453             if (v.isActive()) {
3454                 prev = i;
3455                 
3456             }
3457             
3458         });
3459         return prev;
3460     },
3461     /**
3462     * adds a Navigation item
3463     * @param {Roo.bootstrap.NavItem} the navitem to add
3464     */
3465     addItem : function(cfg)
3466     {
3467         var cn = new Roo.bootstrap.NavItem(cfg);
3468         this.register(cn);
3469         cn.parentId = this.id;
3470         cn.onRender(this.el, null);
3471         return cn;
3472     },
3473     /**
3474     * register a Navigation item
3475     * @param {Roo.bootstrap.NavItem} the navitem to add
3476     */
3477     register : function(item)
3478     {
3479         this.navItems.push( item);
3480         item.navId = this.navId;
3481     
3482     },
3483   
3484     
3485     getNavItem: function(tabId)
3486     {
3487         var ret = false;
3488         Roo.each(this.navItems, function(e) {
3489             if (e.tabId == tabId) {
3490                ret =  e;
3491                return false;
3492             }
3493             return true;
3494             
3495         });
3496         return ret;
3497     },
3498     
3499     setActiveNext : function()
3500     {
3501         var i = this.indexOfNav(this.getActive());
3502         if (i > this.navItems.length) {
3503             return;
3504         }
3505         this.setActiveItem(this.navItems[i+1]);
3506     },
3507     setActivePrev : function()
3508     {
3509         var i = this.indexOfNav(this.getActive());
3510         if (i  < 1) {
3511             return;
3512         }
3513         this.setActiveItem(this.navItems[i-1]);
3514     },
3515     clearWasActive : function(except) {
3516         Roo.each(this.navItems, function(e) {
3517             if (e.tabId != except.tabId && e.was_active) {
3518                e.was_active = false;
3519                return false;
3520             }
3521             return true;
3522             
3523         });
3524     },
3525     getWasActive : function ()
3526     {
3527         var r = false;
3528         Roo.each(this.navItems, function(e) {
3529             if (e.was_active) {
3530                r = e;
3531                return false;
3532             }
3533             return true;
3534             
3535         });
3536         return r;
3537     }
3538     
3539     
3540 });
3541
3542  
3543 Roo.apply(Roo.bootstrap.NavGroup, {
3544     
3545     groups: {},
3546      /**
3547     * register a Navigation Group
3548     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3549     */
3550     register : function(navgrp)
3551     {
3552         this.groups[navgrp.navId] = navgrp;
3553         
3554     },
3555     /**
3556     * fetch a Navigation Group based on the navigation ID
3557     * @param {string} the navgroup to add
3558     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3559     */
3560     get: function(navId) {
3561         if (typeof(this.groups[navId]) == 'undefined') {
3562             return false;
3563             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3564         }
3565         return this.groups[navId] ;
3566     }
3567     
3568     
3569     
3570 });
3571
3572  /*
3573  * - LGPL
3574  *
3575  * row
3576  * 
3577  */
3578
3579 /**
3580  * @class Roo.bootstrap.NavItem
3581  * @extends Roo.bootstrap.Component
3582  * Bootstrap Navbar.NavItem class
3583  * @cfg {String} href  link to
3584  * @cfg {String} html content of button
3585  * @cfg {String} badge text inside badge
3586  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3587  * @cfg {String} glyphicon name of glyphicon
3588  * @cfg {String} icon name of font awesome icon
3589  * @cfg {Boolean} active Is item active
3590  * @cfg {Boolean} disabled Is item disabled
3591  
3592  * @cfg {Boolean} preventDefault (true | false) default false
3593  * @cfg {String} tabId the tab that this item activates.
3594  * @cfg {String} tagtype (a|span) render as a href or span?
3595   
3596  * @constructor
3597  * Create a new Navbar Item
3598  * @param {Object} config The config object
3599  */
3600 Roo.bootstrap.NavItem = function(config){
3601     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3602     this.addEvents({
3603         // raw events
3604         /**
3605          * @event click
3606          * The raw click event for the entire grid.
3607          * @param {Roo.EventObject} e
3608          */
3609         "click" : true,
3610          /**
3611             * @event changed
3612             * Fires when the active item active state changes
3613             * @param {Roo.bootstrap.NavItem} this
3614             * @param {boolean} state the new state
3615              
3616          */
3617         'changed': true
3618     });
3619    
3620 };
3621
3622 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3623     
3624     href: false,
3625     html: '',
3626     badge: '',
3627     icon: false,
3628     glyphicon: false,
3629     active: false,
3630     preventDefault : false,
3631     tabId : false,
3632     tagtype : 'a',
3633     disabled : false,
3634     
3635     was_active : false,
3636     
3637     getAutoCreate : function(){
3638          
3639         var cfg = {
3640             tag: 'li',
3641             cls: 'nav-item'
3642             
3643         }
3644         if (this.active) {
3645             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3646         }
3647         if (this.disabled) {
3648             cfg.cls += ' disabled';
3649         }
3650         
3651         if (this.href || this.html || this.glyphicon || this.icon) {
3652             cfg.cn = [
3653                 {
3654                     tag: this.tagtype,
3655                     href : this.href || "#",
3656                     html: this.html || ''
3657                 }
3658             ];
3659             
3660             if (this.icon) {
3661                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3662             }
3663
3664             if(this.glyphicon) {
3665                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3666             }
3667             
3668             if (this.menu) {
3669                 
3670                 cfg.cn[0].html += " <span class='caret'></span>";
3671              
3672             }
3673             
3674             if (this.badge !== '') {
3675                  
3676                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3677             }
3678         }
3679         
3680         
3681         
3682         return cfg;
3683     },
3684     initEvents: function() {
3685        // Roo.log('init events?');
3686        // Roo.log(this.el.dom);
3687         if (typeof (this.menu) != 'undefined') {
3688             this.menu.parentType = this.xtype;
3689             this.menu.triggerEl = this.el;
3690             this.addxtype(Roo.apply({}, this.menu));
3691         }
3692
3693        
3694         this.el.select('a',true).on('click', this.onClick, this);
3695         // at this point parent should be available..
3696         this.parent().register(this);
3697     },
3698     
3699     onClick : function(e)
3700     {
3701          
3702         if(this.preventDefault){
3703             e.preventDefault();
3704         }
3705         if (this.disabled) {
3706             return;
3707         }
3708         
3709         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3710         if (tg && tg.transition) {
3711             Roo.log("waiting for the transitionend");
3712             return;
3713         }
3714         
3715         Roo.log("fire event clicked");
3716         if(this.fireEvent('click', this, e) === false){
3717             return;
3718         };
3719         var p = this.parent();
3720         if (['tabs','pills'].indexOf(p.type)!==-1) {
3721             if (typeof(p.setActiveItem) !== 'undefined') {
3722                 p.setActiveItem(this);
3723             }
3724         }
3725         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3726         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3727             // remove the collapsed menu expand...
3728             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3729         }
3730         
3731     },
3732     
3733     isActive: function () {
3734         return this.active
3735     },
3736     setActive : function(state, fire, is_was_active)
3737     {
3738         if (this.active && !state & this.navId) {
3739             this.was_active = true;
3740             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3741             if (nv) {
3742                 nv.clearWasActive(this);
3743             }
3744             
3745         }
3746         this.active = state;
3747         
3748         if (!state ) {
3749             this.el.removeClass('active');
3750         } else if (!this.el.hasClass('active')) {
3751             this.el.addClass('active');
3752         }
3753         if (fire) {
3754             this.fireEvent('changed', this, state);
3755         }
3756         
3757         // show a panel if it's registered and related..
3758         
3759         if (!this.navId || !this.tabId || !state || is_was_active) {
3760             return;
3761         }
3762         
3763         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3764         if (!tg) {
3765             return;
3766         }
3767         var pan = tg.getPanelByName(this.tabId);
3768         if (!pan) {
3769             return;
3770         }
3771         // if we can not flip to new panel - go back to old nav highlight..
3772         if (false == tg.showPanel(pan)) {
3773             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3774             if (nv) {
3775                 var onav = nv.getWasActive();
3776                 if (onav) {
3777                     onav.setActive(true, false, true);
3778                 }
3779             }
3780             
3781         }
3782         
3783         
3784         
3785     },
3786      // this should not be here...
3787     setDisabled : function(state)
3788     {
3789         this.disabled = state;
3790         if (!state ) {
3791             this.el.removeClass('disabled');
3792         } else if (!this.el.hasClass('disabled')) {
3793             this.el.addClass('disabled');
3794         }
3795         
3796     }
3797 });
3798  
3799
3800  /*
3801  * - LGPL
3802  *
3803  * sidebar item
3804  *
3805  *  li
3806  *    <span> icon </span>
3807  *    <span> text </span>
3808  *    <span>badge </span>
3809  */
3810
3811 /**
3812  * @class Roo.bootstrap.NavSidebarItem
3813  * @extends Roo.bootstrap.NavItem
3814  * Bootstrap Navbar.NavSidebarItem class
3815  * @constructor
3816  * Create a new Navbar Button
3817  * @param {Object} config The config object
3818  */
3819 Roo.bootstrap.NavSidebarItem = function(config){
3820     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3821     this.addEvents({
3822         // raw events
3823         /**
3824          * @event click
3825          * The raw click event for the entire grid.
3826          * @param {Roo.EventObject} e
3827          */
3828         "click" : true,
3829          /**
3830             * @event changed
3831             * Fires when the active item active state changes
3832             * @param {Roo.bootstrap.NavSidebarItem} this
3833             * @param {boolean} state the new state
3834              
3835          */
3836         'changed': true
3837     });
3838    
3839 };
3840
3841 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3842     
3843     
3844     getAutoCreate : function(){
3845         
3846         
3847         var a = {
3848                 tag: 'a',
3849                 href : this.href || '#',
3850                 cls: '',
3851                 html : '',
3852                 cn : []
3853         };
3854         var cfg = {
3855             tag: 'li',
3856             cls: '',
3857             cn: [ a ]
3858         }
3859         var span = {
3860             tag: 'span',
3861             html : this.html || ''
3862         }
3863         
3864         
3865         if (this.active) {
3866             cfg.cls += ' active';
3867         }
3868         
3869         // left icon..
3870         if (this.glyphicon || this.icon) {
3871             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3872             a.cn.push({ tag : 'i', cls : c }) ;
3873         }
3874         // html..
3875         a.cn.push(span);
3876         // then badge..
3877         if (this.badge !== '') {
3878             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3879         }
3880         // fi
3881         if (this.menu) {
3882             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3883             a.cls += 'dropdown-toggle treeview' ;
3884             
3885         }
3886         
3887         
3888         
3889         return cfg;
3890          
3891            
3892     }
3893    
3894      
3895  
3896 });
3897  
3898
3899  /*
3900  * - LGPL
3901  *
3902  * row
3903  * 
3904  */
3905
3906 /**
3907  * @class Roo.bootstrap.Row
3908  * @extends Roo.bootstrap.Component
3909  * Bootstrap Row class (contains columns...)
3910  * 
3911  * @constructor
3912  * Create a new Row
3913  * @param {Object} config The config object
3914  */
3915
3916 Roo.bootstrap.Row = function(config){
3917     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3918 };
3919
3920 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3921     
3922     getAutoCreate : function(){
3923        return {
3924             cls: 'row clearfix'
3925        };
3926     }
3927     
3928     
3929 });
3930
3931  
3932
3933  /*
3934  * - LGPL
3935  *
3936  * element
3937  * 
3938  */
3939
3940 /**
3941  * @class Roo.bootstrap.Element
3942  * @extends Roo.bootstrap.Component
3943  * Bootstrap Element class
3944  * @cfg {String} html contents of the element
3945  * @cfg {String} tag tag of the element
3946  * @cfg {String} cls class of the element
3947  * 
3948  * @constructor
3949  * Create a new Element
3950  * @param {Object} config The config object
3951  */
3952
3953 Roo.bootstrap.Element = function(config){
3954     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3955 };
3956
3957 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3958     
3959     tag: 'div',
3960     cls: '',
3961     html: '',
3962      
3963     
3964     getAutoCreate : function(){
3965         
3966         var cfg = {
3967             tag: this.tag,
3968             cls: this.cls,
3969             html: this.html
3970         }
3971         
3972         
3973         
3974         return cfg;
3975     }
3976    
3977 });
3978
3979  
3980
3981  /*
3982  * - LGPL
3983  *
3984  * pagination
3985  * 
3986  */
3987
3988 /**
3989  * @class Roo.bootstrap.Pagination
3990  * @extends Roo.bootstrap.Component
3991  * Bootstrap Pagination class
3992  * @cfg {String} size xs | sm | md | lg
3993  * @cfg {Boolean} inverse false | true
3994  * 
3995  * @constructor
3996  * Create a new Pagination
3997  * @param {Object} config The config object
3998  */
3999
4000 Roo.bootstrap.Pagination = function(config){
4001     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4002 };
4003
4004 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4005     
4006     cls: false,
4007     size: false,
4008     inverse: false,
4009     
4010     getAutoCreate : function(){
4011         var cfg = {
4012             tag: 'ul',
4013                 cls: 'pagination'
4014         };
4015         if (this.inverse) {
4016             cfg.cls += ' inverse';
4017         }
4018         if (this.html) {
4019             cfg.html=this.html;
4020         }
4021         if (this.cls) {
4022             cfg.cls += " " + this.cls;
4023         }
4024         return cfg;
4025     }
4026    
4027 });
4028
4029  
4030
4031  /*
4032  * - LGPL
4033  *
4034  * Pagination item
4035  * 
4036  */
4037
4038
4039 /**
4040  * @class Roo.bootstrap.PaginationItem
4041  * @extends Roo.bootstrap.Component
4042  * Bootstrap PaginationItem class
4043  * @cfg {String} html text
4044  * @cfg {String} href the link
4045  * @cfg {Boolean} preventDefault (true | false) default true
4046  * @cfg {Boolean} active (true | false) default false
4047  * 
4048  * 
4049  * @constructor
4050  * Create a new PaginationItem
4051  * @param {Object} config The config object
4052  */
4053
4054
4055 Roo.bootstrap.PaginationItem = function(config){
4056     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4057     this.addEvents({
4058         // raw events
4059         /**
4060          * @event click
4061          * The raw click event for the entire grid.
4062          * @param {Roo.EventObject} e
4063          */
4064         "click" : true
4065     });
4066 };
4067
4068 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4069     
4070     href : false,
4071     html : false,
4072     preventDefault: true,
4073     active : false,
4074     cls : false,
4075     
4076     getAutoCreate : function(){
4077         var cfg= {
4078             tag: 'li',
4079             cn: [
4080                 {
4081                     tag : 'a',
4082                     href : this.href ? this.href : '#',
4083                     html : this.html ? this.html : ''
4084                 }
4085             ]
4086         };
4087         
4088         if(this.cls){
4089             cfg.cls = this.cls;
4090         }
4091         
4092         if(this.active){
4093             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4094         }
4095         
4096         return cfg;
4097     },
4098     
4099     initEvents: function() {
4100         
4101         this.el.on('click', this.onClick, this);
4102         
4103     },
4104     onClick : function(e)
4105     {
4106         Roo.log('PaginationItem on click ');
4107         if(this.preventDefault){
4108             e.preventDefault();
4109         }
4110         
4111         this.fireEvent('click', this, e);
4112     }
4113    
4114 });
4115
4116  
4117
4118  /*
4119  * - LGPL
4120  *
4121  * slider
4122  * 
4123  */
4124
4125
4126 /**
4127  * @class Roo.bootstrap.Slider
4128  * @extends Roo.bootstrap.Component
4129  * Bootstrap Slider class
4130  *    
4131  * @constructor
4132  * Create a new Slider
4133  * @param {Object} config The config object
4134  */
4135
4136 Roo.bootstrap.Slider = function(config){
4137     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4138 };
4139
4140 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4141     
4142     getAutoCreate : function(){
4143         
4144         var cfg = {
4145             tag: 'div',
4146             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4147             cn: [
4148                 {
4149                     tag: 'a',
4150                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4151                 }
4152             ]
4153         }
4154         
4155         return cfg;
4156     }
4157    
4158 });
4159
4160  /*
4161  * Based on:
4162  * Ext JS Library 1.1.1
4163  * Copyright(c) 2006-2007, Ext JS, LLC.
4164  *
4165  * Originally Released Under LGPL - original licence link has changed is not relivant.
4166  *
4167  * Fork - LGPL
4168  * <script type="text/javascript">
4169  */
4170  
4171
4172 /**
4173  * @class Roo.grid.ColumnModel
4174  * @extends Roo.util.Observable
4175  * This is the default implementation of a ColumnModel used by the Grid. It defines
4176  * the columns in the grid.
4177  * <br>Usage:<br>
4178  <pre><code>
4179  var colModel = new Roo.grid.ColumnModel([
4180         {header: "Ticker", width: 60, sortable: true, locked: true},
4181         {header: "Company Name", width: 150, sortable: true},
4182         {header: "Market Cap.", width: 100, sortable: true},
4183         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4184         {header: "Employees", width: 100, sortable: true, resizable: false}
4185  ]);
4186  </code></pre>
4187  * <p>
4188  
4189  * The config options listed for this class are options which may appear in each
4190  * individual column definition.
4191  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4192  * @constructor
4193  * @param {Object} config An Array of column config objects. See this class's
4194  * config objects for details.
4195 */
4196 Roo.grid.ColumnModel = function(config){
4197         /**
4198      * The config passed into the constructor
4199      */
4200     this.config = config;
4201     this.lookup = {};
4202
4203     // if no id, create one
4204     // if the column does not have a dataIndex mapping,
4205     // map it to the order it is in the config
4206     for(var i = 0, len = config.length; i < len; i++){
4207         var c = config[i];
4208         if(typeof c.dataIndex == "undefined"){
4209             c.dataIndex = i;
4210         }
4211         if(typeof c.renderer == "string"){
4212             c.renderer = Roo.util.Format[c.renderer];
4213         }
4214         if(typeof c.id == "undefined"){
4215             c.id = Roo.id();
4216         }
4217         if(c.editor && c.editor.xtype){
4218             c.editor  = Roo.factory(c.editor, Roo.grid);
4219         }
4220         if(c.editor && c.editor.isFormField){
4221             c.editor = new Roo.grid.GridEditor(c.editor);
4222         }
4223         this.lookup[c.id] = c;
4224     }
4225
4226     /**
4227      * The width of columns which have no width specified (defaults to 100)
4228      * @type Number
4229      */
4230     this.defaultWidth = 100;
4231
4232     /**
4233      * Default sortable of columns which have no sortable specified (defaults to false)
4234      * @type Boolean
4235      */
4236     this.defaultSortable = false;
4237
4238     this.addEvents({
4239         /**
4240              * @event widthchange
4241              * Fires when the width of a column changes.
4242              * @param {ColumnModel} this
4243              * @param {Number} columnIndex The column index
4244              * @param {Number} newWidth The new width
4245              */
4246             "widthchange": true,
4247         /**
4248              * @event headerchange
4249              * Fires when the text of a header changes.
4250              * @param {ColumnModel} this
4251              * @param {Number} columnIndex The column index
4252              * @param {Number} newText The new header text
4253              */
4254             "headerchange": true,
4255         /**
4256              * @event hiddenchange
4257              * Fires when a column is hidden or "unhidden".
4258              * @param {ColumnModel} this
4259              * @param {Number} columnIndex The column index
4260              * @param {Boolean} hidden true if hidden, false otherwise
4261              */
4262             "hiddenchange": true,
4263             /**
4264          * @event columnmoved
4265          * Fires when a column is moved.
4266          * @param {ColumnModel} this
4267          * @param {Number} oldIndex
4268          * @param {Number} newIndex
4269          */
4270         "columnmoved" : true,
4271         /**
4272          * @event columlockchange
4273          * Fires when a column's locked state is changed
4274          * @param {ColumnModel} this
4275          * @param {Number} colIndex
4276          * @param {Boolean} locked true if locked
4277          */
4278         "columnlockchange" : true
4279     });
4280     Roo.grid.ColumnModel.superclass.constructor.call(this);
4281 };
4282 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4283     /**
4284      * @cfg {String} header The header text to display in the Grid view.
4285      */
4286     /**
4287      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4288      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4289      * specified, the column's index is used as an index into the Record's data Array.
4290      */
4291     /**
4292      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4293      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4294      */
4295     /**
4296      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4297      * Defaults to the value of the {@link #defaultSortable} property.
4298      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4299      */
4300     /**
4301      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4302      */
4303     /**
4304      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4305      */
4306     /**
4307      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4308      */
4309     /**
4310      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4311      */
4312     /**
4313      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4314      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4315      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4316      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4317      */
4318        /**
4319      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4320      */
4321     /**
4322      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4323      */
4324
4325     /**
4326      * Returns the id of the column at the specified index.
4327      * @param {Number} index The column index
4328      * @return {String} the id
4329      */
4330     getColumnId : function(index){
4331         return this.config[index].id;
4332     },
4333
4334     /**
4335      * Returns the column for a specified id.
4336      * @param {String} id The column id
4337      * @return {Object} the column
4338      */
4339     getColumnById : function(id){
4340         return this.lookup[id];
4341     },
4342
4343     
4344     /**
4345      * Returns the column for a specified dataIndex.
4346      * @param {String} dataIndex The column dataIndex
4347      * @return {Object|Boolean} the column or false if not found
4348      */
4349     getColumnByDataIndex: function(dataIndex){
4350         var index = this.findColumnIndex(dataIndex);
4351         return index > -1 ? this.config[index] : false;
4352     },
4353     
4354     /**
4355      * Returns the index for a specified column id.
4356      * @param {String} id The column id
4357      * @return {Number} the index, or -1 if not found
4358      */
4359     getIndexById : function(id){
4360         for(var i = 0, len = this.config.length; i < len; i++){
4361             if(this.config[i].id == id){
4362                 return i;
4363             }
4364         }
4365         return -1;
4366     },
4367     
4368     /**
4369      * Returns the index for a specified column dataIndex.
4370      * @param {String} dataIndex The column dataIndex
4371      * @return {Number} the index, or -1 if not found
4372      */
4373     
4374     findColumnIndex : function(dataIndex){
4375         for(var i = 0, len = this.config.length; i < len; i++){
4376             if(this.config[i].dataIndex == dataIndex){
4377                 return i;
4378             }
4379         }
4380         return -1;
4381     },
4382     
4383     
4384     moveColumn : function(oldIndex, newIndex){
4385         var c = this.config[oldIndex];
4386         this.config.splice(oldIndex, 1);
4387         this.config.splice(newIndex, 0, c);
4388         this.dataMap = null;
4389         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4390     },
4391
4392     isLocked : function(colIndex){
4393         return this.config[colIndex].locked === true;
4394     },
4395
4396     setLocked : function(colIndex, value, suppressEvent){
4397         if(this.isLocked(colIndex) == value){
4398             return;
4399         }
4400         this.config[colIndex].locked = value;
4401         if(!suppressEvent){
4402             this.fireEvent("columnlockchange", this, colIndex, value);
4403         }
4404     },
4405
4406     getTotalLockedWidth : function(){
4407         var totalWidth = 0;
4408         for(var i = 0; i < this.config.length; i++){
4409             if(this.isLocked(i) && !this.isHidden(i)){
4410                 this.totalWidth += this.getColumnWidth(i);
4411             }
4412         }
4413         return totalWidth;
4414     },
4415
4416     getLockedCount : function(){
4417         for(var i = 0, len = this.config.length; i < len; i++){
4418             if(!this.isLocked(i)){
4419                 return i;
4420             }
4421         }
4422     },
4423
4424     /**
4425      * Returns the number of columns.
4426      * @return {Number}
4427      */
4428     getColumnCount : function(visibleOnly){
4429         if(visibleOnly === true){
4430             var c = 0;
4431             for(var i = 0, len = this.config.length; i < len; i++){
4432                 if(!this.isHidden(i)){
4433                     c++;
4434                 }
4435             }
4436             return c;
4437         }
4438         return this.config.length;
4439     },
4440
4441     /**
4442      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4443      * @param {Function} fn
4444      * @param {Object} scope (optional)
4445      * @return {Array} result
4446      */
4447     getColumnsBy : function(fn, scope){
4448         var r = [];
4449         for(var i = 0, len = this.config.length; i < len; i++){
4450             var c = this.config[i];
4451             if(fn.call(scope||this, c, i) === true){
4452                 r[r.length] = c;
4453             }
4454         }
4455         return r;
4456     },
4457
4458     /**
4459      * Returns true if the specified column is sortable.
4460      * @param {Number} col The column index
4461      * @return {Boolean}
4462      */
4463     isSortable : function(col){
4464         if(typeof this.config[col].sortable == "undefined"){
4465             return this.defaultSortable;
4466         }
4467         return this.config[col].sortable;
4468     },
4469
4470     /**
4471      * Returns the rendering (formatting) function defined for the column.
4472      * @param {Number} col The column index.
4473      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4474      */
4475     getRenderer : function(col){
4476         if(!this.config[col].renderer){
4477             return Roo.grid.ColumnModel.defaultRenderer;
4478         }
4479         return this.config[col].renderer;
4480     },
4481
4482     /**
4483      * Sets the rendering (formatting) function for a column.
4484      * @param {Number} col The column index
4485      * @param {Function} fn The function to use to process the cell's raw data
4486      * to return HTML markup for the grid view. The render function is called with
4487      * the following parameters:<ul>
4488      * <li>Data value.</li>
4489      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4490      * <li>css A CSS style string to apply to the table cell.</li>
4491      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4492      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4493      * <li>Row index</li>
4494      * <li>Column index</li>
4495      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4496      */
4497     setRenderer : function(col, fn){
4498         this.config[col].renderer = fn;
4499     },
4500
4501     /**
4502      * Returns the width for the specified column.
4503      * @param {Number} col The column index
4504      * @return {Number}
4505      */
4506     getColumnWidth : function(col){
4507         return this.config[col].width * 1 || this.defaultWidth;
4508     },
4509
4510     /**
4511      * Sets the width for a column.
4512      * @param {Number} col The column index
4513      * @param {Number} width The new width
4514      */
4515     setColumnWidth : function(col, width, suppressEvent){
4516         this.config[col].width = width;
4517         this.totalWidth = null;
4518         if(!suppressEvent){
4519              this.fireEvent("widthchange", this, col, width);
4520         }
4521     },
4522
4523     /**
4524      * Returns the total width of all columns.
4525      * @param {Boolean} includeHidden True to include hidden column widths
4526      * @return {Number}
4527      */
4528     getTotalWidth : function(includeHidden){
4529         if(!this.totalWidth){
4530             this.totalWidth = 0;
4531             for(var i = 0, len = this.config.length; i < len; i++){
4532                 if(includeHidden || !this.isHidden(i)){
4533                     this.totalWidth += this.getColumnWidth(i);
4534                 }
4535             }
4536         }
4537         return this.totalWidth;
4538     },
4539
4540     /**
4541      * Returns the header for the specified column.
4542      * @param {Number} col The column index
4543      * @return {String}
4544      */
4545     getColumnHeader : function(col){
4546         return this.config[col].header;
4547     },
4548
4549     /**
4550      * Sets the header for a column.
4551      * @param {Number} col The column index
4552      * @param {String} header The new header
4553      */
4554     setColumnHeader : function(col, header){
4555         this.config[col].header = header;
4556         this.fireEvent("headerchange", this, col, header);
4557     },
4558
4559     /**
4560      * Returns the tooltip for the specified column.
4561      * @param {Number} col The column index
4562      * @return {String}
4563      */
4564     getColumnTooltip : function(col){
4565             return this.config[col].tooltip;
4566     },
4567     /**
4568      * Sets the tooltip for a column.
4569      * @param {Number} col The column index
4570      * @param {String} tooltip The new tooltip
4571      */
4572     setColumnTooltip : function(col, tooltip){
4573             this.config[col].tooltip = tooltip;
4574     },
4575
4576     /**
4577      * Returns the dataIndex for the specified column.
4578      * @param {Number} col The column index
4579      * @return {Number}
4580      */
4581     getDataIndex : function(col){
4582         return this.config[col].dataIndex;
4583     },
4584
4585     /**
4586      * Sets the dataIndex for a column.
4587      * @param {Number} col The column index
4588      * @param {Number} dataIndex The new dataIndex
4589      */
4590     setDataIndex : function(col, dataIndex){
4591         this.config[col].dataIndex = dataIndex;
4592     },
4593
4594     
4595     
4596     /**
4597      * Returns true if the cell is editable.
4598      * @param {Number} colIndex The column index
4599      * @param {Number} rowIndex The row index
4600      * @return {Boolean}
4601      */
4602     isCellEditable : function(colIndex, rowIndex){
4603         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4604     },
4605
4606     /**
4607      * Returns the editor defined for the cell/column.
4608      * return false or null to disable editing.
4609      * @param {Number} colIndex The column index
4610      * @param {Number} rowIndex The row index
4611      * @return {Object}
4612      */
4613     getCellEditor : function(colIndex, rowIndex){
4614         return this.config[colIndex].editor;
4615     },
4616
4617     /**
4618      * Sets if a column is editable.
4619      * @param {Number} col The column index
4620      * @param {Boolean} editable True if the column is editable
4621      */
4622     setEditable : function(col, editable){
4623         this.config[col].editable = editable;
4624     },
4625
4626
4627     /**
4628      * Returns true if the column is hidden.
4629      * @param {Number} colIndex The column index
4630      * @return {Boolean}
4631      */
4632     isHidden : function(colIndex){
4633         return this.config[colIndex].hidden;
4634     },
4635
4636
4637     /**
4638      * Returns true if the column width cannot be changed
4639      */
4640     isFixed : function(colIndex){
4641         return this.config[colIndex].fixed;
4642     },
4643
4644     /**
4645      * Returns true if the column can be resized
4646      * @return {Boolean}
4647      */
4648     isResizable : function(colIndex){
4649         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4650     },
4651     /**
4652      * Sets if a column is hidden.
4653      * @param {Number} colIndex The column index
4654      * @param {Boolean} hidden True if the column is hidden
4655      */
4656     setHidden : function(colIndex, hidden){
4657         this.config[colIndex].hidden = hidden;
4658         this.totalWidth = null;
4659         this.fireEvent("hiddenchange", this, colIndex, hidden);
4660     },
4661
4662     /**
4663      * Sets the editor for a column.
4664      * @param {Number} col The column index
4665      * @param {Object} editor The editor object
4666      */
4667     setEditor : function(col, editor){
4668         this.config[col].editor = editor;
4669     }
4670 });
4671
4672 Roo.grid.ColumnModel.defaultRenderer = function(value){
4673         if(typeof value == "string" && value.length < 1){
4674             return "&#160;";
4675         }
4676         return value;
4677 };
4678
4679 // Alias for backwards compatibility
4680 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4681 /*
4682  * Based on:
4683  * Ext JS Library 1.1.1
4684  * Copyright(c) 2006-2007, Ext JS, LLC.
4685  *
4686  * Originally Released Under LGPL - original licence link has changed is not relivant.
4687  *
4688  * Fork - LGPL
4689  * <script type="text/javascript">
4690  */
4691  
4692 /**
4693  * @class Roo.LoadMask
4694  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4695  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4696  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4697  * element's UpdateManager load indicator and will be destroyed after the initial load.
4698  * @constructor
4699  * Create a new LoadMask
4700  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4701  * @param {Object} config The config object
4702  */
4703 Roo.LoadMask = function(el, config){
4704     this.el = Roo.get(el);
4705     Roo.apply(this, config);
4706     if(this.store){
4707         this.store.on('beforeload', this.onBeforeLoad, this);
4708         this.store.on('load', this.onLoad, this);
4709         this.store.on('loadexception', this.onLoadException, this);
4710         this.removeMask = false;
4711     }else{
4712         var um = this.el.getUpdateManager();
4713         um.showLoadIndicator = false; // disable the default indicator
4714         um.on('beforeupdate', this.onBeforeLoad, this);
4715         um.on('update', this.onLoad, this);
4716         um.on('failure', this.onLoad, this);
4717         this.removeMask = true;
4718     }
4719 };
4720
4721 Roo.LoadMask.prototype = {
4722     /**
4723      * @cfg {Boolean} removeMask
4724      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4725      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4726      */
4727     /**
4728      * @cfg {String} msg
4729      * The text to display in a centered loading message box (defaults to 'Loading...')
4730      */
4731     msg : 'Loading...',
4732     /**
4733      * @cfg {String} msgCls
4734      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4735      */
4736     msgCls : 'x-mask-loading',
4737
4738     /**
4739      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4740      * @type Boolean
4741      */
4742     disabled: false,
4743
4744     /**
4745      * Disables the mask to prevent it from being displayed
4746      */
4747     disable : function(){
4748        this.disabled = true;
4749     },
4750
4751     /**
4752      * Enables the mask so that it can be displayed
4753      */
4754     enable : function(){
4755         this.disabled = false;
4756     },
4757     
4758     onLoadException : function()
4759     {
4760         Roo.log(arguments);
4761         
4762         if (typeof(arguments[3]) != 'undefined') {
4763             Roo.MessageBox.alert("Error loading",arguments[3]);
4764         } 
4765         /*
4766         try {
4767             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4768                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4769             }   
4770         } catch(e) {
4771             
4772         }
4773         */
4774     
4775         
4776         
4777         this.el.unmask(this.removeMask);
4778     },
4779     // private
4780     onLoad : function()
4781     {
4782         this.el.unmask(this.removeMask);
4783     },
4784
4785     // private
4786     onBeforeLoad : function(){
4787         if(!this.disabled){
4788             this.el.mask(this.msg, this.msgCls);
4789         }
4790     },
4791
4792     // private
4793     destroy : function(){
4794         if(this.store){
4795             this.store.un('beforeload', this.onBeforeLoad, this);
4796             this.store.un('load', this.onLoad, this);
4797             this.store.un('loadexception', this.onLoadException, this);
4798         }else{
4799             var um = this.el.getUpdateManager();
4800             um.un('beforeupdate', this.onBeforeLoad, this);
4801             um.un('update', this.onLoad, this);
4802             um.un('failure', this.onLoad, this);
4803         }
4804     }
4805 };/*
4806  * - LGPL
4807  *
4808  * table
4809  * 
4810  */
4811
4812 /**
4813  * @class Roo.bootstrap.Table
4814  * @extends Roo.bootstrap.Component
4815  * Bootstrap Table class
4816  * @cfg {String} cls table class
4817  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4818  * @cfg {String} bgcolor Specifies the background color for a table
4819  * @cfg {Number} border Specifies whether the table cells should have borders or not
4820  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4821  * @cfg {Number} cellspacing Specifies the space between cells
4822  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4823  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4824  * @cfg {String} sortable Specifies that the table should be sortable
4825  * @cfg {String} summary Specifies a summary of the content of a table
4826  * @cfg {Number} width Specifies the width of a table
4827  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4828  * 
4829  * @cfg {boolean} striped Should the rows be alternative striped
4830  * @cfg {boolean} bordered Add borders to the table
4831  * @cfg {boolean} hover Add hover highlighting
4832  * @cfg {boolean} condensed Format condensed
4833  * @cfg {boolean} responsive Format condensed
4834  * @cfg {Boolean} loadMask (true|false) default false
4835  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4836  * @cfg {Boolean} thead (true|false) generate thead, default true
4837  * @cfg {Boolean} RowSelection (true|false) default false
4838  * @cfg {Boolean} CellSelection (true|false) default false
4839  *
4840  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4841  
4842  * 
4843  * @constructor
4844  * Create a new Table
4845  * @param {Object} config The config object
4846  */
4847
4848 Roo.bootstrap.Table = function(config){
4849     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4850     
4851     if (this.sm) {
4852         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4853         this.sm = this.selModel;
4854         this.sm.xmodule = this.xmodule || false;
4855     }
4856     if (this.cm && typeof(this.cm.config) == 'undefined') {
4857         this.colModel = new Roo.grid.ColumnModel(this.cm);
4858         this.cm = this.colModel;
4859         this.cm.xmodule = this.xmodule || false;
4860     }
4861     if (this.store) {
4862         this.store= Roo.factory(this.store, Roo.data);
4863         this.ds = this.store;
4864         this.ds.xmodule = this.xmodule || false;
4865          
4866     }
4867     if (this.footer && this.store) {
4868         this.footer.dataSource = this.ds;
4869         this.footer = Roo.factory(this.footer);
4870     }
4871     
4872     /** @private */
4873     this.addEvents({
4874         /**
4875          * @event cellclick
4876          * Fires when a cell is clicked
4877          * @param {Roo.bootstrap.Table} this
4878          * @param {Roo.Element} el
4879          * @param {Number} rowIndex
4880          * @param {Number} columnIndex
4881          * @param {Roo.EventObject} e
4882          */
4883         "cellclick" : true,
4884         /**
4885          * @event celldblclick
4886          * Fires when a cell is double clicked
4887          * @param {Roo.bootstrap.Table} this
4888          * @param {Roo.Element} el
4889          * @param {Number} rowIndex
4890          * @param {Number} columnIndex
4891          * @param {Roo.EventObject} e
4892          */
4893         "celldblclick" : true,
4894         /**
4895          * @event rowclick
4896          * Fires when a row is clicked
4897          * @param {Roo.bootstrap.Table} this
4898          * @param {Roo.Element} el
4899          * @param {Number} rowIndex
4900          * @param {Roo.EventObject} e
4901          */
4902         "rowclick" : true,
4903         /**
4904          * @event rowdblclick
4905          * Fires when a row is double clicked
4906          * @param {Roo.bootstrap.Table} this
4907          * @param {Roo.Element} el
4908          * @param {Number} rowIndex
4909          * @param {Roo.EventObject} e
4910          */
4911         "rowdblclick" : true,
4912         /**
4913          * @event mouseover
4914          * Fires when a mouseover occur
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         "mouseover" : true,
4922         /**
4923          * @event mouseout
4924          * Fires when a mouseout occur
4925          * @param {Roo.bootstrap.Table} this
4926          * @param {Roo.Element} el
4927          * @param {Number} rowIndex
4928          * @param {Number} columnIndex
4929          * @param {Roo.EventObject} e
4930          */
4931         "mouseout" : true,
4932         /**
4933          * @event rowclass
4934          * Fires when a row is rendered, so you can change add a style to it.
4935          * @param {Roo.bootstrap.Table} this
4936          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4937          */
4938         'rowclass' : true
4939         
4940     });
4941 };
4942
4943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4944     
4945     cls: false,
4946     align: false,
4947     bgcolor: false,
4948     border: false,
4949     cellpadding: false,
4950     cellspacing: false,
4951     frame: false,
4952     rules: false,
4953     sortable: false,
4954     summary: false,
4955     width: false,
4956     striped : false,
4957     bordered: false,
4958     hover:  false,
4959     condensed : false,
4960     responsive : false,
4961     sm : false,
4962     cm : false,
4963     store : false,
4964     loadMask : false,
4965     tfoot : true,
4966     thead : true,
4967     RowSelection : false,
4968     CellSelection : false,
4969     layout : false,
4970     
4971     // Roo.Element - the tbody
4972     mainBody: false, 
4973     
4974     getAutoCreate : function(){
4975         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4976         
4977         cfg = {
4978             tag: 'table',
4979             cls : 'table',
4980             cn : []
4981         }
4982             
4983         if (this.striped) {
4984             cfg.cls += ' table-striped';
4985         }
4986         
4987         if (this.hover) {
4988             cfg.cls += ' table-hover';
4989         }
4990         if (this.bordered) {
4991             cfg.cls += ' table-bordered';
4992         }
4993         if (this.condensed) {
4994             cfg.cls += ' table-condensed';
4995         }
4996         if (this.responsive) {
4997             cfg.cls += ' table-responsive';
4998         }
4999         
5000         if (this.cls) {
5001             cfg.cls+=  ' ' +this.cls;
5002         }
5003         
5004         // this lot should be simplifed...
5005         
5006         if (this.align) {
5007             cfg.align=this.align;
5008         }
5009         if (this.bgcolor) {
5010             cfg.bgcolor=this.bgcolor;
5011         }
5012         if (this.border) {
5013             cfg.border=this.border;
5014         }
5015         if (this.cellpadding) {
5016             cfg.cellpadding=this.cellpadding;
5017         }
5018         if (this.cellspacing) {
5019             cfg.cellspacing=this.cellspacing;
5020         }
5021         if (this.frame) {
5022             cfg.frame=this.frame;
5023         }
5024         if (this.rules) {
5025             cfg.rules=this.rules;
5026         }
5027         if (this.sortable) {
5028             cfg.sortable=this.sortable;
5029         }
5030         if (this.summary) {
5031             cfg.summary=this.summary;
5032         }
5033         if (this.width) {
5034             cfg.width=this.width;
5035         }
5036         if (this.layout) {
5037             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5038         }
5039         
5040         if(this.store || this.cm){
5041             if(this.thead){
5042                 cfg.cn.push(this.renderHeader());
5043             }
5044             
5045             cfg.cn.push(this.renderBody());
5046             
5047             if(this.tfoot){
5048                 cfg.cn.push(this.renderFooter());
5049             }
5050             
5051             cfg.cls+=  ' TableGrid';
5052         }
5053         
5054         return { cn : [ cfg ] };
5055     },
5056     
5057     initEvents : function()
5058     {   
5059         if(!this.store || !this.cm){
5060             return;
5061         }
5062         
5063         //Roo.log('initEvents with ds!!!!');
5064         
5065         this.mainBody = this.el.select('tbody', true).first();
5066         
5067         
5068         var _this = this;
5069         
5070         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5071             e.on('click', _this.sort, _this);
5072         });
5073         
5074         this.el.on("click", this.onClick, this);
5075         this.el.on("dblclick", this.onDblClick, this);
5076         
5077         this.parent().el.setStyle('position', 'relative');
5078         if (this.footer) {
5079             this.footer.parentId = this.id;
5080             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5081         }
5082         
5083         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5084         
5085         this.store.on('load', this.onLoad, this);
5086         this.store.on('beforeload', this.onBeforeLoad, this);
5087         this.store.on('update', this.onUpdate, this);
5088         
5089     },
5090     
5091     onMouseover : function(e, el)
5092     {
5093         var cell = Roo.get(el);
5094         
5095         if(!cell){
5096             return;
5097         }
5098         
5099         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5100             cell = cell.findParent('td', false, true);
5101         }
5102         
5103         var row = cell.findParent('tr', false, true);
5104         var cellIndex = cell.dom.cellIndex;
5105         var rowIndex = row.dom.rowIndex - 1; // start from 0
5106         
5107         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5108         
5109     },
5110     
5111     onMouseout : function(e, el)
5112     {
5113         var cell = Roo.get(el);
5114         
5115         if(!cell){
5116             return;
5117         }
5118         
5119         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5120             cell = cell.findParent('td', false, true);
5121         }
5122         
5123         var row = cell.findParent('tr', false, true);
5124         var cellIndex = cell.dom.cellIndex;
5125         var rowIndex = row.dom.rowIndex - 1; // start from 0
5126         
5127         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5128         
5129     },
5130     
5131     onClick : function(e, el)
5132     {
5133         var cell = Roo.get(el);
5134         
5135         if(!cell || (!this.CellSelection && !this.RowSelection)){
5136             return;
5137         }
5138         
5139         
5140         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5141             cell = cell.findParent('td', false, true);
5142         }
5143         
5144         var row = cell.findParent('tr', false, true);
5145         var cellIndex = cell.dom.cellIndex;
5146         var rowIndex = row.dom.rowIndex - 1;
5147         
5148         if(this.CellSelection){
5149             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5150         }
5151         
5152         if(this.RowSelection){
5153             this.fireEvent('rowclick', this, row, rowIndex, e);
5154         }
5155         
5156         
5157     },
5158     
5159     onDblClick : function(e,el)
5160     {
5161         var cell = Roo.get(el);
5162         
5163         if(!cell || (!this.CellSelection && !this.RowSelection)){
5164             return;
5165         }
5166         
5167         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5168             cell = cell.findParent('td', false, true);
5169         }
5170         
5171         var row = cell.findParent('tr', false, true);
5172         var cellIndex = cell.dom.cellIndex;
5173         var rowIndex = row.dom.rowIndex - 1;
5174         
5175         if(this.CellSelection){
5176             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5177         }
5178         
5179         if(this.RowSelection){
5180             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5181         }
5182     },
5183     
5184     sort : function(e,el)
5185     {
5186         var col = Roo.get(el)
5187         
5188         if(!col.hasClass('sortable')){
5189             return;
5190         }
5191         
5192         var sort = col.attr('sort');
5193         var dir = 'ASC';
5194         
5195         if(col.hasClass('glyphicon-arrow-up')){
5196             dir = 'DESC';
5197         }
5198         
5199         this.store.sortInfo = {field : sort, direction : dir};
5200         
5201         if (this.footer) {
5202             Roo.log("calling footer first");
5203             this.footer.onClick('first');
5204         } else {
5205         
5206             this.store.load({ params : { start : 0 } });
5207         }
5208     },
5209     
5210     renderHeader : function()
5211     {
5212         var header = {
5213             tag: 'thead',
5214             cn : []
5215         };
5216         
5217         var cm = this.cm;
5218         
5219         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5220             
5221             var config = cm.config[i];
5222                     
5223             var c = {
5224                 tag: 'th',
5225                 style : '',
5226                 html: cm.getColumnHeader(i)
5227             };
5228             
5229             if(typeof(config.hidden) != 'undefined' && config.hidden){
5230                 c.style += ' display:none;';
5231             }
5232             
5233             if(typeof(config.dataIndex) != 'undefined'){
5234                 c.sort = config.dataIndex;
5235             }
5236             
5237             if(typeof(config.sortable) != 'undefined' && config.sortable){
5238                 c.cls = 'sortable';
5239             }
5240             
5241             if(typeof(config.align) != 'undefined' && config.align.length){
5242                 c.style += ' text-align:' + config.align + ';';
5243             }
5244             
5245             if(typeof(config.width) != 'undefined'){
5246                 c.style += ' width:' + config.width + 'px;';
5247             }
5248             
5249             header.cn.push(c)
5250         }
5251         
5252         return header;
5253     },
5254     
5255     renderBody : function()
5256     {
5257         var body = {
5258             tag: 'tbody',
5259             cn : [
5260                 {
5261                     tag: 'tr',
5262                     cn : [
5263                         {
5264                             tag : 'td',
5265                             colspan :  this.cm.getColumnCount()
5266                         }
5267                     ]
5268                 }
5269             ]
5270         };
5271         
5272         return body;
5273     },
5274     
5275     renderFooter : function()
5276     {
5277         var footer = {
5278             tag: 'tfoot',
5279             cn : [
5280                 {
5281                     tag: 'tr',
5282                     cn : [
5283                         {
5284                             tag : 'td',
5285                             colspan :  this.cm.getColumnCount()
5286                         }
5287                     ]
5288                 }
5289             ]
5290         };
5291         
5292         return footer;
5293     },
5294     
5295     
5296     
5297     onLoad : function()
5298     {
5299         Roo.log('ds onload');
5300         this.clear();
5301         
5302         var _this = this;
5303         var cm = this.cm;
5304         var ds = this.store;
5305         
5306         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5307             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5308             
5309             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5310                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5311             }
5312             
5313             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5314                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5315             }
5316         });
5317         
5318         var tbody =  this.mainBody;
5319               
5320         if(ds.getCount() > 0){
5321             ds.data.each(function(d,rowIndex){
5322                 var row =  this.renderRow(cm, ds, rowIndex);
5323                 
5324                 tbody.createChild(row);
5325                 
5326                 var _this = this;
5327                 
5328                 if(row.cellObjects.length){
5329                     Roo.each(row.cellObjects, function(r){
5330                         _this.renderCellObject(r);
5331                     })
5332                 }
5333                 
5334             }, this);
5335         }
5336         
5337         Roo.each(this.el.select('tbody td', true).elements, function(e){
5338             e.on('mouseover', _this.onMouseover, _this);
5339         });
5340         
5341         Roo.each(this.el.select('tbody td', true).elements, function(e){
5342             e.on('mouseout', _this.onMouseout, _this);
5343         });
5344
5345         //if(this.loadMask){
5346         //    this.maskEl.hide();
5347         //}
5348     },
5349     
5350     
5351     onUpdate : function(ds,record)
5352     {
5353         this.refreshRow(record);
5354     },
5355     onRemove : function(ds, record, index, isUpdate){
5356         if(isUpdate !== true){
5357             this.fireEvent("beforerowremoved", this, index, record);
5358         }
5359         var bt = this.mainBody.dom;
5360         if(bt.rows[index]){
5361             bt.removeChild(bt.rows[index]);
5362         }
5363         
5364         if(isUpdate !== true){
5365             //this.stripeRows(index);
5366             //this.syncRowHeights(index, index);
5367             //this.layout();
5368             this.fireEvent("rowremoved", this, index, record);
5369         }
5370     },
5371     
5372     
5373     refreshRow : function(record){
5374         var ds = this.store, index;
5375         if(typeof record == 'number'){
5376             index = record;
5377             record = ds.getAt(index);
5378         }else{
5379             index = ds.indexOf(record);
5380         }
5381         this.insertRow(ds, index, true);
5382         this.onRemove(ds, record, index+1, true);
5383         //this.syncRowHeights(index, index);
5384         //this.layout();
5385         this.fireEvent("rowupdated", this, index, record);
5386     },
5387     
5388     insertRow : function(dm, rowIndex, isUpdate){
5389         
5390         if(!isUpdate){
5391             this.fireEvent("beforerowsinserted", this, rowIndex);
5392         }
5393             //var s = this.getScrollState();
5394         var row = this.renderRow(this.cm, this.store, rowIndex);
5395         // insert before rowIndex..
5396         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5397         
5398         var _this = this;
5399                 
5400         if(row.cellObjects.length){
5401             Roo.each(row.cellObjects, function(r){
5402                 _this.renderCellObject(r);
5403             })
5404         }
5405             
5406         if(!isUpdate){
5407             this.fireEvent("rowsinserted", this, rowIndex);
5408             //this.syncRowHeights(firstRow, lastRow);
5409             //this.stripeRows(firstRow);
5410             //this.layout();
5411         }
5412         
5413     },
5414     
5415     
5416     getRowDom : function(rowIndex)
5417     {
5418         // not sure if I need to check this.. but let's do it anyway..
5419         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5420                 this.mainBody.dom.rows[rowIndex] : false
5421     },
5422     // returns the object tree for a tr..
5423   
5424     
5425     renderRow : function(cm, ds, rowIndex) {
5426         
5427         var d = ds.getAt(rowIndex);
5428         
5429         var row = {
5430             tag : 'tr',
5431             cn : []
5432         };
5433             
5434         var cellObjects = [];
5435         
5436         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5437             var config = cm.config[i];
5438             
5439             var renderer = cm.getRenderer(i);
5440             var value = '';
5441             var id = false;
5442             
5443             if(typeof(renderer) !== 'undefined'){
5444                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5445             }
5446             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5447             // and are rendered into the cells after the row is rendered - using the id for the element.
5448             
5449             if(typeof(value) === 'object'){
5450                 id = Roo.id();
5451                 cellObjects.push({
5452                     container : id,
5453                     cfg : value 
5454                 })
5455             }
5456             
5457             var rowcfg = {
5458                 record: d,
5459                 rowIndex : rowIndex,
5460                 colIndex : i,
5461                 rowClass : ''
5462             }
5463
5464             this.fireEvent('rowclass', this, rowcfg);
5465             
5466             var td = {
5467                 tag: 'td',
5468                 cls : rowcfg.rowClass,
5469                 style: '',
5470                 html: (typeof(value) === 'object') ? '' : value
5471             };
5472             
5473             if (id) {
5474                 td.id = id;
5475             }
5476             
5477             if(typeof(config.hidden) != 'undefined' && config.hidden){
5478                 td.style += ' display:none;';
5479             }
5480             
5481             if(typeof(config.align) != 'undefined' && config.align.length){
5482                 td.style += ' text-align:' + config.align + ';';
5483             }
5484             
5485             if(typeof(config.width) != 'undefined'){
5486                 td.style += ' width:' +  config.width + 'px;';
5487             }
5488              
5489             row.cn.push(td);
5490            
5491         }
5492         
5493         row.cellObjects = cellObjects;
5494         
5495         return row;
5496           
5497     },
5498     
5499     
5500     
5501     onBeforeLoad : function()
5502     {
5503         //Roo.log('ds onBeforeLoad');
5504         
5505         //this.clear();
5506         
5507         //if(this.loadMask){
5508         //    this.maskEl.show();
5509         //}
5510     },
5511     
5512     clear : function()
5513     {
5514         this.el.select('tbody', true).first().dom.innerHTML = '';
5515     },
5516     
5517     getSelectionModel : function(){
5518         if(!this.selModel){
5519             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5520         }
5521         return this.selModel;
5522     },
5523     /*
5524      * Render the Roo.bootstrap object from renderder
5525      */
5526     renderCellObject : function(r)
5527     {
5528         var _this = this;
5529         
5530         var t = r.cfg.render(r.container);
5531         
5532         if(r.cfg.cn){
5533             Roo.each(r.cfg.cn, function(c){
5534                 var child = {
5535                     container: t.getChildContainer(),
5536                     cfg: c
5537                 }
5538                 _this.renderCellObject(child);
5539             })
5540         }
5541     }
5542    
5543 });
5544
5545  
5546
5547  /*
5548  * - LGPL
5549  *
5550  * table cell
5551  * 
5552  */
5553
5554 /**
5555  * @class Roo.bootstrap.TableCell
5556  * @extends Roo.bootstrap.Component
5557  * Bootstrap TableCell class
5558  * @cfg {String} html cell contain text
5559  * @cfg {String} cls cell class
5560  * @cfg {String} tag cell tag (td|th) default td
5561  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5562  * @cfg {String} align Aligns the content in a cell
5563  * @cfg {String} axis Categorizes cells
5564  * @cfg {String} bgcolor Specifies the background color of a cell
5565  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5566  * @cfg {Number} colspan Specifies the number of columns a cell should span
5567  * @cfg {String} headers Specifies one or more header cells a cell is related to
5568  * @cfg {Number} height Sets the height of a cell
5569  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5570  * @cfg {Number} rowspan Sets the number of rows a cell should span
5571  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5572  * @cfg {String} valign Vertical aligns the content in a cell
5573  * @cfg {Number} width Specifies the width of a cell
5574  * 
5575  * @constructor
5576  * Create a new TableCell
5577  * @param {Object} config The config object
5578  */
5579
5580 Roo.bootstrap.TableCell = function(config){
5581     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5582 };
5583
5584 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5585     
5586     html: false,
5587     cls: false,
5588     tag: false,
5589     abbr: false,
5590     align: false,
5591     axis: false,
5592     bgcolor: false,
5593     charoff: false,
5594     colspan: false,
5595     headers: false,
5596     height: false,
5597     nowrap: false,
5598     rowspan: false,
5599     scope: false,
5600     valign: false,
5601     width: false,
5602     
5603     
5604     getAutoCreate : function(){
5605         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5606         
5607         cfg = {
5608             tag: 'td'
5609         }
5610         
5611         if(this.tag){
5612             cfg.tag = this.tag;
5613         }
5614         
5615         if (this.html) {
5616             cfg.html=this.html
5617         }
5618         if (this.cls) {
5619             cfg.cls=this.cls
5620         }
5621         if (this.abbr) {
5622             cfg.abbr=this.abbr
5623         }
5624         if (this.align) {
5625             cfg.align=this.align
5626         }
5627         if (this.axis) {
5628             cfg.axis=this.axis
5629         }
5630         if (this.bgcolor) {
5631             cfg.bgcolor=this.bgcolor
5632         }
5633         if (this.charoff) {
5634             cfg.charoff=this.charoff
5635         }
5636         if (this.colspan) {
5637             cfg.colspan=this.colspan
5638         }
5639         if (this.headers) {
5640             cfg.headers=this.headers
5641         }
5642         if (this.height) {
5643             cfg.height=this.height
5644         }
5645         if (this.nowrap) {
5646             cfg.nowrap=this.nowrap
5647         }
5648         if (this.rowspan) {
5649             cfg.rowspan=this.rowspan
5650         }
5651         if (this.scope) {
5652             cfg.scope=this.scope
5653         }
5654         if (this.valign) {
5655             cfg.valign=this.valign
5656         }
5657         if (this.width) {
5658             cfg.width=this.width
5659         }
5660         
5661         
5662         return cfg;
5663     }
5664    
5665 });
5666
5667  
5668
5669  /*
5670  * - LGPL
5671  *
5672  * table row
5673  * 
5674  */
5675
5676 /**
5677  * @class Roo.bootstrap.TableRow
5678  * @extends Roo.bootstrap.Component
5679  * Bootstrap TableRow class
5680  * @cfg {String} cls row class
5681  * @cfg {String} align Aligns the content in a table row
5682  * @cfg {String} bgcolor Specifies a background color for a table row
5683  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5684  * @cfg {String} valign Vertical aligns the content in a table row
5685  * 
5686  * @constructor
5687  * Create a new TableRow
5688  * @param {Object} config The config object
5689  */
5690
5691 Roo.bootstrap.TableRow = function(config){
5692     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5693 };
5694
5695 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5696     
5697     cls: false,
5698     align: false,
5699     bgcolor: false,
5700     charoff: false,
5701     valign: false,
5702     
5703     getAutoCreate : function(){
5704         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5705         
5706         cfg = {
5707             tag: 'tr'
5708         }
5709             
5710         if(this.cls){
5711             cfg.cls = this.cls;
5712         }
5713         if(this.align){
5714             cfg.align = this.align;
5715         }
5716         if(this.bgcolor){
5717             cfg.bgcolor = this.bgcolor;
5718         }
5719         if(this.charoff){
5720             cfg.charoff = this.charoff;
5721         }
5722         if(this.valign){
5723             cfg.valign = this.valign;
5724         }
5725         
5726         return cfg;
5727     }
5728    
5729 });
5730
5731  
5732
5733  /*
5734  * - LGPL
5735  *
5736  * table body
5737  * 
5738  */
5739
5740 /**
5741  * @class Roo.bootstrap.TableBody
5742  * @extends Roo.bootstrap.Component
5743  * Bootstrap TableBody class
5744  * @cfg {String} cls element class
5745  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5746  * @cfg {String} align Aligns the content inside the element
5747  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5748  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5749  * 
5750  * @constructor
5751  * Create a new TableBody
5752  * @param {Object} config The config object
5753  */
5754
5755 Roo.bootstrap.TableBody = function(config){
5756     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5757 };
5758
5759 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5760     
5761     cls: false,
5762     tag: false,
5763     align: false,
5764     charoff: false,
5765     valign: false,
5766     
5767     getAutoCreate : function(){
5768         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5769         
5770         cfg = {
5771             tag: 'tbody'
5772         }
5773             
5774         if (this.cls) {
5775             cfg.cls=this.cls
5776         }
5777         if(this.tag){
5778             cfg.tag = this.tag;
5779         }
5780         
5781         if(this.align){
5782             cfg.align = this.align;
5783         }
5784         if(this.charoff){
5785             cfg.charoff = this.charoff;
5786         }
5787         if(this.valign){
5788             cfg.valign = this.valign;
5789         }
5790         
5791         return cfg;
5792     }
5793     
5794     
5795 //    initEvents : function()
5796 //    {
5797 //        
5798 //        if(!this.store){
5799 //            return;
5800 //        }
5801 //        
5802 //        this.store = Roo.factory(this.store, Roo.data);
5803 //        this.store.on('load', this.onLoad, this);
5804 //        
5805 //        this.store.load();
5806 //        
5807 //    },
5808 //    
5809 //    onLoad: function () 
5810 //    {   
5811 //        this.fireEvent('load', this);
5812 //    }
5813 //    
5814 //   
5815 });
5816
5817  
5818
5819  /*
5820  * Based on:
5821  * Ext JS Library 1.1.1
5822  * Copyright(c) 2006-2007, Ext JS, LLC.
5823  *
5824  * Originally Released Under LGPL - original licence link has changed is not relivant.
5825  *
5826  * Fork - LGPL
5827  * <script type="text/javascript">
5828  */
5829
5830 // as we use this in bootstrap.
5831 Roo.namespace('Roo.form');
5832  /**
5833  * @class Roo.form.Action
5834  * Internal Class used to handle form actions
5835  * @constructor
5836  * @param {Roo.form.BasicForm} el The form element or its id
5837  * @param {Object} config Configuration options
5838  */
5839
5840  
5841  
5842 // define the action interface
5843 Roo.form.Action = function(form, options){
5844     this.form = form;
5845     this.options = options || {};
5846 };
5847 /**
5848  * Client Validation Failed
5849  * @const 
5850  */
5851 Roo.form.Action.CLIENT_INVALID = 'client';
5852 /**
5853  * Server Validation Failed
5854  * @const 
5855  */
5856 Roo.form.Action.SERVER_INVALID = 'server';
5857  /**
5858  * Connect to Server Failed
5859  * @const 
5860  */
5861 Roo.form.Action.CONNECT_FAILURE = 'connect';
5862 /**
5863  * Reading Data from Server Failed
5864  * @const 
5865  */
5866 Roo.form.Action.LOAD_FAILURE = 'load';
5867
5868 Roo.form.Action.prototype = {
5869     type : 'default',
5870     failureType : undefined,
5871     response : undefined,
5872     result : undefined,
5873
5874     // interface method
5875     run : function(options){
5876
5877     },
5878
5879     // interface method
5880     success : function(response){
5881
5882     },
5883
5884     // interface method
5885     handleResponse : function(response){
5886
5887     },
5888
5889     // default connection failure
5890     failure : function(response){
5891         
5892         this.response = response;
5893         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5894         this.form.afterAction(this, false);
5895     },
5896
5897     processResponse : function(response){
5898         this.response = response;
5899         if(!response.responseText){
5900             return true;
5901         }
5902         this.result = this.handleResponse(response);
5903         return this.result;
5904     },
5905
5906     // utility functions used internally
5907     getUrl : function(appendParams){
5908         var url = this.options.url || this.form.url || this.form.el.dom.action;
5909         if(appendParams){
5910             var p = this.getParams();
5911             if(p){
5912                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5913             }
5914         }
5915         return url;
5916     },
5917
5918     getMethod : function(){
5919         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5920     },
5921
5922     getParams : function(){
5923         var bp = this.form.baseParams;
5924         var p = this.options.params;
5925         if(p){
5926             if(typeof p == "object"){
5927                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5928             }else if(typeof p == 'string' && bp){
5929                 p += '&' + Roo.urlEncode(bp);
5930             }
5931         }else if(bp){
5932             p = Roo.urlEncode(bp);
5933         }
5934         return p;
5935     },
5936
5937     createCallback : function(){
5938         return {
5939             success: this.success,
5940             failure: this.failure,
5941             scope: this,
5942             timeout: (this.form.timeout*1000),
5943             upload: this.form.fileUpload ? this.success : undefined
5944         };
5945     }
5946 };
5947
5948 Roo.form.Action.Submit = function(form, options){
5949     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5950 };
5951
5952 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5953     type : 'submit',
5954
5955     haveProgress : false,
5956     uploadComplete : false,
5957     
5958     // uploadProgress indicator.
5959     uploadProgress : function()
5960     {
5961         if (!this.form.progressUrl) {
5962             return;
5963         }
5964         
5965         if (!this.haveProgress) {
5966             Roo.MessageBox.progress("Uploading", "Uploading");
5967         }
5968         if (this.uploadComplete) {
5969            Roo.MessageBox.hide();
5970            return;
5971         }
5972         
5973         this.haveProgress = true;
5974    
5975         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5976         
5977         var c = new Roo.data.Connection();
5978         c.request({
5979             url : this.form.progressUrl,
5980             params: {
5981                 id : uid
5982             },
5983             method: 'GET',
5984             success : function(req){
5985                //console.log(data);
5986                 var rdata = false;
5987                 var edata;
5988                 try  {
5989                    rdata = Roo.decode(req.responseText)
5990                 } catch (e) {
5991                     Roo.log("Invalid data from server..");
5992                     Roo.log(edata);
5993                     return;
5994                 }
5995                 if (!rdata || !rdata.success) {
5996                     Roo.log(rdata);
5997                     Roo.MessageBox.alert(Roo.encode(rdata));
5998                     return;
5999                 }
6000                 var data = rdata.data;
6001                 
6002                 if (this.uploadComplete) {
6003                    Roo.MessageBox.hide();
6004                    return;
6005                 }
6006                    
6007                 if (data){
6008                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6009                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6010                     );
6011                 }
6012                 this.uploadProgress.defer(2000,this);
6013             },
6014        
6015             failure: function(data) {
6016                 Roo.log('progress url failed ');
6017                 Roo.log(data);
6018             },
6019             scope : this
6020         });
6021            
6022     },
6023     
6024     
6025     run : function()
6026     {
6027         // run get Values on the form, so it syncs any secondary forms.
6028         this.form.getValues();
6029         
6030         var o = this.options;
6031         var method = this.getMethod();
6032         var isPost = method == 'POST';
6033         if(o.clientValidation === false || this.form.isValid()){
6034             
6035             if (this.form.progressUrl) {
6036                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6037                     (new Date() * 1) + '' + Math.random());
6038                     
6039             } 
6040             
6041             
6042             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6043                 form:this.form.el.dom,
6044                 url:this.getUrl(!isPost),
6045                 method: method,
6046                 params:isPost ? this.getParams() : null,
6047                 isUpload: this.form.fileUpload
6048             }));
6049             
6050             this.uploadProgress();
6051
6052         }else if (o.clientValidation !== false){ // client validation failed
6053             this.failureType = Roo.form.Action.CLIENT_INVALID;
6054             this.form.afterAction(this, false);
6055         }
6056     },
6057
6058     success : function(response)
6059     {
6060         this.uploadComplete= true;
6061         if (this.haveProgress) {
6062             Roo.MessageBox.hide();
6063         }
6064         
6065         
6066         var result = this.processResponse(response);
6067         if(result === true || result.success){
6068             this.form.afterAction(this, true);
6069             return;
6070         }
6071         if(result.errors){
6072             this.form.markInvalid(result.errors);
6073             this.failureType = Roo.form.Action.SERVER_INVALID;
6074         }
6075         this.form.afterAction(this, false);
6076     },
6077     failure : function(response)
6078     {
6079         this.uploadComplete= true;
6080         if (this.haveProgress) {
6081             Roo.MessageBox.hide();
6082         }
6083         
6084         this.response = response;
6085         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6086         this.form.afterAction(this, false);
6087     },
6088     
6089     handleResponse : function(response){
6090         if(this.form.errorReader){
6091             var rs = this.form.errorReader.read(response);
6092             var errors = [];
6093             if(rs.records){
6094                 for(var i = 0, len = rs.records.length; i < len; i++) {
6095                     var r = rs.records[i];
6096                     errors[i] = r.data;
6097                 }
6098             }
6099             if(errors.length < 1){
6100                 errors = null;
6101             }
6102             return {
6103                 success : rs.success,
6104                 errors : errors
6105             };
6106         }
6107         var ret = false;
6108         try {
6109             ret = Roo.decode(response.responseText);
6110         } catch (e) {
6111             ret = {
6112                 success: false,
6113                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6114                 errors : []
6115             };
6116         }
6117         return ret;
6118         
6119     }
6120 });
6121
6122
6123 Roo.form.Action.Load = function(form, options){
6124     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6125     this.reader = this.form.reader;
6126 };
6127
6128 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6129     type : 'load',
6130
6131     run : function(){
6132         
6133         Roo.Ajax.request(Roo.apply(
6134                 this.createCallback(), {
6135                     method:this.getMethod(),
6136                     url:this.getUrl(false),
6137                     params:this.getParams()
6138         }));
6139     },
6140
6141     success : function(response){
6142         
6143         var result = this.processResponse(response);
6144         if(result === true || !result.success || !result.data){
6145             this.failureType = Roo.form.Action.LOAD_FAILURE;
6146             this.form.afterAction(this, false);
6147             return;
6148         }
6149         this.form.clearInvalid();
6150         this.form.setValues(result.data);
6151         this.form.afterAction(this, true);
6152     },
6153
6154     handleResponse : function(response){
6155         if(this.form.reader){
6156             var rs = this.form.reader.read(response);
6157             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6158             return {
6159                 success : rs.success,
6160                 data : data
6161             };
6162         }
6163         return Roo.decode(response.responseText);
6164     }
6165 });
6166
6167 Roo.form.Action.ACTION_TYPES = {
6168     'load' : Roo.form.Action.Load,
6169     'submit' : Roo.form.Action.Submit
6170 };/*
6171  * - LGPL
6172  *
6173  * form
6174  * 
6175  */
6176
6177 /**
6178  * @class Roo.bootstrap.Form
6179  * @extends Roo.bootstrap.Component
6180  * Bootstrap Form class
6181  * @cfg {String} method  GET | POST (default POST)
6182  * @cfg {String} labelAlign top | left (default top)
6183  * @cfg {String} align left  | right - for navbars
6184  * @cfg {Boolean} loadMask load mask when submit (default true)
6185
6186  * 
6187  * @constructor
6188  * Create a new Form
6189  * @param {Object} config The config object
6190  */
6191
6192
6193 Roo.bootstrap.Form = function(config){
6194     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6195     this.addEvents({
6196         /**
6197          * @event clientvalidation
6198          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6199          * @param {Form} this
6200          * @param {Boolean} valid true if the form has passed client-side validation
6201          */
6202         clientvalidation: true,
6203         /**
6204          * @event beforeaction
6205          * Fires before any action is performed. Return false to cancel the action.
6206          * @param {Form} this
6207          * @param {Action} action The action to be performed
6208          */
6209         beforeaction: true,
6210         /**
6211          * @event actionfailed
6212          * Fires when an action fails.
6213          * @param {Form} this
6214          * @param {Action} action The action that failed
6215          */
6216         actionfailed : true,
6217         /**
6218          * @event actioncomplete
6219          * Fires when an action is completed.
6220          * @param {Form} this
6221          * @param {Action} action The action that completed
6222          */
6223         actioncomplete : true
6224     });
6225     
6226 };
6227
6228 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6229       
6230      /**
6231      * @cfg {String} method
6232      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6233      */
6234     method : 'POST',
6235     /**
6236      * @cfg {String} url
6237      * The URL to use for form actions if one isn't supplied in the action options.
6238      */
6239     /**
6240      * @cfg {Boolean} fileUpload
6241      * Set to true if this form is a file upload.
6242      */
6243      
6244     /**
6245      * @cfg {Object} baseParams
6246      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6247      */
6248       
6249     /**
6250      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6251      */
6252     timeout: 30,
6253     /**
6254      * @cfg {Sting} align (left|right) for navbar forms
6255      */
6256     align : 'left',
6257
6258     // private
6259     activeAction : null,
6260  
6261     /**
6262      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6263      * element by passing it or its id or mask the form itself by passing in true.
6264      * @type Mixed
6265      */
6266     waitMsgTarget : false,
6267     
6268     loadMask : true,
6269     
6270     getAutoCreate : function(){
6271         
6272         var cfg = {
6273             tag: 'form',
6274             method : this.method || 'POST',
6275             id : this.id || Roo.id(),
6276             cls : ''
6277         }
6278         if (this.parent().xtype.match(/^Nav/)) {
6279             cfg.cls = 'navbar-form navbar-' + this.align;
6280             
6281         }
6282         
6283         if (this.labelAlign == 'left' ) {
6284             cfg.cls += ' form-horizontal';
6285         }
6286         
6287         
6288         return cfg;
6289     },
6290     initEvents : function()
6291     {
6292         this.el.on('submit', this.onSubmit, this);
6293         // this was added as random key presses on the form where triggering form submit.
6294         this.el.on('keypress', function(e) {
6295             if (e.getCharCode() != 13) {
6296                 return true;
6297             }
6298             // we might need to allow it for textareas.. and some other items.
6299             // check e.getTarget().
6300             
6301             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6302                 return true;
6303             }
6304         
6305             Roo.log("keypress blocked");
6306             
6307             e.preventDefault();
6308             return false;
6309         });
6310         
6311     },
6312     // private
6313     onSubmit : function(e){
6314         e.stopEvent();
6315     },
6316     
6317      /**
6318      * Returns true if client-side validation on the form is successful.
6319      * @return Boolean
6320      */
6321     isValid : function(){
6322         var items = this.getItems();
6323         var valid = true;
6324         items.each(function(f){
6325            if(!f.validate()){
6326                valid = false;
6327                
6328            }
6329         });
6330         return valid;
6331     },
6332     /**
6333      * Returns true if any fields in this form have changed since their original load.
6334      * @return Boolean
6335      */
6336     isDirty : function(){
6337         var dirty = false;
6338         var items = this.getItems();
6339         items.each(function(f){
6340            if(f.isDirty()){
6341                dirty = true;
6342                return false;
6343            }
6344            return true;
6345         });
6346         return dirty;
6347     },
6348      /**
6349      * Performs a predefined action (submit or load) or custom actions you define on this form.
6350      * @param {String} actionName The name of the action type
6351      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6352      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6353      * accept other config options):
6354      * <pre>
6355 Property          Type             Description
6356 ----------------  ---------------  ----------------------------------------------------------------------------------
6357 url               String           The url for the action (defaults to the form's url)
6358 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6359 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6360 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6361                                    validate the form on the client (defaults to false)
6362      * </pre>
6363      * @return {BasicForm} this
6364      */
6365     doAction : function(action, options){
6366         if(typeof action == 'string'){
6367             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6368         }
6369         if(this.fireEvent('beforeaction', this, action) !== false){
6370             this.beforeAction(action);
6371             action.run.defer(100, action);
6372         }
6373         return this;
6374     },
6375     
6376     // private
6377     beforeAction : function(action){
6378         var o = action.options;
6379         
6380         if(this.loadMask){
6381             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6382         }
6383         // not really supported yet.. ??
6384         
6385         //if(this.waitMsgTarget === true){
6386         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6387         //}else if(this.waitMsgTarget){
6388         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6389         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6390         //}else {
6391         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6392        // }
6393          
6394     },
6395
6396     // private
6397     afterAction : function(action, success){
6398         this.activeAction = null;
6399         var o = action.options;
6400         
6401         //if(this.waitMsgTarget === true){
6402             this.el.unmask();
6403         //}else if(this.waitMsgTarget){
6404         //    this.waitMsgTarget.unmask();
6405         //}else{
6406         //    Roo.MessageBox.updateProgress(1);
6407         //    Roo.MessageBox.hide();
6408        // }
6409         // 
6410         if(success){
6411             if(o.reset){
6412                 this.reset();
6413             }
6414             Roo.callback(o.success, o.scope, [this, action]);
6415             this.fireEvent('actioncomplete', this, action);
6416             
6417         }else{
6418             
6419             // failure condition..
6420             // we have a scenario where updates need confirming.
6421             // eg. if a locking scenario exists..
6422             // we look for { errors : { needs_confirm : true }} in the response.
6423             if (
6424                 (typeof(action.result) != 'undefined')  &&
6425                 (typeof(action.result.errors) != 'undefined')  &&
6426                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6427            ){
6428                 var _t = this;
6429                 Roo.log("not supported yet");
6430                  /*
6431                 
6432                 Roo.MessageBox.confirm(
6433                     "Change requires confirmation",
6434                     action.result.errorMsg,
6435                     function(r) {
6436                         if (r != 'yes') {
6437                             return;
6438                         }
6439                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6440                     }
6441                     
6442                 );
6443                 */
6444                 
6445                 
6446                 return;
6447             }
6448             
6449             Roo.callback(o.failure, o.scope, [this, action]);
6450             // show an error message if no failed handler is set..
6451             if (!this.hasListener('actionfailed')) {
6452                 Roo.log("need to add dialog support");
6453                 /*
6454                 Roo.MessageBox.alert("Error",
6455                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6456                         action.result.errorMsg :
6457                         "Saving Failed, please check your entries or try again"
6458                 );
6459                 */
6460             }
6461             
6462             this.fireEvent('actionfailed', this, action);
6463         }
6464         
6465     },
6466     /**
6467      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6468      * @param {String} id The value to search for
6469      * @return Field
6470      */
6471     findField : function(id){
6472         var items = this.getItems();
6473         var field = items.get(id);
6474         if(!field){
6475              items.each(function(f){
6476                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6477                     field = f;
6478                     return false;
6479                 }
6480                 return true;
6481             });
6482         }
6483         return field || null;
6484     },
6485      /**
6486      * Mark fields in this form invalid in bulk.
6487      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6488      * @return {BasicForm} this
6489      */
6490     markInvalid : function(errors){
6491         if(errors instanceof Array){
6492             for(var i = 0, len = errors.length; i < len; i++){
6493                 var fieldError = errors[i];
6494                 var f = this.findField(fieldError.id);
6495                 if(f){
6496                     f.markInvalid(fieldError.msg);
6497                 }
6498             }
6499         }else{
6500             var field, id;
6501             for(id in errors){
6502                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6503                     field.markInvalid(errors[id]);
6504                 }
6505             }
6506         }
6507         //Roo.each(this.childForms || [], function (f) {
6508         //    f.markInvalid(errors);
6509         //});
6510         
6511         return this;
6512     },
6513
6514     /**
6515      * Set values for fields in this form in bulk.
6516      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6517      * @return {BasicForm} this
6518      */
6519     setValues : function(values){
6520         if(values instanceof Array){ // array of objects
6521             for(var i = 0, len = values.length; i < len; i++){
6522                 var v = values[i];
6523                 var f = this.findField(v.id);
6524                 if(f){
6525                     f.setValue(v.value);
6526                     if(this.trackResetOnLoad){
6527                         f.originalValue = f.getValue();
6528                     }
6529                 }
6530             }
6531         }else{ // object hash
6532             var field, id;
6533             for(id in values){
6534                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6535                     
6536                     if (field.setFromData && 
6537                         field.valueField && 
6538                         field.displayField &&
6539                         // combos' with local stores can 
6540                         // be queried via setValue()
6541                         // to set their value..
6542                         (field.store && !field.store.isLocal)
6543                         ) {
6544                         // it's a combo
6545                         var sd = { };
6546                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6547                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6548                         field.setFromData(sd);
6549                         
6550                     } else {
6551                         field.setValue(values[id]);
6552                     }
6553                     
6554                     
6555                     if(this.trackResetOnLoad){
6556                         field.originalValue = field.getValue();
6557                     }
6558                 }
6559             }
6560         }
6561          
6562         //Roo.each(this.childForms || [], function (f) {
6563         //    f.setValues(values);
6564         //});
6565                 
6566         return this;
6567     },
6568
6569     /**
6570      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6571      * they are returned as an array.
6572      * @param {Boolean} asString
6573      * @return {Object}
6574      */
6575     getValues : function(asString){
6576         //if (this.childForms) {
6577             // copy values from the child forms
6578         //    Roo.each(this.childForms, function (f) {
6579         //        this.setValues(f.getValues());
6580         //    }, this);
6581         //}
6582         
6583         
6584         
6585         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6586         if(asString === true){
6587             return fs;
6588         }
6589         return Roo.urlDecode(fs);
6590     },
6591     
6592     /**
6593      * Returns the fields in this form as an object with key/value pairs. 
6594      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6595      * @return {Object}
6596      */
6597     getFieldValues : function(with_hidden)
6598     {
6599         var items = this.getItems();
6600         var ret = {};
6601         items.each(function(f){
6602             if (!f.getName()) {
6603                 return;
6604             }
6605             var v = f.getValue();
6606             if (f.inputType =='radio') {
6607                 if (typeof(ret[f.getName()]) == 'undefined') {
6608                     ret[f.getName()] = ''; // empty..
6609                 }
6610                 
6611                 if (!f.el.dom.checked) {
6612                     return;
6613                     
6614                 }
6615                 v = f.el.dom.value;
6616                 
6617             }
6618             
6619             // not sure if this supported any more..
6620             if ((typeof(v) == 'object') && f.getRawValue) {
6621                 v = f.getRawValue() ; // dates..
6622             }
6623             // combo boxes where name != hiddenName...
6624             if (f.name != f.getName()) {
6625                 ret[f.name] = f.getRawValue();
6626             }
6627             ret[f.getName()] = v;
6628         });
6629         
6630         return ret;
6631     },
6632
6633     /**
6634      * Clears all invalid messages in this form.
6635      * @return {BasicForm} this
6636      */
6637     clearInvalid : function(){
6638         var items = this.getItems();
6639         
6640         items.each(function(f){
6641            f.clearInvalid();
6642         });
6643         
6644         
6645         
6646         return this;
6647     },
6648
6649     /**
6650      * Resets this form.
6651      * @return {BasicForm} this
6652      */
6653     reset : function(){
6654         var items = this.getItems();
6655         items.each(function(f){
6656             f.reset();
6657         });
6658         
6659         Roo.each(this.childForms || [], function (f) {
6660             f.reset();
6661         });
6662        
6663         
6664         return this;
6665     },
6666     getItems : function()
6667     {
6668         var r=new Roo.util.MixedCollection(false, function(o){
6669             return o.id || (o.id = Roo.id());
6670         });
6671         var iter = function(el) {
6672             if (el.inputEl) {
6673                 r.add(el);
6674             }
6675             if (!el.items) {
6676                 return;
6677             }
6678             Roo.each(el.items,function(e) {
6679                 iter(e);
6680             });
6681             
6682             
6683         };
6684         iter(this);
6685         return r;
6686         
6687         
6688         
6689         
6690     }
6691     
6692 });
6693
6694  
6695 /*
6696  * Based on:
6697  * Ext JS Library 1.1.1
6698  * Copyright(c) 2006-2007, Ext JS, LLC.
6699  *
6700  * Originally Released Under LGPL - original licence link has changed is not relivant.
6701  *
6702  * Fork - LGPL
6703  * <script type="text/javascript">
6704  */
6705 /**
6706  * @class Roo.form.VTypes
6707  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6708  * @singleton
6709  */
6710 Roo.form.VTypes = function(){
6711     // closure these in so they are only created once.
6712     var alpha = /^[a-zA-Z_]+$/;
6713     var alphanum = /^[a-zA-Z0-9_]+$/;
6714     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6715     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6716
6717     // All these messages and functions are configurable
6718     return {
6719         /**
6720          * The function used to validate email addresses
6721          * @param {String} value The email address
6722          */
6723         'email' : function(v){
6724             return email.test(v);
6725         },
6726         /**
6727          * The error text to display when the email validation function returns false
6728          * @type String
6729          */
6730         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6731         /**
6732          * The keystroke filter mask to be applied on email input
6733          * @type RegExp
6734          */
6735         'emailMask' : /[a-z0-9_\.\-@]/i,
6736
6737         /**
6738          * The function used to validate URLs
6739          * @param {String} value The URL
6740          */
6741         'url' : function(v){
6742             return url.test(v);
6743         },
6744         /**
6745          * The error text to display when the url validation function returns false
6746          * @type String
6747          */
6748         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6749         
6750         /**
6751          * The function used to validate alpha values
6752          * @param {String} value The value
6753          */
6754         'alpha' : function(v){
6755             return alpha.test(v);
6756         },
6757         /**
6758          * The error text to display when the alpha validation function returns false
6759          * @type String
6760          */
6761         'alphaText' : 'This field should only contain letters and _',
6762         /**
6763          * The keystroke filter mask to be applied on alpha input
6764          * @type RegExp
6765          */
6766         'alphaMask' : /[a-z_]/i,
6767
6768         /**
6769          * The function used to validate alphanumeric values
6770          * @param {String} value The value
6771          */
6772         'alphanum' : function(v){
6773             return alphanum.test(v);
6774         },
6775         /**
6776          * The error text to display when the alphanumeric validation function returns false
6777          * @type String
6778          */
6779         'alphanumText' : 'This field should only contain letters, numbers and _',
6780         /**
6781          * The keystroke filter mask to be applied on alphanumeric input
6782          * @type RegExp
6783          */
6784         'alphanumMask' : /[a-z0-9_]/i
6785     };
6786 }();/*
6787  * - LGPL
6788  *
6789  * Input
6790  * 
6791  */
6792
6793 /**
6794  * @class Roo.bootstrap.Input
6795  * @extends Roo.bootstrap.Component
6796  * Bootstrap Input class
6797  * @cfg {Boolean} disabled is it disabled
6798  * @cfg {String} fieldLabel - the label associated
6799  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6800  * @cfg {String} name name of the input
6801  * @cfg {string} fieldLabel - the label associated
6802  * @cfg {string}  inputType - input / file submit ...
6803  * @cfg {string} placeholder - placeholder to put in text.
6804  * @cfg {string}  before - input group add on before
6805  * @cfg {string} after - input group add on after
6806  * @cfg {string} size - (lg|sm) or leave empty..
6807  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6808  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6809  * @cfg {Number} md colspan out of 12 for computer-sized screens
6810  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6811  * @cfg {string} value default value of the input
6812  * @cfg {Number} labelWidth set the width of label (0-12)
6813  * @cfg {String} labelAlign (top|left)
6814  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6815  * @cfg {String} align (left|center|right) Default left
6816  * 
6817  * 
6818  * @constructor
6819  * Create a new Input
6820  * @param {Object} config The config object
6821  */
6822
6823 Roo.bootstrap.Input = function(config){
6824     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6825    
6826         this.addEvents({
6827             /**
6828              * @event focus
6829              * Fires when this field receives input focus.
6830              * @param {Roo.form.Field} this
6831              */
6832             focus : true,
6833             /**
6834              * @event blur
6835              * Fires when this field loses input focus.
6836              * @param {Roo.form.Field} this
6837              */
6838             blur : true,
6839             /**
6840              * @event specialkey
6841              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6842              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6843              * @param {Roo.form.Field} this
6844              * @param {Roo.EventObject} e The event object
6845              */
6846             specialkey : true,
6847             /**
6848              * @event change
6849              * Fires just before the field blurs if the field value has changed.
6850              * @param {Roo.form.Field} this
6851              * @param {Mixed} newValue The new value
6852              * @param {Mixed} oldValue The original value
6853              */
6854             change : true,
6855             /**
6856              * @event invalid
6857              * Fires after the field has been marked as invalid.
6858              * @param {Roo.form.Field} this
6859              * @param {String} msg The validation message
6860              */
6861             invalid : true,
6862             /**
6863              * @event valid
6864              * Fires after the field has been validated with no errors.
6865              * @param {Roo.form.Field} this
6866              */
6867             valid : true,
6868              /**
6869              * @event keyup
6870              * Fires after the key up
6871              * @param {Roo.form.Field} this
6872              * @param {Roo.EventObject}  e The event Object
6873              */
6874             keyup : true
6875         });
6876 };
6877
6878 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6879      /**
6880      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6881       automatic validation (defaults to "keyup").
6882      */
6883     validationEvent : "keyup",
6884      /**
6885      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6886      */
6887     validateOnBlur : true,
6888     /**
6889      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6890      */
6891     validationDelay : 250,
6892      /**
6893      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6894      */
6895     focusClass : "x-form-focus",  // not needed???
6896     
6897        
6898     /**
6899      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6900      */
6901     invalidClass : "has-error",
6902     
6903     /**
6904      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6905      */
6906     selectOnFocus : false,
6907     
6908      /**
6909      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6910      */
6911     maskRe : null,
6912        /**
6913      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6914      */
6915     vtype : null,
6916     
6917       /**
6918      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6919      */
6920     disableKeyFilter : false,
6921     
6922        /**
6923      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6924      */
6925     disabled : false,
6926      /**
6927      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6928      */
6929     allowBlank : true,
6930     /**
6931      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6932      */
6933     blankText : "This field is required",
6934     
6935      /**
6936      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6937      */
6938     minLength : 0,
6939     /**
6940      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6941      */
6942     maxLength : Number.MAX_VALUE,
6943     /**
6944      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6945      */
6946     minLengthText : "The minimum length for this field is {0}",
6947     /**
6948      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6949      */
6950     maxLengthText : "The maximum length for this field is {0}",
6951   
6952     
6953     /**
6954      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6955      * If available, this function will be called only after the basic validators all return true, and will be passed the
6956      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6957      */
6958     validator : null,
6959     /**
6960      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6961      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6962      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6963      */
6964     regex : null,
6965     /**
6966      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6967      */
6968     regexText : "",
6969     
6970     
6971     
6972     fieldLabel : '',
6973     inputType : 'text',
6974     
6975     name : false,
6976     placeholder: false,
6977     before : false,
6978     after : false,
6979     size : false,
6980     // private
6981     hasFocus : false,
6982     preventMark: false,
6983     isFormField : true,
6984     value : '',
6985     labelWidth : 2,
6986     labelAlign : false,
6987     readOnly : false,
6988     align : false,
6989     formatedValue : false,
6990     
6991     parentLabelAlign : function()
6992     {
6993         var parent = this;
6994         while (parent.parent()) {
6995             parent = parent.parent();
6996             if (typeof(parent.labelAlign) !='undefined') {
6997                 return parent.labelAlign;
6998             }
6999         }
7000         return 'left';
7001         
7002     },
7003     
7004     getAutoCreate : function(){
7005         
7006         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7007         
7008         var id = Roo.id();
7009         
7010         var cfg = {};
7011         
7012         if(this.inputType != 'hidden'){
7013             cfg.cls = 'form-group' //input-group
7014         }
7015         
7016         var input =  {
7017             tag: 'input',
7018             id : id,
7019             type : this.inputType,
7020             value : this.value,
7021             cls : 'form-control',
7022             placeholder : this.placeholder || ''
7023             
7024         };
7025         
7026         if(this.align){
7027             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7028         }
7029         
7030         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7031             input.maxLength = this.maxLength;
7032         }
7033         
7034         if (this.disabled) {
7035             input.disabled=true;
7036         }
7037         
7038         if (this.readOnly) {
7039             input.readonly=true;
7040         }
7041         
7042         if (this.name) {
7043             input.name = this.name;
7044         }
7045         if (this.size) {
7046             input.cls += ' input-' + this.size;
7047         }
7048         var settings=this;
7049         ['xs','sm','md','lg'].map(function(size){
7050             if (settings[size]) {
7051                 cfg.cls += ' col-' + size + '-' + settings[size];
7052             }
7053         });
7054         
7055         var inputblock = input;
7056         
7057         if (this.before || this.after) {
7058             
7059             inputblock = {
7060                 cls : 'input-group',
7061                 cn :  [] 
7062             };
7063             if (this.before && typeof(this.before) == 'string') {
7064                 
7065                 inputblock.cn.push({
7066                     tag :'span',
7067                     cls : 'roo-input-before input-group-addon',
7068                     html : this.before
7069                 });
7070             }
7071             if (this.before && typeof(this.before) == 'object') {
7072                 this.before = Roo.factory(this.before);
7073                 Roo.log(this.before);
7074                 inputblock.cn.push({
7075                     tag :'span',
7076                     cls : 'roo-input-before input-group-' +
7077                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7078                 });
7079             }
7080             
7081             inputblock.cn.push(input);
7082             
7083             if (this.after && typeof(this.after) == 'string') {
7084                 inputblock.cn.push({
7085                     tag :'span',
7086                     cls : 'roo-input-after input-group-addon',
7087                     html : this.after
7088                 });
7089             }
7090             if (this.after && typeof(this.after) == 'object') {
7091                 this.after = Roo.factory(this.after);
7092                 Roo.log(this.after);
7093                 inputblock.cn.push({
7094                     tag :'span',
7095                     cls : 'roo-input-after input-group-' +
7096                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7097                 });
7098             }
7099         };
7100         
7101         if (align ==='left' && this.fieldLabel.length) {
7102                 Roo.log("left and has label");
7103                 cfg.cn = [
7104                     
7105                     {
7106                         tag: 'label',
7107                         'for' :  id,
7108                         cls : 'control-label col-sm-' + this.labelWidth,
7109                         html : this.fieldLabel
7110                         
7111                     },
7112                     {
7113                         cls : "col-sm-" + (12 - this.labelWidth), 
7114                         cn: [
7115                             inputblock
7116                         ]
7117                     }
7118                     
7119                 ];
7120         } else if ( this.fieldLabel.length) {
7121                 Roo.log(" label");
7122                  cfg.cn = [
7123                    
7124                     {
7125                         tag: 'label',
7126                         //cls : 'input-group-addon',
7127                         html : this.fieldLabel
7128                         
7129                     },
7130                     
7131                     inputblock
7132                     
7133                 ];
7134
7135         } else {
7136             
7137                 Roo.log(" no label && no align");
7138                 cfg.cn = [
7139                     
7140                         inputblock
7141                     
7142                 ];
7143                 
7144                 
7145         };
7146         Roo.log('input-parentType: ' + this.parentType);
7147         
7148         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7149            cfg.cls += ' navbar-form';
7150            Roo.log(cfg);
7151         }
7152         
7153         return cfg;
7154         
7155     },
7156     /**
7157      * return the real input element.
7158      */
7159     inputEl: function ()
7160     {
7161         return this.el.select('input.form-control',true).first();
7162     },
7163     setDisabled : function(v)
7164     {
7165         var i  = this.inputEl().dom;
7166         if (!v) {
7167             i.removeAttribute('disabled');
7168             return;
7169             
7170         }
7171         i.setAttribute('disabled','true');
7172     },
7173     initEvents : function()
7174     {
7175         
7176         this.inputEl().on("keydown" , this.fireKey,  this);
7177         this.inputEl().on("focus", this.onFocus,  this);
7178         this.inputEl().on("blur", this.onBlur,  this);
7179         
7180         this.inputEl().relayEvent('keyup', this);
7181
7182         // reference to original value for reset
7183         this.originalValue = this.getValue();
7184         //Roo.form.TextField.superclass.initEvents.call(this);
7185         if(this.validationEvent == 'keyup'){
7186             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7187             this.inputEl().on('keyup', this.filterValidation, this);
7188         }
7189         else if(this.validationEvent !== false){
7190             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7191         }
7192         
7193         if(this.selectOnFocus){
7194             this.on("focus", this.preFocus, this);
7195             
7196         }
7197         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7198             this.inputEl().on("keypress", this.filterKeys, this);
7199         }
7200        /* if(this.grow){
7201             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7202             this.el.on("click", this.autoSize,  this);
7203         }
7204         */
7205         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7206             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7207         }
7208         
7209         if (typeof(this.before) == 'object') {
7210             this.before.render(this.el.select('.roo-input-before',true).first());
7211         }
7212         if (typeof(this.after) == 'object') {
7213             this.after.render(this.el.select('.roo-input-after',true).first());
7214         }
7215         
7216         
7217     },
7218     filterValidation : function(e){
7219         if(!e.isNavKeyPress()){
7220             this.validationTask.delay(this.validationDelay);
7221         }
7222     },
7223      /**
7224      * Validates the field value
7225      * @return {Boolean} True if the value is valid, else false
7226      */
7227     validate : function(){
7228         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7229         if(this.disabled || this.validateValue(this.getRawValue())){
7230             this.clearInvalid();
7231             return true;
7232         }
7233         return false;
7234     },
7235     
7236     
7237     /**
7238      * Validates a value according to the field's validation rules and marks the field as invalid
7239      * if the validation fails
7240      * @param {Mixed} value The value to validate
7241      * @return {Boolean} True if the value is valid, else false
7242      */
7243     validateValue : function(value){
7244         if(value.length < 1)  { // if it's blank
7245              if(this.allowBlank){
7246                 this.clearInvalid();
7247                 return true;
7248              }else{
7249                 this.markInvalid(this.blankText);
7250                 return false;
7251              }
7252         }
7253         if(value.length < this.minLength){
7254             this.markInvalid(String.format(this.minLengthText, this.minLength));
7255             return false;
7256         }
7257         if(value.length > this.maxLength){
7258             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7259             return false;
7260         }
7261         if(this.vtype){
7262             var vt = Roo.form.VTypes;
7263             if(!vt[this.vtype](value, this)){
7264                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7265                 return false;
7266             }
7267         }
7268         if(typeof this.validator == "function"){
7269             var msg = this.validator(value);
7270             if(msg !== true){
7271                 this.markInvalid(msg);
7272                 return false;
7273             }
7274         }
7275         if(this.regex && !this.regex.test(value)){
7276             this.markInvalid(this.regexText);
7277             return false;
7278         }
7279         return true;
7280     },
7281
7282     
7283     
7284      // private
7285     fireKey : function(e){
7286         //Roo.log('field ' + e.getKey());
7287         if(e.isNavKeyPress()){
7288             this.fireEvent("specialkey", this, e);
7289         }
7290     },
7291     focus : function (selectText){
7292         if(this.rendered){
7293             this.inputEl().focus();
7294             if(selectText === true){
7295                 this.inputEl().dom.select();
7296             }
7297         }
7298         return this;
7299     } ,
7300     
7301     onFocus : function(){
7302         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7303            // this.el.addClass(this.focusClass);
7304         }
7305         if(!this.hasFocus){
7306             this.hasFocus = true;
7307             this.startValue = this.getValue();
7308             this.fireEvent("focus", this);
7309         }
7310     },
7311     
7312     beforeBlur : Roo.emptyFn,
7313
7314     
7315     // private
7316     onBlur : function(){
7317         this.beforeBlur();
7318         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7319             //this.el.removeClass(this.focusClass);
7320         }
7321         this.hasFocus = false;
7322         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7323             this.validate();
7324         }
7325         var v = this.getValue();
7326         if(String(v) !== String(this.startValue)){
7327             this.fireEvent('change', this, v, this.startValue);
7328         }
7329         this.fireEvent("blur", this);
7330     },
7331     
7332     /**
7333      * Resets the current field value to the originally loaded value and clears any validation messages
7334      */
7335     reset : function(){
7336         this.setValue(this.originalValue);
7337         this.clearInvalid();
7338     },
7339      /**
7340      * Returns the name of the field
7341      * @return {Mixed} name The name field
7342      */
7343     getName: function(){
7344         return this.name;
7345     },
7346      /**
7347      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7348      * @return {Mixed} value The field value
7349      */
7350     getValue : function(){
7351         
7352         var v = this.inputEl().getValue();
7353         
7354         return v;
7355     },
7356     /**
7357      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7358      * @return {Mixed} value The field value
7359      */
7360     getRawValue : function(){
7361         var v = this.inputEl().getValue();
7362         
7363         return v;
7364     },
7365     
7366     /**
7367      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7368      * @param {Mixed} value The value to set
7369      */
7370     setRawValue : function(v){
7371         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7372     },
7373     
7374     selectText : function(start, end){
7375         var v = this.getRawValue();
7376         if(v.length > 0){
7377             start = start === undefined ? 0 : start;
7378             end = end === undefined ? v.length : end;
7379             var d = this.inputEl().dom;
7380             if(d.setSelectionRange){
7381                 d.setSelectionRange(start, end);
7382             }else if(d.createTextRange){
7383                 var range = d.createTextRange();
7384                 range.moveStart("character", start);
7385                 range.moveEnd("character", v.length-end);
7386                 range.select();
7387             }
7388         }
7389     },
7390     
7391     /**
7392      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7393      * @param {Mixed} value The value to set
7394      */
7395     setValue : function(v){
7396         this.value = v;
7397         if(this.rendered){
7398             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7399             this.validate();
7400         }
7401     },
7402     
7403     /*
7404     processValue : function(value){
7405         if(this.stripCharsRe){
7406             var newValue = value.replace(this.stripCharsRe, '');
7407             if(newValue !== value){
7408                 this.setRawValue(newValue);
7409                 return newValue;
7410             }
7411         }
7412         return value;
7413     },
7414   */
7415     preFocus : function(){
7416         
7417         if(this.selectOnFocus){
7418             this.inputEl().dom.select();
7419         }
7420     },
7421     filterKeys : function(e){
7422         var k = e.getKey();
7423         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7424             return;
7425         }
7426         var c = e.getCharCode(), cc = String.fromCharCode(c);
7427         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7428             return;
7429         }
7430         if(!this.maskRe.test(cc)){
7431             e.stopEvent();
7432         }
7433     },
7434      /**
7435      * Clear any invalid styles/messages for this field
7436      */
7437     clearInvalid : function(){
7438         
7439         if(!this.el || this.preventMark){ // not rendered
7440             return;
7441         }
7442         this.el.removeClass(this.invalidClass);
7443         /*
7444         switch(this.msgTarget){
7445             case 'qtip':
7446                 this.el.dom.qtip = '';
7447                 break;
7448             case 'title':
7449                 this.el.dom.title = '';
7450                 break;
7451             case 'under':
7452                 if(this.errorEl){
7453                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7454                 }
7455                 break;
7456             case 'side':
7457                 if(this.errorIcon){
7458                     this.errorIcon.dom.qtip = '';
7459                     this.errorIcon.hide();
7460                     this.un('resize', this.alignErrorIcon, this);
7461                 }
7462                 break;
7463             default:
7464                 var t = Roo.getDom(this.msgTarget);
7465                 t.innerHTML = '';
7466                 t.style.display = 'none';
7467                 break;
7468         }
7469         */
7470         this.fireEvent('valid', this);
7471     },
7472      /**
7473      * Mark this field as invalid
7474      * @param {String} msg The validation message
7475      */
7476     markInvalid : function(msg){
7477         if(!this.el  || this.preventMark){ // not rendered
7478             return;
7479         }
7480         this.el.addClass(this.invalidClass);
7481         /*
7482         msg = msg || this.invalidText;
7483         switch(this.msgTarget){
7484             case 'qtip':
7485                 this.el.dom.qtip = msg;
7486                 this.el.dom.qclass = 'x-form-invalid-tip';
7487                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7488                     Roo.QuickTips.enable();
7489                 }
7490                 break;
7491             case 'title':
7492                 this.el.dom.title = msg;
7493                 break;
7494             case 'under':
7495                 if(!this.errorEl){
7496                     var elp = this.el.findParent('.x-form-element', 5, true);
7497                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7498                     this.errorEl.setWidth(elp.getWidth(true)-20);
7499                 }
7500                 this.errorEl.update(msg);
7501                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7502                 break;
7503             case 'side':
7504                 if(!this.errorIcon){
7505                     var elp = this.el.findParent('.x-form-element', 5, true);
7506                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7507                 }
7508                 this.alignErrorIcon();
7509                 this.errorIcon.dom.qtip = msg;
7510                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7511                 this.errorIcon.show();
7512                 this.on('resize', this.alignErrorIcon, this);
7513                 break;
7514             default:
7515                 var t = Roo.getDom(this.msgTarget);
7516                 t.innerHTML = msg;
7517                 t.style.display = this.msgDisplay;
7518                 break;
7519         }
7520         */
7521         this.fireEvent('invalid', this, msg);
7522     },
7523     // private
7524     SafariOnKeyDown : function(event)
7525     {
7526         // this is a workaround for a password hang bug on chrome/ webkit.
7527         
7528         var isSelectAll = false;
7529         
7530         if(this.inputEl().dom.selectionEnd > 0){
7531             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7532         }
7533         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7534             event.preventDefault();
7535             this.setValue('');
7536             return;
7537         }
7538         
7539         if(isSelectAll){ // backspace and delete key
7540             
7541             event.preventDefault();
7542             // this is very hacky as keydown always get's upper case.
7543             //
7544             var cc = String.fromCharCode(event.getCharCode());
7545             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7546             
7547         }
7548     },
7549     adjustWidth : function(tag, w){
7550         tag = tag.toLowerCase();
7551         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7552             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7553                 if(tag == 'input'){
7554                     return w + 2;
7555                 }
7556                 if(tag == 'textarea'){
7557                     return w-2;
7558                 }
7559             }else if(Roo.isOpera){
7560                 if(tag == 'input'){
7561                     return w + 2;
7562                 }
7563                 if(tag == 'textarea'){
7564                     return w-2;
7565                 }
7566             }
7567         }
7568         return w;
7569     }
7570     
7571 });
7572
7573  
7574 /*
7575  * - LGPL
7576  *
7577  * Input
7578  * 
7579  */
7580
7581 /**
7582  * @class Roo.bootstrap.TextArea
7583  * @extends Roo.bootstrap.Input
7584  * Bootstrap TextArea class
7585  * @cfg {Number} cols Specifies the visible width of a text area
7586  * @cfg {Number} rows Specifies the visible number of lines in a text area
7587  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7588  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7589  * @cfg {string} html text
7590  * 
7591  * @constructor
7592  * Create a new TextArea
7593  * @param {Object} config The config object
7594  */
7595
7596 Roo.bootstrap.TextArea = function(config){
7597     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7598    
7599 };
7600
7601 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7602      
7603     cols : false,
7604     rows : 5,
7605     readOnly : false,
7606     warp : 'soft',
7607     resize : false,
7608     value: false,
7609     html: false,
7610     
7611     getAutoCreate : function(){
7612         
7613         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7614         
7615         var id = Roo.id();
7616         
7617         var cfg = {};
7618         
7619         var input =  {
7620             tag: 'textarea',
7621             id : id,
7622             warp : this.warp,
7623             rows : this.rows,
7624             value : this.value || '',
7625             html: this.html || '',
7626             cls : 'form-control',
7627             placeholder : this.placeholder || '' 
7628             
7629         };
7630         
7631         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7632             input.maxLength = this.maxLength;
7633         }
7634         
7635         if(this.resize){
7636             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7637         }
7638         
7639         if(this.cols){
7640             input.cols = this.cols;
7641         }
7642         
7643         if (this.readOnly) {
7644             input.readonly = true;
7645         }
7646         
7647         if (this.name) {
7648             input.name = this.name;
7649         }
7650         
7651         if (this.size) {
7652             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7653         }
7654         
7655         var settings=this;
7656         ['xs','sm','md','lg'].map(function(size){
7657             if (settings[size]) {
7658                 cfg.cls += ' col-' + size + '-' + settings[size];
7659             }
7660         });
7661         
7662         var inputblock = input;
7663         
7664         if (this.before || this.after) {
7665             
7666             inputblock = {
7667                 cls : 'input-group',
7668                 cn :  [] 
7669             };
7670             if (this.before) {
7671                 inputblock.cn.push({
7672                     tag :'span',
7673                     cls : 'input-group-addon',
7674                     html : this.before
7675                 });
7676             }
7677             inputblock.cn.push(input);
7678             if (this.after) {
7679                 inputblock.cn.push({
7680                     tag :'span',
7681                     cls : 'input-group-addon',
7682                     html : this.after
7683                 });
7684             }
7685             
7686         }
7687         
7688         if (align ==='left' && this.fieldLabel.length) {
7689                 Roo.log("left and has label");
7690                 cfg.cn = [
7691                     
7692                     {
7693                         tag: 'label',
7694                         'for' :  id,
7695                         cls : 'control-label col-sm-' + this.labelWidth,
7696                         html : this.fieldLabel
7697                         
7698                     },
7699                     {
7700                         cls : "col-sm-" + (12 - this.labelWidth), 
7701                         cn: [
7702                             inputblock
7703                         ]
7704                     }
7705                     
7706                 ];
7707         } else if ( this.fieldLabel.length) {
7708                 Roo.log(" label");
7709                  cfg.cn = [
7710                    
7711                     {
7712                         tag: 'label',
7713                         //cls : 'input-group-addon',
7714                         html : this.fieldLabel
7715                         
7716                     },
7717                     
7718                     inputblock
7719                     
7720                 ];
7721
7722         } else {
7723             
7724                    Roo.log(" no label && no align");
7725                 cfg.cn = [
7726                     
7727                         inputblock
7728                     
7729                 ];
7730                 
7731                 
7732         }
7733         
7734         if (this.disabled) {
7735             input.disabled=true;
7736         }
7737         
7738         return cfg;
7739         
7740     },
7741     /**
7742      * return the real textarea element.
7743      */
7744     inputEl: function ()
7745     {
7746         return this.el.select('textarea.form-control',true).first();
7747     }
7748 });
7749
7750  
7751 /*
7752  * - LGPL
7753  *
7754  * trigger field - base class for combo..
7755  * 
7756  */
7757  
7758 /**
7759  * @class Roo.bootstrap.TriggerField
7760  * @extends Roo.bootstrap.Input
7761  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7762  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7763  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7764  * for which you can provide a custom implementation.  For example:
7765  * <pre><code>
7766 var trigger = new Roo.bootstrap.TriggerField();
7767 trigger.onTriggerClick = myTriggerFn;
7768 trigger.applyTo('my-field');
7769 </code></pre>
7770  *
7771  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7772  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7773  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7774  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7775  * @constructor
7776  * Create a new TriggerField.
7777  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7778  * to the base TextField)
7779  */
7780 Roo.bootstrap.TriggerField = function(config){
7781     this.mimicing = false;
7782     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7783 };
7784
7785 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7786     /**
7787      * @cfg {String} triggerClass A CSS class to apply to the trigger
7788      */
7789      /**
7790      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7791      */
7792     hideTrigger:false,
7793
7794     /** @cfg {Boolean} grow @hide */
7795     /** @cfg {Number} growMin @hide */
7796     /** @cfg {Number} growMax @hide */
7797
7798     /**
7799      * @hide 
7800      * @method
7801      */
7802     autoSize: Roo.emptyFn,
7803     // private
7804     monitorTab : true,
7805     // private
7806     deferHeight : true,
7807
7808     
7809     actionMode : 'wrap',
7810     
7811     
7812     
7813     getAutoCreate : function(){
7814        
7815         var align = this.labelAlign || this.parentLabelAlign();
7816         
7817         var id = Roo.id();
7818         
7819         var cfg = {
7820             cls: 'form-group' //input-group
7821         };
7822         
7823         
7824         var input =  {
7825             tag: 'input',
7826             id : id,
7827             type : this.inputType,
7828             cls : 'form-control',
7829             autocomplete: 'off',
7830             placeholder : this.placeholder || '' 
7831             
7832         };
7833         if (this.name) {
7834             input.name = this.name;
7835         }
7836         if (this.size) {
7837             input.cls += ' input-' + this.size;
7838         }
7839         
7840         if (this.disabled) {
7841             input.disabled=true;
7842         }
7843         
7844         var inputblock = input;
7845         
7846         if (this.before || this.after) {
7847             
7848             inputblock = {
7849                 cls : 'input-group',
7850                 cn :  [] 
7851             };
7852             if (this.before) {
7853                 inputblock.cn.push({
7854                     tag :'span',
7855                     cls : 'input-group-addon',
7856                     html : this.before
7857                 });
7858             }
7859             inputblock.cn.push(input);
7860             if (this.after) {
7861                 inputblock.cn.push({
7862                     tag :'span',
7863                     cls : 'input-group-addon',
7864                     html : this.after
7865                 });
7866             }
7867             
7868         };
7869         
7870         var box = {
7871             tag: 'div',
7872             cn: [
7873                 {
7874                     tag: 'input',
7875                     type : 'hidden',
7876                     cls: 'form-hidden-field'
7877                 },
7878                 inputblock
7879             ]
7880             
7881         };
7882         
7883         if(this.multiple){
7884             Roo.log('multiple');
7885             
7886             box = {
7887                 tag: 'div',
7888                 cn: [
7889                     {
7890                         tag: 'input',
7891                         type : 'hidden',
7892                         cls: 'form-hidden-field'
7893                     },
7894                     {
7895                         tag: 'ul',
7896                         cls: 'select2-choices',
7897                         cn:[
7898                             {
7899                                 tag: 'li',
7900                                 cls: 'select2-search-field',
7901                                 cn: [
7902
7903                                     inputblock
7904                                 ]
7905                             }
7906                         ]
7907                     }
7908                 ]
7909             }
7910         };
7911         
7912         var combobox = {
7913             cls: 'select2-container input-group',
7914             cn: [
7915                 box
7916 //                {
7917 //                    tag: 'ul',
7918 //                    cls: 'typeahead typeahead-long dropdown-menu',
7919 //                    style: 'display:none'
7920 //                }
7921             ]
7922         };
7923         
7924         if(!this.multiple && this.showToggleBtn){
7925             combobox.cn.push({
7926                 tag :'span',
7927                 cls : 'input-group-addon btn dropdown-toggle',
7928                 cn : [
7929                     {
7930                         tag: 'span',
7931                         cls: 'caret'
7932                     },
7933                     {
7934                         tag: 'span',
7935                         cls: 'combobox-clear',
7936                         cn  : [
7937                             {
7938                                 tag : 'i',
7939                                 cls: 'icon-remove'
7940                             }
7941                         ]
7942                     }
7943                 ]
7944
7945             })
7946         }
7947         
7948         if(this.multiple){
7949             combobox.cls += ' select2-container-multi';
7950         }
7951         
7952         if (align ==='left' && this.fieldLabel.length) {
7953             
7954                 Roo.log("left and has label");
7955                 cfg.cn = [
7956                     
7957                     {
7958                         tag: 'label',
7959                         'for' :  id,
7960                         cls : 'control-label col-sm-' + this.labelWidth,
7961                         html : this.fieldLabel
7962                         
7963                     },
7964                     {
7965                         cls : "col-sm-" + (12 - this.labelWidth), 
7966                         cn: [
7967                             combobox
7968                         ]
7969                     }
7970                     
7971                 ];
7972         } else if ( this.fieldLabel.length) {
7973                 Roo.log(" label");
7974                  cfg.cn = [
7975                    
7976                     {
7977                         tag: 'label',
7978                         //cls : 'input-group-addon',
7979                         html : this.fieldLabel
7980                         
7981                     },
7982                     
7983                     combobox
7984                     
7985                 ];
7986
7987         } else {
7988             
7989                 Roo.log(" no label && no align");
7990                 cfg = combobox
7991                      
7992                 
7993         }
7994          
7995         var settings=this;
7996         ['xs','sm','md','lg'].map(function(size){
7997             if (settings[size]) {
7998                 cfg.cls += ' col-' + size + '-' + settings[size];
7999             }
8000         });
8001         
8002         return cfg;
8003         
8004     },
8005     
8006     
8007     
8008     // private
8009     onResize : function(w, h){
8010 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8011 //        if(typeof w == 'number'){
8012 //            var x = w - this.trigger.getWidth();
8013 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8014 //            this.trigger.setStyle('left', x+'px');
8015 //        }
8016     },
8017
8018     // private
8019     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8020
8021     // private
8022     getResizeEl : function(){
8023         return this.inputEl();
8024     },
8025
8026     // private
8027     getPositionEl : function(){
8028         return this.inputEl();
8029     },
8030
8031     // private
8032     alignErrorIcon : function(){
8033         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8034     },
8035
8036     // private
8037     initEvents : function(){
8038         
8039         this.createList();
8040         
8041         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8042         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8043         if(!this.multiple && this.showToggleBtn){
8044             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8045             if(this.hideTrigger){
8046                 this.trigger.setDisplayed(false);
8047             }
8048             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8049         }
8050         
8051         if(this.multiple){
8052             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8053         }
8054         
8055         //this.trigger.addClassOnOver('x-form-trigger-over');
8056         //this.trigger.addClassOnClick('x-form-trigger-click');
8057         
8058         //if(!this.width){
8059         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8060         //}
8061     },
8062     
8063     createList : function()
8064     {
8065         this.list = Roo.get(document.body).createChild({
8066             tag: 'ul',
8067             cls: 'typeahead typeahead-long dropdown-menu',
8068             style: 'display:none'
8069         });
8070         
8071         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8072         
8073     },
8074
8075     // private
8076     initTrigger : function(){
8077        
8078     },
8079
8080     // private
8081     onDestroy : function(){
8082         if(this.trigger){
8083             this.trigger.removeAllListeners();
8084           //  this.trigger.remove();
8085         }
8086         //if(this.wrap){
8087         //    this.wrap.remove();
8088         //}
8089         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8090     },
8091
8092     // private
8093     onFocus : function(){
8094         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8095         /*
8096         if(!this.mimicing){
8097             this.wrap.addClass('x-trigger-wrap-focus');
8098             this.mimicing = true;
8099             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8100             if(this.monitorTab){
8101                 this.el.on("keydown", this.checkTab, this);
8102             }
8103         }
8104         */
8105     },
8106
8107     // private
8108     checkTab : function(e){
8109         if(e.getKey() == e.TAB){
8110             this.triggerBlur();
8111         }
8112     },
8113
8114     // private
8115     onBlur : function(){
8116         // do nothing
8117     },
8118
8119     // private
8120     mimicBlur : function(e, t){
8121         /*
8122         if(!this.wrap.contains(t) && this.validateBlur()){
8123             this.triggerBlur();
8124         }
8125         */
8126     },
8127
8128     // private
8129     triggerBlur : function(){
8130         this.mimicing = false;
8131         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8132         if(this.monitorTab){
8133             this.el.un("keydown", this.checkTab, this);
8134         }
8135         //this.wrap.removeClass('x-trigger-wrap-focus');
8136         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8137     },
8138
8139     // private
8140     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8141     validateBlur : function(e, t){
8142         return true;
8143     },
8144
8145     // private
8146     onDisable : function(){
8147         this.inputEl().dom.disabled = true;
8148         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8149         //if(this.wrap){
8150         //    this.wrap.addClass('x-item-disabled');
8151         //}
8152     },
8153
8154     // private
8155     onEnable : function(){
8156         this.inputEl().dom.disabled = false;
8157         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8158         //if(this.wrap){
8159         //    this.el.removeClass('x-item-disabled');
8160         //}
8161     },
8162
8163     // private
8164     onShow : function(){
8165         var ae = this.getActionEl();
8166         
8167         if(ae){
8168             ae.dom.style.display = '';
8169             ae.dom.style.visibility = 'visible';
8170         }
8171     },
8172
8173     // private
8174     
8175     onHide : function(){
8176         var ae = this.getActionEl();
8177         ae.dom.style.display = 'none';
8178     },
8179
8180     /**
8181      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8182      * by an implementing function.
8183      * @method
8184      * @param {EventObject} e
8185      */
8186     onTriggerClick : Roo.emptyFn
8187 });
8188  /*
8189  * Based on:
8190  * Ext JS Library 1.1.1
8191  * Copyright(c) 2006-2007, Ext JS, LLC.
8192  *
8193  * Originally Released Under LGPL - original licence link has changed is not relivant.
8194  *
8195  * Fork - LGPL
8196  * <script type="text/javascript">
8197  */
8198
8199
8200 /**
8201  * @class Roo.data.SortTypes
8202  * @singleton
8203  * Defines the default sorting (casting?) comparison functions used when sorting data.
8204  */
8205 Roo.data.SortTypes = {
8206     /**
8207      * Default sort that does nothing
8208      * @param {Mixed} s The value being converted
8209      * @return {Mixed} The comparison value
8210      */
8211     none : function(s){
8212         return s;
8213     },
8214     
8215     /**
8216      * The regular expression used to strip tags
8217      * @type {RegExp}
8218      * @property
8219      */
8220     stripTagsRE : /<\/?[^>]+>/gi,
8221     
8222     /**
8223      * Strips all HTML tags to sort on text only
8224      * @param {Mixed} s The value being converted
8225      * @return {String} The comparison value
8226      */
8227     asText : function(s){
8228         return String(s).replace(this.stripTagsRE, "");
8229     },
8230     
8231     /**
8232      * Strips all HTML tags to sort on text only - Case insensitive
8233      * @param {Mixed} s The value being converted
8234      * @return {String} The comparison value
8235      */
8236     asUCText : function(s){
8237         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8238     },
8239     
8240     /**
8241      * Case insensitive string
8242      * @param {Mixed} s The value being converted
8243      * @return {String} The comparison value
8244      */
8245     asUCString : function(s) {
8246         return String(s).toUpperCase();
8247     },
8248     
8249     /**
8250      * Date sorting
8251      * @param {Mixed} s The value being converted
8252      * @return {Number} The comparison value
8253      */
8254     asDate : function(s) {
8255         if(!s){
8256             return 0;
8257         }
8258         if(s instanceof Date){
8259             return s.getTime();
8260         }
8261         return Date.parse(String(s));
8262     },
8263     
8264     /**
8265      * Float sorting
8266      * @param {Mixed} s The value being converted
8267      * @return {Float} The comparison value
8268      */
8269     asFloat : function(s) {
8270         var val = parseFloat(String(s).replace(/,/g, ""));
8271         if(isNaN(val)) val = 0;
8272         return val;
8273     },
8274     
8275     /**
8276      * Integer sorting
8277      * @param {Mixed} s The value being converted
8278      * @return {Number} The comparison value
8279      */
8280     asInt : function(s) {
8281         var val = parseInt(String(s).replace(/,/g, ""));
8282         if(isNaN(val)) val = 0;
8283         return val;
8284     }
8285 };/*
8286  * Based on:
8287  * Ext JS Library 1.1.1
8288  * Copyright(c) 2006-2007, Ext JS, LLC.
8289  *
8290  * Originally Released Under LGPL - original licence link has changed is not relivant.
8291  *
8292  * Fork - LGPL
8293  * <script type="text/javascript">
8294  */
8295
8296 /**
8297 * @class Roo.data.Record
8298  * Instances of this class encapsulate both record <em>definition</em> information, and record
8299  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8300  * to access Records cached in an {@link Roo.data.Store} object.<br>
8301  * <p>
8302  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8303  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8304  * objects.<br>
8305  * <p>
8306  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8307  * @constructor
8308  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8309  * {@link #create}. The parameters are the same.
8310  * @param {Array} data An associative Array of data values keyed by the field name.
8311  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8312  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8313  * not specified an integer id is generated.
8314  */
8315 Roo.data.Record = function(data, id){
8316     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8317     this.data = data;
8318 };
8319
8320 /**
8321  * Generate a constructor for a specific record layout.
8322  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8323  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8324  * Each field definition object may contain the following properties: <ul>
8325  * <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,
8326  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8327  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8328  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8329  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8330  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8331  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8332  * this may be omitted.</p></li>
8333  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8334  * <ul><li>auto (Default, implies no conversion)</li>
8335  * <li>string</li>
8336  * <li>int</li>
8337  * <li>float</li>
8338  * <li>boolean</li>
8339  * <li>date</li></ul></p></li>
8340  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8341  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8342  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8343  * by the Reader into an object that will be stored in the Record. It is passed the
8344  * following parameters:<ul>
8345  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8346  * </ul></p></li>
8347  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8348  * </ul>
8349  * <br>usage:<br><pre><code>
8350 var TopicRecord = Roo.data.Record.create(
8351     {name: 'title', mapping: 'topic_title'},
8352     {name: 'author', mapping: 'username'},
8353     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8354     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8355     {name: 'lastPoster', mapping: 'user2'},
8356     {name: 'excerpt', mapping: 'post_text'}
8357 );
8358
8359 var myNewRecord = new TopicRecord({
8360     title: 'Do my job please',
8361     author: 'noobie',
8362     totalPosts: 1,
8363     lastPost: new Date(),
8364     lastPoster: 'Animal',
8365     excerpt: 'No way dude!'
8366 });
8367 myStore.add(myNewRecord);
8368 </code></pre>
8369  * @method create
8370  * @static
8371  */
8372 Roo.data.Record.create = function(o){
8373     var f = function(){
8374         f.superclass.constructor.apply(this, arguments);
8375     };
8376     Roo.extend(f, Roo.data.Record);
8377     var p = f.prototype;
8378     p.fields = new Roo.util.MixedCollection(false, function(field){
8379         return field.name;
8380     });
8381     for(var i = 0, len = o.length; i < len; i++){
8382         p.fields.add(new Roo.data.Field(o[i]));
8383     }
8384     f.getField = function(name){
8385         return p.fields.get(name);  
8386     };
8387     return f;
8388 };
8389
8390 Roo.data.Record.AUTO_ID = 1000;
8391 Roo.data.Record.EDIT = 'edit';
8392 Roo.data.Record.REJECT = 'reject';
8393 Roo.data.Record.COMMIT = 'commit';
8394
8395 Roo.data.Record.prototype = {
8396     /**
8397      * Readonly flag - true if this record has been modified.
8398      * @type Boolean
8399      */
8400     dirty : false,
8401     editing : false,
8402     error: null,
8403     modified: null,
8404
8405     // private
8406     join : function(store){
8407         this.store = store;
8408     },
8409
8410     /**
8411      * Set the named field to the specified value.
8412      * @param {String} name The name of the field to set.
8413      * @param {Object} value The value to set the field to.
8414      */
8415     set : function(name, value){
8416         if(this.data[name] == value){
8417             return;
8418         }
8419         this.dirty = true;
8420         if(!this.modified){
8421             this.modified = {};
8422         }
8423         if(typeof this.modified[name] == 'undefined'){
8424             this.modified[name] = this.data[name];
8425         }
8426         this.data[name] = value;
8427         if(!this.editing && this.store){
8428             this.store.afterEdit(this);
8429         }       
8430     },
8431
8432     /**
8433      * Get the value of the named field.
8434      * @param {String} name The name of the field to get the value of.
8435      * @return {Object} The value of the field.
8436      */
8437     get : function(name){
8438         return this.data[name]; 
8439     },
8440
8441     // private
8442     beginEdit : function(){
8443         this.editing = true;
8444         this.modified = {}; 
8445     },
8446
8447     // private
8448     cancelEdit : function(){
8449         this.editing = false;
8450         delete this.modified;
8451     },
8452
8453     // private
8454     endEdit : function(){
8455         this.editing = false;
8456         if(this.dirty && this.store){
8457             this.store.afterEdit(this);
8458         }
8459     },
8460
8461     /**
8462      * Usually called by the {@link Roo.data.Store} which owns the Record.
8463      * Rejects all changes made to the Record since either creation, or the last commit operation.
8464      * Modified fields are reverted to their original values.
8465      * <p>
8466      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8467      * of reject operations.
8468      */
8469     reject : function(){
8470         var m = this.modified;
8471         for(var n in m){
8472             if(typeof m[n] != "function"){
8473                 this.data[n] = m[n];
8474             }
8475         }
8476         this.dirty = false;
8477         delete this.modified;
8478         this.editing = false;
8479         if(this.store){
8480             this.store.afterReject(this);
8481         }
8482     },
8483
8484     /**
8485      * Usually called by the {@link Roo.data.Store} which owns the Record.
8486      * Commits all changes made to the Record since either creation, or the last commit operation.
8487      * <p>
8488      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8489      * of commit operations.
8490      */
8491     commit : function(){
8492         this.dirty = false;
8493         delete this.modified;
8494         this.editing = false;
8495         if(this.store){
8496             this.store.afterCommit(this);
8497         }
8498     },
8499
8500     // private
8501     hasError : function(){
8502         return this.error != null;
8503     },
8504
8505     // private
8506     clearError : function(){
8507         this.error = null;
8508     },
8509
8510     /**
8511      * Creates a copy of this record.
8512      * @param {String} id (optional) A new record id if you don't want to use this record's id
8513      * @return {Record}
8514      */
8515     copy : function(newId) {
8516         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8517     }
8518 };/*
8519  * Based on:
8520  * Ext JS Library 1.1.1
8521  * Copyright(c) 2006-2007, Ext JS, LLC.
8522  *
8523  * Originally Released Under LGPL - original licence link has changed is not relivant.
8524  *
8525  * Fork - LGPL
8526  * <script type="text/javascript">
8527  */
8528
8529
8530
8531 /**
8532  * @class Roo.data.Store
8533  * @extends Roo.util.Observable
8534  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8535  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8536  * <p>
8537  * 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
8538  * has no knowledge of the format of the data returned by the Proxy.<br>
8539  * <p>
8540  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8541  * instances from the data object. These records are cached and made available through accessor functions.
8542  * @constructor
8543  * Creates a new Store.
8544  * @param {Object} config A config object containing the objects needed for the Store to access data,
8545  * and read the data into Records.
8546  */
8547 Roo.data.Store = function(config){
8548     this.data = new Roo.util.MixedCollection(false);
8549     this.data.getKey = function(o){
8550         return o.id;
8551     };
8552     this.baseParams = {};
8553     // private
8554     this.paramNames = {
8555         "start" : "start",
8556         "limit" : "limit",
8557         "sort" : "sort",
8558         "dir" : "dir",
8559         "multisort" : "_multisort"
8560     };
8561
8562     if(config && config.data){
8563         this.inlineData = config.data;
8564         delete config.data;
8565     }
8566
8567     Roo.apply(this, config);
8568     
8569     if(this.reader){ // reader passed
8570         this.reader = Roo.factory(this.reader, Roo.data);
8571         this.reader.xmodule = this.xmodule || false;
8572         if(!this.recordType){
8573             this.recordType = this.reader.recordType;
8574         }
8575         if(this.reader.onMetaChange){
8576             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8577         }
8578     }
8579
8580     if(this.recordType){
8581         this.fields = this.recordType.prototype.fields;
8582     }
8583     this.modified = [];
8584
8585     this.addEvents({
8586         /**
8587          * @event datachanged
8588          * Fires when the data cache has changed, and a widget which is using this Store
8589          * as a Record cache should refresh its view.
8590          * @param {Store} this
8591          */
8592         datachanged : true,
8593         /**
8594          * @event metachange
8595          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8596          * @param {Store} this
8597          * @param {Object} meta The JSON metadata
8598          */
8599         metachange : true,
8600         /**
8601          * @event add
8602          * Fires when Records have been added to the Store
8603          * @param {Store} this
8604          * @param {Roo.data.Record[]} records The array of Records added
8605          * @param {Number} index The index at which the record(s) were added
8606          */
8607         add : true,
8608         /**
8609          * @event remove
8610          * Fires when a Record has been removed from the Store
8611          * @param {Store} this
8612          * @param {Roo.data.Record} record The Record that was removed
8613          * @param {Number} index The index at which the record was removed
8614          */
8615         remove : true,
8616         /**
8617          * @event update
8618          * Fires when a Record has been updated
8619          * @param {Store} this
8620          * @param {Roo.data.Record} record The Record that was updated
8621          * @param {String} operation The update operation being performed.  Value may be one of:
8622          * <pre><code>
8623  Roo.data.Record.EDIT
8624  Roo.data.Record.REJECT
8625  Roo.data.Record.COMMIT
8626          * </code></pre>
8627          */
8628         update : true,
8629         /**
8630          * @event clear
8631          * Fires when the data cache has been cleared.
8632          * @param {Store} this
8633          */
8634         clear : true,
8635         /**
8636          * @event beforeload
8637          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8638          * the load action will be canceled.
8639          * @param {Store} this
8640          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8641          */
8642         beforeload : true,
8643         /**
8644          * @event beforeloadadd
8645          * Fires after a new set of Records has been loaded.
8646          * @param {Store} this
8647          * @param {Roo.data.Record[]} records The Records that were loaded
8648          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8649          */
8650         beforeloadadd : true,
8651         /**
8652          * @event load
8653          * Fires after a new set of Records has been loaded, before they are added to the store.
8654          * @param {Store} this
8655          * @param {Roo.data.Record[]} records The Records that were loaded
8656          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8657          * @params {Object} return from reader
8658          */
8659         load : true,
8660         /**
8661          * @event loadexception
8662          * Fires if an exception occurs in the Proxy during loading.
8663          * Called with the signature of the Proxy's "loadexception" event.
8664          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8665          * 
8666          * @param {Proxy} 
8667          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8668          * @param {Object} load options 
8669          * @param {Object} jsonData from your request (normally this contains the Exception)
8670          */
8671         loadexception : true
8672     });
8673     
8674     if(this.proxy){
8675         this.proxy = Roo.factory(this.proxy, Roo.data);
8676         this.proxy.xmodule = this.xmodule || false;
8677         this.relayEvents(this.proxy,  ["loadexception"]);
8678     }
8679     this.sortToggle = {};
8680     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8681
8682     Roo.data.Store.superclass.constructor.call(this);
8683
8684     if(this.inlineData){
8685         this.loadData(this.inlineData);
8686         delete this.inlineData;
8687     }
8688 };
8689
8690 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8691      /**
8692     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8693     * without a remote query - used by combo/forms at present.
8694     */
8695     
8696     /**
8697     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8698     */
8699     /**
8700     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8701     */
8702     /**
8703     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8704     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8705     */
8706     /**
8707     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8708     * on any HTTP request
8709     */
8710     /**
8711     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8712     */
8713     /**
8714     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8715     */
8716     multiSort: false,
8717     /**
8718     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8719     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8720     */
8721     remoteSort : false,
8722
8723     /**
8724     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8725      * loaded or when a record is removed. (defaults to false).
8726     */
8727     pruneModifiedRecords : false,
8728
8729     // private
8730     lastOptions : null,
8731
8732     /**
8733      * Add Records to the Store and fires the add event.
8734      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8735      */
8736     add : function(records){
8737         records = [].concat(records);
8738         for(var i = 0, len = records.length; i < len; i++){
8739             records[i].join(this);
8740         }
8741         var index = this.data.length;
8742         this.data.addAll(records);
8743         this.fireEvent("add", this, records, index);
8744     },
8745
8746     /**
8747      * Remove a Record from the Store and fires the remove event.
8748      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8749      */
8750     remove : function(record){
8751         var index = this.data.indexOf(record);
8752         this.data.removeAt(index);
8753         if(this.pruneModifiedRecords){
8754             this.modified.remove(record);
8755         }
8756         this.fireEvent("remove", this, record, index);
8757     },
8758
8759     /**
8760      * Remove all Records from the Store and fires the clear event.
8761      */
8762     removeAll : function(){
8763         this.data.clear();
8764         if(this.pruneModifiedRecords){
8765             this.modified = [];
8766         }
8767         this.fireEvent("clear", this);
8768     },
8769
8770     /**
8771      * Inserts Records to the Store at the given index and fires the add event.
8772      * @param {Number} index The start index at which to insert the passed Records.
8773      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8774      */
8775     insert : function(index, records){
8776         records = [].concat(records);
8777         for(var i = 0, len = records.length; i < len; i++){
8778             this.data.insert(index, records[i]);
8779             records[i].join(this);
8780         }
8781         this.fireEvent("add", this, records, index);
8782     },
8783
8784     /**
8785      * Get the index within the cache of the passed Record.
8786      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8787      * @return {Number} The index of the passed Record. Returns -1 if not found.
8788      */
8789     indexOf : function(record){
8790         return this.data.indexOf(record);
8791     },
8792
8793     /**
8794      * Get the index within the cache of the Record with the passed id.
8795      * @param {String} id The id of the Record to find.
8796      * @return {Number} The index of the Record. Returns -1 if not found.
8797      */
8798     indexOfId : function(id){
8799         return this.data.indexOfKey(id);
8800     },
8801
8802     /**
8803      * Get the Record with the specified id.
8804      * @param {String} id The id of the Record to find.
8805      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8806      */
8807     getById : function(id){
8808         return this.data.key(id);
8809     },
8810
8811     /**
8812      * Get the Record at the specified index.
8813      * @param {Number} index The index of the Record to find.
8814      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8815      */
8816     getAt : function(index){
8817         return this.data.itemAt(index);
8818     },
8819
8820     /**
8821      * Returns a range of Records between specified indices.
8822      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8823      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8824      * @return {Roo.data.Record[]} An array of Records
8825      */
8826     getRange : function(start, end){
8827         return this.data.getRange(start, end);
8828     },
8829
8830     // private
8831     storeOptions : function(o){
8832         o = Roo.apply({}, o);
8833         delete o.callback;
8834         delete o.scope;
8835         this.lastOptions = o;
8836     },
8837
8838     /**
8839      * Loads the Record cache from the configured Proxy using the configured Reader.
8840      * <p>
8841      * If using remote paging, then the first load call must specify the <em>start</em>
8842      * and <em>limit</em> properties in the options.params property to establish the initial
8843      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8844      * <p>
8845      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8846      * and this call will return before the new data has been loaded. Perform any post-processing
8847      * in a callback function, or in a "load" event handler.</strong>
8848      * <p>
8849      * @param {Object} options An object containing properties which control loading options:<ul>
8850      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8851      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8852      * passed the following arguments:<ul>
8853      * <li>r : Roo.data.Record[]</li>
8854      * <li>options: Options object from the load call</li>
8855      * <li>success: Boolean success indicator</li></ul></li>
8856      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8857      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8858      * </ul>
8859      */
8860     load : function(options){
8861         options = options || {};
8862         if(this.fireEvent("beforeload", this, options) !== false){
8863             this.storeOptions(options);
8864             var p = Roo.apply(options.params || {}, this.baseParams);
8865             // if meta was not loaded from remote source.. try requesting it.
8866             if (!this.reader.metaFromRemote) {
8867                 p._requestMeta = 1;
8868             }
8869             if(this.sortInfo && this.remoteSort){
8870                 var pn = this.paramNames;
8871                 p[pn["sort"]] = this.sortInfo.field;
8872                 p[pn["dir"]] = this.sortInfo.direction;
8873             }
8874             if (this.multiSort) {
8875                 var pn = this.paramNames;
8876                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8877             }
8878             
8879             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8880         }
8881     },
8882
8883     /**
8884      * Reloads the Record cache from the configured Proxy using the configured Reader and
8885      * the options from the last load operation performed.
8886      * @param {Object} options (optional) An object containing properties which may override the options
8887      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8888      * the most recently used options are reused).
8889      */
8890     reload : function(options){
8891         this.load(Roo.applyIf(options||{}, this.lastOptions));
8892     },
8893
8894     // private
8895     // Called as a callback by the Reader during a load operation.
8896     loadRecords : function(o, options, success){
8897         if(!o || success === false){
8898             if(success !== false){
8899                 this.fireEvent("load", this, [], options, o);
8900             }
8901             if(options.callback){
8902                 options.callback.call(options.scope || this, [], options, false);
8903             }
8904             return;
8905         }
8906         // if data returned failure - throw an exception.
8907         if (o.success === false) {
8908             // show a message if no listener is registered.
8909             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8910                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8911             }
8912             // loadmask wil be hooked into this..
8913             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8914             return;
8915         }
8916         var r = o.records, t = o.totalRecords || r.length;
8917         
8918         this.fireEvent("beforeloadadd", this, r, options, o);
8919         
8920         if(!options || options.add !== true){
8921             if(this.pruneModifiedRecords){
8922                 this.modified = [];
8923             }
8924             for(var i = 0, len = r.length; i < len; i++){
8925                 r[i].join(this);
8926             }
8927             if(this.snapshot){
8928                 this.data = this.snapshot;
8929                 delete this.snapshot;
8930             }
8931             this.data.clear();
8932             this.data.addAll(r);
8933             this.totalLength = t;
8934             this.applySort();
8935             this.fireEvent("datachanged", this);
8936         }else{
8937             this.totalLength = Math.max(t, this.data.length+r.length);
8938             this.add(r);
8939         }
8940         this.fireEvent("load", this, r, options, o);
8941         if(options.callback){
8942             options.callback.call(options.scope || this, r, options, true);
8943         }
8944     },
8945
8946
8947     /**
8948      * Loads data from a passed data block. A Reader which understands the format of the data
8949      * must have been configured in the constructor.
8950      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8951      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8952      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8953      */
8954     loadData : function(o, append){
8955         var r = this.reader.readRecords(o);
8956         this.loadRecords(r, {add: append}, true);
8957     },
8958
8959     /**
8960      * Gets the number of cached records.
8961      * <p>
8962      * <em>If using paging, this may not be the total size of the dataset. If the data object
8963      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8964      * the data set size</em>
8965      */
8966     getCount : function(){
8967         return this.data.length || 0;
8968     },
8969
8970     /**
8971      * Gets the total number of records in the dataset as returned by the server.
8972      * <p>
8973      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8974      * the dataset size</em>
8975      */
8976     getTotalCount : function(){
8977         return this.totalLength || 0;
8978     },
8979
8980     /**
8981      * Returns the sort state of the Store as an object with two properties:
8982      * <pre><code>
8983  field {String} The name of the field by which the Records are sorted
8984  direction {String} The sort order, "ASC" or "DESC"
8985      * </code></pre>
8986      */
8987     getSortState : function(){
8988         return this.sortInfo;
8989     },
8990
8991     // private
8992     applySort : function(){
8993         if(this.sortInfo && !this.remoteSort){
8994             var s = this.sortInfo, f = s.field;
8995             var st = this.fields.get(f).sortType;
8996             var fn = function(r1, r2){
8997                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8998                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8999             };
9000             this.data.sort(s.direction, fn);
9001             if(this.snapshot && this.snapshot != this.data){
9002                 this.snapshot.sort(s.direction, fn);
9003             }
9004         }
9005     },
9006
9007     /**
9008      * Sets the default sort column and order to be used by the next load operation.
9009      * @param {String} fieldName The name of the field to sort by.
9010      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9011      */
9012     setDefaultSort : function(field, dir){
9013         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9014     },
9015
9016     /**
9017      * Sort the Records.
9018      * If remote sorting is used, the sort is performed on the server, and the cache is
9019      * reloaded. If local sorting is used, the cache is sorted internally.
9020      * @param {String} fieldName The name of the field to sort by.
9021      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9022      */
9023     sort : function(fieldName, dir){
9024         var f = this.fields.get(fieldName);
9025         if(!dir){
9026             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9027             
9028             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9029                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9030             }else{
9031                 dir = f.sortDir;
9032             }
9033         }
9034         this.sortToggle[f.name] = dir;
9035         this.sortInfo = {field: f.name, direction: dir};
9036         if(!this.remoteSort){
9037             this.applySort();
9038             this.fireEvent("datachanged", this);
9039         }else{
9040             this.load(this.lastOptions);
9041         }
9042     },
9043
9044     /**
9045      * Calls the specified function for each of the Records in the cache.
9046      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9047      * Returning <em>false</em> aborts and exits the iteration.
9048      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9049      */
9050     each : function(fn, scope){
9051         this.data.each(fn, scope);
9052     },
9053
9054     /**
9055      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9056      * (e.g., during paging).
9057      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9058      */
9059     getModifiedRecords : function(){
9060         return this.modified;
9061     },
9062
9063     // private
9064     createFilterFn : function(property, value, anyMatch){
9065         if(!value.exec){ // not a regex
9066             value = String(value);
9067             if(value.length == 0){
9068                 return false;
9069             }
9070             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9071         }
9072         return function(r){
9073             return value.test(r.data[property]);
9074         };
9075     },
9076
9077     /**
9078      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9079      * @param {String} property A field on your records
9080      * @param {Number} start The record index to start at (defaults to 0)
9081      * @param {Number} end The last record index to include (defaults to length - 1)
9082      * @return {Number} The sum
9083      */
9084     sum : function(property, start, end){
9085         var rs = this.data.items, v = 0;
9086         start = start || 0;
9087         end = (end || end === 0) ? end : rs.length-1;
9088
9089         for(var i = start; i <= end; i++){
9090             v += (rs[i].data[property] || 0);
9091         }
9092         return v;
9093     },
9094
9095     /**
9096      * Filter the records by a specified property.
9097      * @param {String} field A field on your records
9098      * @param {String/RegExp} value Either a string that the field
9099      * should start with or a RegExp to test against the field
9100      * @param {Boolean} anyMatch True to match any part not just the beginning
9101      */
9102     filter : function(property, value, anyMatch){
9103         var fn = this.createFilterFn(property, value, anyMatch);
9104         return fn ? this.filterBy(fn) : this.clearFilter();
9105     },
9106
9107     /**
9108      * Filter by a function. The specified function will be called with each
9109      * record in this data source. If the function returns true the record is included,
9110      * otherwise it is filtered.
9111      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9112      * @param {Object} scope (optional) The scope of the function (defaults to this)
9113      */
9114     filterBy : function(fn, scope){
9115         this.snapshot = this.snapshot || this.data;
9116         this.data = this.queryBy(fn, scope||this);
9117         this.fireEvent("datachanged", this);
9118     },
9119
9120     /**
9121      * Query the records by a specified property.
9122      * @param {String} field A field on your records
9123      * @param {String/RegExp} value Either a string that the field
9124      * should start with or a RegExp to test against the field
9125      * @param {Boolean} anyMatch True to match any part not just the beginning
9126      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9127      */
9128     query : function(property, value, anyMatch){
9129         var fn = this.createFilterFn(property, value, anyMatch);
9130         return fn ? this.queryBy(fn) : this.data.clone();
9131     },
9132
9133     /**
9134      * Query by a function. The specified function will be called with each
9135      * record in this data source. If the function returns true the record is included
9136      * in the results.
9137      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9138      * @param {Object} scope (optional) The scope of the function (defaults to this)
9139       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9140      **/
9141     queryBy : function(fn, scope){
9142         var data = this.snapshot || this.data;
9143         return data.filterBy(fn, scope||this);
9144     },
9145
9146     /**
9147      * Collects unique values for a particular dataIndex from this store.
9148      * @param {String} dataIndex The property to collect
9149      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9150      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9151      * @return {Array} An array of the unique values
9152      **/
9153     collect : function(dataIndex, allowNull, bypassFilter){
9154         var d = (bypassFilter === true && this.snapshot) ?
9155                 this.snapshot.items : this.data.items;
9156         var v, sv, r = [], l = {};
9157         for(var i = 0, len = d.length; i < len; i++){
9158             v = d[i].data[dataIndex];
9159             sv = String(v);
9160             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9161                 l[sv] = true;
9162                 r[r.length] = v;
9163             }
9164         }
9165         return r;
9166     },
9167
9168     /**
9169      * Revert to a view of the Record cache with no filtering applied.
9170      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9171      */
9172     clearFilter : function(suppressEvent){
9173         if(this.snapshot && this.snapshot != this.data){
9174             this.data = this.snapshot;
9175             delete this.snapshot;
9176             if(suppressEvent !== true){
9177                 this.fireEvent("datachanged", this);
9178             }
9179         }
9180     },
9181
9182     // private
9183     afterEdit : function(record){
9184         if(this.modified.indexOf(record) == -1){
9185             this.modified.push(record);
9186         }
9187         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9188     },
9189     
9190     // private
9191     afterReject : function(record){
9192         this.modified.remove(record);
9193         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9194     },
9195
9196     // private
9197     afterCommit : function(record){
9198         this.modified.remove(record);
9199         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9200     },
9201
9202     /**
9203      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9204      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9205      */
9206     commitChanges : function(){
9207         var m = this.modified.slice(0);
9208         this.modified = [];
9209         for(var i = 0, len = m.length; i < len; i++){
9210             m[i].commit();
9211         }
9212     },
9213
9214     /**
9215      * Cancel outstanding changes on all changed records.
9216      */
9217     rejectChanges : function(){
9218         var m = this.modified.slice(0);
9219         this.modified = [];
9220         for(var i = 0, len = m.length; i < len; i++){
9221             m[i].reject();
9222         }
9223     },
9224
9225     onMetaChange : function(meta, rtype, o){
9226         this.recordType = rtype;
9227         this.fields = rtype.prototype.fields;
9228         delete this.snapshot;
9229         this.sortInfo = meta.sortInfo || this.sortInfo;
9230         this.modified = [];
9231         this.fireEvent('metachange', this, this.reader.meta);
9232     },
9233     
9234     moveIndex : function(data, type)
9235     {
9236         var index = this.indexOf(data);
9237         
9238         var newIndex = index + type;
9239         
9240         this.remove(data);
9241         
9242         this.insert(newIndex, data);
9243         
9244     }
9245 });/*
9246  * Based on:
9247  * Ext JS Library 1.1.1
9248  * Copyright(c) 2006-2007, Ext JS, LLC.
9249  *
9250  * Originally Released Under LGPL - original licence link has changed is not relivant.
9251  *
9252  * Fork - LGPL
9253  * <script type="text/javascript">
9254  */
9255
9256 /**
9257  * @class Roo.data.SimpleStore
9258  * @extends Roo.data.Store
9259  * Small helper class to make creating Stores from Array data easier.
9260  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9261  * @cfg {Array} fields An array of field definition objects, or field name strings.
9262  * @cfg {Array} data The multi-dimensional array of data
9263  * @constructor
9264  * @param {Object} config
9265  */
9266 Roo.data.SimpleStore = function(config){
9267     Roo.data.SimpleStore.superclass.constructor.call(this, {
9268         isLocal : true,
9269         reader: new Roo.data.ArrayReader({
9270                 id: config.id
9271             },
9272             Roo.data.Record.create(config.fields)
9273         ),
9274         proxy : new Roo.data.MemoryProxy(config.data)
9275     });
9276     this.load();
9277 };
9278 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9279  * Based on:
9280  * Ext JS Library 1.1.1
9281  * Copyright(c) 2006-2007, Ext JS, LLC.
9282  *
9283  * Originally Released Under LGPL - original licence link has changed is not relivant.
9284  *
9285  * Fork - LGPL
9286  * <script type="text/javascript">
9287  */
9288
9289 /**
9290 /**
9291  * @extends Roo.data.Store
9292  * @class Roo.data.JsonStore
9293  * Small helper class to make creating Stores for JSON data easier. <br/>
9294 <pre><code>
9295 var store = new Roo.data.JsonStore({
9296     url: 'get-images.php',
9297     root: 'images',
9298     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9299 });
9300 </code></pre>
9301  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9302  * JsonReader and HttpProxy (unless inline data is provided).</b>
9303  * @cfg {Array} fields An array of field definition objects, or field name strings.
9304  * @constructor
9305  * @param {Object} config
9306  */
9307 Roo.data.JsonStore = function(c){
9308     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9309         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9310         reader: new Roo.data.JsonReader(c, c.fields)
9311     }));
9312 };
9313 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9314  * Based on:
9315  * Ext JS Library 1.1.1
9316  * Copyright(c) 2006-2007, Ext JS, LLC.
9317  *
9318  * Originally Released Under LGPL - original licence link has changed is not relivant.
9319  *
9320  * Fork - LGPL
9321  * <script type="text/javascript">
9322  */
9323
9324  
9325 Roo.data.Field = function(config){
9326     if(typeof config == "string"){
9327         config = {name: config};
9328     }
9329     Roo.apply(this, config);
9330     
9331     if(!this.type){
9332         this.type = "auto";
9333     }
9334     
9335     var st = Roo.data.SortTypes;
9336     // named sortTypes are supported, here we look them up
9337     if(typeof this.sortType == "string"){
9338         this.sortType = st[this.sortType];
9339     }
9340     
9341     // set default sortType for strings and dates
9342     if(!this.sortType){
9343         switch(this.type){
9344             case "string":
9345                 this.sortType = st.asUCString;
9346                 break;
9347             case "date":
9348                 this.sortType = st.asDate;
9349                 break;
9350             default:
9351                 this.sortType = st.none;
9352         }
9353     }
9354
9355     // define once
9356     var stripRe = /[\$,%]/g;
9357
9358     // prebuilt conversion function for this field, instead of
9359     // switching every time we're reading a value
9360     if(!this.convert){
9361         var cv, dateFormat = this.dateFormat;
9362         switch(this.type){
9363             case "":
9364             case "auto":
9365             case undefined:
9366                 cv = function(v){ return v; };
9367                 break;
9368             case "string":
9369                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9370                 break;
9371             case "int":
9372                 cv = function(v){
9373                     return v !== undefined && v !== null && v !== '' ?
9374                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9375                     };
9376                 break;
9377             case "float":
9378                 cv = function(v){
9379                     return v !== undefined && v !== null && v !== '' ?
9380                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9381                     };
9382                 break;
9383             case "bool":
9384             case "boolean":
9385                 cv = function(v){ return v === true || v === "true" || v == 1; };
9386                 break;
9387             case "date":
9388                 cv = function(v){
9389                     if(!v){
9390                         return '';
9391                     }
9392                     if(v instanceof Date){
9393                         return v;
9394                     }
9395                     if(dateFormat){
9396                         if(dateFormat == "timestamp"){
9397                             return new Date(v*1000);
9398                         }
9399                         return Date.parseDate(v, dateFormat);
9400                     }
9401                     var parsed = Date.parse(v);
9402                     return parsed ? new Date(parsed) : null;
9403                 };
9404              break;
9405             
9406         }
9407         this.convert = cv;
9408     }
9409 };
9410
9411 Roo.data.Field.prototype = {
9412     dateFormat: null,
9413     defaultValue: "",
9414     mapping: null,
9415     sortType : null,
9416     sortDir : "ASC"
9417 };/*
9418  * Based on:
9419  * Ext JS Library 1.1.1
9420  * Copyright(c) 2006-2007, Ext JS, LLC.
9421  *
9422  * Originally Released Under LGPL - original licence link has changed is not relivant.
9423  *
9424  * Fork - LGPL
9425  * <script type="text/javascript">
9426  */
9427  
9428 // Base class for reading structured data from a data source.  This class is intended to be
9429 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9430
9431 /**
9432  * @class Roo.data.DataReader
9433  * Base class for reading structured data from a data source.  This class is intended to be
9434  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9435  */
9436
9437 Roo.data.DataReader = function(meta, recordType){
9438     
9439     this.meta = meta;
9440     
9441     this.recordType = recordType instanceof Array ? 
9442         Roo.data.Record.create(recordType) : recordType;
9443 };
9444
9445 Roo.data.DataReader.prototype = {
9446      /**
9447      * Create an empty record
9448      * @param {Object} data (optional) - overlay some values
9449      * @return {Roo.data.Record} record created.
9450      */
9451     newRow :  function(d) {
9452         var da =  {};
9453         this.recordType.prototype.fields.each(function(c) {
9454             switch( c.type) {
9455                 case 'int' : da[c.name] = 0; break;
9456                 case 'date' : da[c.name] = new Date(); break;
9457                 case 'float' : da[c.name] = 0.0; break;
9458                 case 'boolean' : da[c.name] = false; break;
9459                 default : da[c.name] = ""; break;
9460             }
9461             
9462         });
9463         return new this.recordType(Roo.apply(da, d));
9464     }
9465     
9466 };/*
9467  * Based on:
9468  * Ext JS Library 1.1.1
9469  * Copyright(c) 2006-2007, Ext JS, LLC.
9470  *
9471  * Originally Released Under LGPL - original licence link has changed is not relivant.
9472  *
9473  * Fork - LGPL
9474  * <script type="text/javascript">
9475  */
9476
9477 /**
9478  * @class Roo.data.DataProxy
9479  * @extends Roo.data.Observable
9480  * This class is an abstract base class for implementations which provide retrieval of
9481  * unformatted data objects.<br>
9482  * <p>
9483  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9484  * (of the appropriate type which knows how to parse the data object) to provide a block of
9485  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9486  * <p>
9487  * Custom implementations must implement the load method as described in
9488  * {@link Roo.data.HttpProxy#load}.
9489  */
9490 Roo.data.DataProxy = function(){
9491     this.addEvents({
9492         /**
9493          * @event beforeload
9494          * Fires before a network request is made to retrieve a data object.
9495          * @param {Object} This DataProxy object.
9496          * @param {Object} params The params parameter to the load function.
9497          */
9498         beforeload : true,
9499         /**
9500          * @event load
9501          * Fires before the load method's callback is called.
9502          * @param {Object} This DataProxy object.
9503          * @param {Object} o The data object.
9504          * @param {Object} arg The callback argument object passed to the load function.
9505          */
9506         load : true,
9507         /**
9508          * @event loadexception
9509          * Fires if an Exception occurs during data retrieval.
9510          * @param {Object} This DataProxy object.
9511          * @param {Object} o The data object.
9512          * @param {Object} arg The callback argument object passed to the load function.
9513          * @param {Object} e The Exception.
9514          */
9515         loadexception : true
9516     });
9517     Roo.data.DataProxy.superclass.constructor.call(this);
9518 };
9519
9520 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9521
9522     /**
9523      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9524      */
9525 /*
9526  * Based on:
9527  * Ext JS Library 1.1.1
9528  * Copyright(c) 2006-2007, Ext JS, LLC.
9529  *
9530  * Originally Released Under LGPL - original licence link has changed is not relivant.
9531  *
9532  * Fork - LGPL
9533  * <script type="text/javascript">
9534  */
9535 /**
9536  * @class Roo.data.MemoryProxy
9537  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9538  * to the Reader when its load method is called.
9539  * @constructor
9540  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9541  */
9542 Roo.data.MemoryProxy = function(data){
9543     if (data.data) {
9544         data = data.data;
9545     }
9546     Roo.data.MemoryProxy.superclass.constructor.call(this);
9547     this.data = data;
9548 };
9549
9550 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9551     /**
9552      * Load data from the requested source (in this case an in-memory
9553      * data object passed to the constructor), read the data object into
9554      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9555      * process that block using the passed callback.
9556      * @param {Object} params This parameter is not used by the MemoryProxy class.
9557      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9558      * object into a block of Roo.data.Records.
9559      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9560      * The function must be passed <ul>
9561      * <li>The Record block object</li>
9562      * <li>The "arg" argument from the load function</li>
9563      * <li>A boolean success indicator</li>
9564      * </ul>
9565      * @param {Object} scope The scope in which to call the callback
9566      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9567      */
9568     load : function(params, reader, callback, scope, arg){
9569         params = params || {};
9570         var result;
9571         try {
9572             result = reader.readRecords(this.data);
9573         }catch(e){
9574             this.fireEvent("loadexception", this, arg, null, e);
9575             callback.call(scope, null, arg, false);
9576             return;
9577         }
9578         callback.call(scope, result, arg, true);
9579     },
9580     
9581     // private
9582     update : function(params, records){
9583         
9584     }
9585 });/*
9586  * Based on:
9587  * Ext JS Library 1.1.1
9588  * Copyright(c) 2006-2007, Ext JS, LLC.
9589  *
9590  * Originally Released Under LGPL - original licence link has changed is not relivant.
9591  *
9592  * Fork - LGPL
9593  * <script type="text/javascript">
9594  */
9595 /**
9596  * @class Roo.data.HttpProxy
9597  * @extends Roo.data.DataProxy
9598  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9599  * configured to reference a certain URL.<br><br>
9600  * <p>
9601  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9602  * from which the running page was served.<br><br>
9603  * <p>
9604  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9605  * <p>
9606  * Be aware that to enable the browser to parse an XML document, the server must set
9607  * the Content-Type header in the HTTP response to "text/xml".
9608  * @constructor
9609  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9610  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9611  * will be used to make the request.
9612  */
9613 Roo.data.HttpProxy = function(conn){
9614     Roo.data.HttpProxy.superclass.constructor.call(this);
9615     // is conn a conn config or a real conn?
9616     this.conn = conn;
9617     this.useAjax = !conn || !conn.events;
9618   
9619 };
9620
9621 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9622     // thse are take from connection...
9623     
9624     /**
9625      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9626      */
9627     /**
9628      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9629      * extra parameters to each request made by this object. (defaults to undefined)
9630      */
9631     /**
9632      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9633      *  to each request made by this object. (defaults to undefined)
9634      */
9635     /**
9636      * @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)
9637      */
9638     /**
9639      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9640      */
9641      /**
9642      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9643      * @type Boolean
9644      */
9645   
9646
9647     /**
9648      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9649      * @type Boolean
9650      */
9651     /**
9652      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9653      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9654      * a finer-grained basis than the DataProxy events.
9655      */
9656     getConnection : function(){
9657         return this.useAjax ? Roo.Ajax : this.conn;
9658     },
9659
9660     /**
9661      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9662      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9663      * process that block using the passed callback.
9664      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9665      * for the request to the remote server.
9666      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9667      * object into a block of Roo.data.Records.
9668      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9669      * The function must be passed <ul>
9670      * <li>The Record block object</li>
9671      * <li>The "arg" argument from the load function</li>
9672      * <li>A boolean success indicator</li>
9673      * </ul>
9674      * @param {Object} scope The scope in which to call the callback
9675      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9676      */
9677     load : function(params, reader, callback, scope, arg){
9678         if(this.fireEvent("beforeload", this, params) !== false){
9679             var  o = {
9680                 params : params || {},
9681                 request: {
9682                     callback : callback,
9683                     scope : scope,
9684                     arg : arg
9685                 },
9686                 reader: reader,
9687                 callback : this.loadResponse,
9688                 scope: this
9689             };
9690             if(this.useAjax){
9691                 Roo.applyIf(o, this.conn);
9692                 if(this.activeRequest){
9693                     Roo.Ajax.abort(this.activeRequest);
9694                 }
9695                 this.activeRequest = Roo.Ajax.request(o);
9696             }else{
9697                 this.conn.request(o);
9698             }
9699         }else{
9700             callback.call(scope||this, null, arg, false);
9701         }
9702     },
9703
9704     // private
9705     loadResponse : function(o, success, response){
9706         delete this.activeRequest;
9707         if(!success){
9708             this.fireEvent("loadexception", this, o, response);
9709             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9710             return;
9711         }
9712         var result;
9713         try {
9714             result = o.reader.read(response);
9715         }catch(e){
9716             this.fireEvent("loadexception", this, o, response, e);
9717             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9718             return;
9719         }
9720         
9721         this.fireEvent("load", this, o, o.request.arg);
9722         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9723     },
9724
9725     // private
9726     update : function(dataSet){
9727
9728     },
9729
9730     // private
9731     updateResponse : function(dataSet){
9732
9733     }
9734 });/*
9735  * Based on:
9736  * Ext JS Library 1.1.1
9737  * Copyright(c) 2006-2007, Ext JS, LLC.
9738  *
9739  * Originally Released Under LGPL - original licence link has changed is not relivant.
9740  *
9741  * Fork - LGPL
9742  * <script type="text/javascript">
9743  */
9744
9745 /**
9746  * @class Roo.data.ScriptTagProxy
9747  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9748  * other than the originating domain of the running page.<br><br>
9749  * <p>
9750  * <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
9751  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9752  * <p>
9753  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9754  * source code that is used as the source inside a &lt;script> tag.<br><br>
9755  * <p>
9756  * In order for the browser to process the returned data, the server must wrap the data object
9757  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9758  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9759  * depending on whether the callback name was passed:
9760  * <p>
9761  * <pre><code>
9762 boolean scriptTag = false;
9763 String cb = request.getParameter("callback");
9764 if (cb != null) {
9765     scriptTag = true;
9766     response.setContentType("text/javascript");
9767 } else {
9768     response.setContentType("application/x-json");
9769 }
9770 Writer out = response.getWriter();
9771 if (scriptTag) {
9772     out.write(cb + "(");
9773 }
9774 out.print(dataBlock.toJsonString());
9775 if (scriptTag) {
9776     out.write(");");
9777 }
9778 </pre></code>
9779  *
9780  * @constructor
9781  * @param {Object} config A configuration object.
9782  */
9783 Roo.data.ScriptTagProxy = function(config){
9784     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9785     Roo.apply(this, config);
9786     this.head = document.getElementsByTagName("head")[0];
9787 };
9788
9789 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9790
9791 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9792     /**
9793      * @cfg {String} url The URL from which to request the data object.
9794      */
9795     /**
9796      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9797      */
9798     timeout : 30000,
9799     /**
9800      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9801      * the server the name of the callback function set up by the load call to process the returned data object.
9802      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9803      * javascript output which calls this named function passing the data object as its only parameter.
9804      */
9805     callbackParam : "callback",
9806     /**
9807      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9808      * name to the request.
9809      */
9810     nocache : true,
9811
9812     /**
9813      * Load data from the configured URL, read the data object into
9814      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9815      * process that block using the passed callback.
9816      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9817      * for the request to the remote server.
9818      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9819      * object into a block of Roo.data.Records.
9820      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9821      * The function must be passed <ul>
9822      * <li>The Record block object</li>
9823      * <li>The "arg" argument from the load function</li>
9824      * <li>A boolean success indicator</li>
9825      * </ul>
9826      * @param {Object} scope The scope in which to call the callback
9827      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9828      */
9829     load : function(params, reader, callback, scope, arg){
9830         if(this.fireEvent("beforeload", this, params) !== false){
9831
9832             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9833
9834             var url = this.url;
9835             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9836             if(this.nocache){
9837                 url += "&_dc=" + (new Date().getTime());
9838             }
9839             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9840             var trans = {
9841                 id : transId,
9842                 cb : "stcCallback"+transId,
9843                 scriptId : "stcScript"+transId,
9844                 params : params,
9845                 arg : arg,
9846                 url : url,
9847                 callback : callback,
9848                 scope : scope,
9849                 reader : reader
9850             };
9851             var conn = this;
9852
9853             window[trans.cb] = function(o){
9854                 conn.handleResponse(o, trans);
9855             };
9856
9857             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9858
9859             if(this.autoAbort !== false){
9860                 this.abort();
9861             }
9862
9863             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9864
9865             var script = document.createElement("script");
9866             script.setAttribute("src", url);
9867             script.setAttribute("type", "text/javascript");
9868             script.setAttribute("id", trans.scriptId);
9869             this.head.appendChild(script);
9870
9871             this.trans = trans;
9872         }else{
9873             callback.call(scope||this, null, arg, false);
9874         }
9875     },
9876
9877     // private
9878     isLoading : function(){
9879         return this.trans ? true : false;
9880     },
9881
9882     /**
9883      * Abort the current server request.
9884      */
9885     abort : function(){
9886         if(this.isLoading()){
9887             this.destroyTrans(this.trans);
9888         }
9889     },
9890
9891     // private
9892     destroyTrans : function(trans, isLoaded){
9893         this.head.removeChild(document.getElementById(trans.scriptId));
9894         clearTimeout(trans.timeoutId);
9895         if(isLoaded){
9896             window[trans.cb] = undefined;
9897             try{
9898                 delete window[trans.cb];
9899             }catch(e){}
9900         }else{
9901             // if hasn't been loaded, wait for load to remove it to prevent script error
9902             window[trans.cb] = function(){
9903                 window[trans.cb] = undefined;
9904                 try{
9905                     delete window[trans.cb];
9906                 }catch(e){}
9907             };
9908         }
9909     },
9910
9911     // private
9912     handleResponse : function(o, trans){
9913         this.trans = false;
9914         this.destroyTrans(trans, true);
9915         var result;
9916         try {
9917             result = trans.reader.readRecords(o);
9918         }catch(e){
9919             this.fireEvent("loadexception", this, o, trans.arg, e);
9920             trans.callback.call(trans.scope||window, null, trans.arg, false);
9921             return;
9922         }
9923         this.fireEvent("load", this, o, trans.arg);
9924         trans.callback.call(trans.scope||window, result, trans.arg, true);
9925     },
9926
9927     // private
9928     handleFailure : function(trans){
9929         this.trans = false;
9930         this.destroyTrans(trans, false);
9931         this.fireEvent("loadexception", this, null, trans.arg);
9932         trans.callback.call(trans.scope||window, null, trans.arg, false);
9933     }
9934 });/*
9935  * Based on:
9936  * Ext JS Library 1.1.1
9937  * Copyright(c) 2006-2007, Ext JS, LLC.
9938  *
9939  * Originally Released Under LGPL - original licence link has changed is not relivant.
9940  *
9941  * Fork - LGPL
9942  * <script type="text/javascript">
9943  */
9944
9945 /**
9946  * @class Roo.data.JsonReader
9947  * @extends Roo.data.DataReader
9948  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9949  * based on mappings in a provided Roo.data.Record constructor.
9950  * 
9951  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9952  * in the reply previously. 
9953  * 
9954  * <p>
9955  * Example code:
9956  * <pre><code>
9957 var RecordDef = Roo.data.Record.create([
9958     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9959     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9960 ]);
9961 var myReader = new Roo.data.JsonReader({
9962     totalProperty: "results",    // The property which contains the total dataset size (optional)
9963     root: "rows",                // The property which contains an Array of row objects
9964     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9965 }, RecordDef);
9966 </code></pre>
9967  * <p>
9968  * This would consume a JSON file like this:
9969  * <pre><code>
9970 { 'results': 2, 'rows': [
9971     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9972     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9973 }
9974 </code></pre>
9975  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9976  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9977  * paged from the remote server.
9978  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9979  * @cfg {String} root name of the property which contains the Array of row objects.
9980  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9981  * @constructor
9982  * Create a new JsonReader
9983  * @param {Object} meta Metadata configuration options
9984  * @param {Object} recordType Either an Array of field definition objects,
9985  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9986  */
9987 Roo.data.JsonReader = function(meta, recordType){
9988     
9989     meta = meta || {};
9990     // set some defaults:
9991     Roo.applyIf(meta, {
9992         totalProperty: 'total',
9993         successProperty : 'success',
9994         root : 'data',
9995         id : 'id'
9996     });
9997     
9998     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9999 };
10000 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10001     
10002     /**
10003      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10004      * Used by Store query builder to append _requestMeta to params.
10005      * 
10006      */
10007     metaFromRemote : false,
10008     /**
10009      * This method is only used by a DataProxy which has retrieved data from a remote server.
10010      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10011      * @return {Object} data A data block which is used by an Roo.data.Store object as
10012      * a cache of Roo.data.Records.
10013      */
10014     read : function(response){
10015         var json = response.responseText;
10016        
10017         var o = /* eval:var:o */ eval("("+json+")");
10018         if(!o) {
10019             throw {message: "JsonReader.read: Json object not found"};
10020         }
10021         
10022         if(o.metaData){
10023             
10024             delete this.ef;
10025             this.metaFromRemote = true;
10026             this.meta = o.metaData;
10027             this.recordType = Roo.data.Record.create(o.metaData.fields);
10028             this.onMetaChange(this.meta, this.recordType, o);
10029         }
10030         return this.readRecords(o);
10031     },
10032
10033     // private function a store will implement
10034     onMetaChange : function(meta, recordType, o){
10035
10036     },
10037
10038     /**
10039          * @ignore
10040          */
10041     simpleAccess: function(obj, subsc) {
10042         return obj[subsc];
10043     },
10044
10045         /**
10046          * @ignore
10047          */
10048     getJsonAccessor: function(){
10049         var re = /[\[\.]/;
10050         return function(expr) {
10051             try {
10052                 return(re.test(expr))
10053                     ? new Function("obj", "return obj." + expr)
10054                     : function(obj){
10055                         return obj[expr];
10056                     };
10057             } catch(e){}
10058             return Roo.emptyFn;
10059         };
10060     }(),
10061
10062     /**
10063      * Create a data block containing Roo.data.Records from an XML document.
10064      * @param {Object} o An object which contains an Array of row objects in the property specified
10065      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10066      * which contains the total size of the dataset.
10067      * @return {Object} data A data block which is used by an Roo.data.Store object as
10068      * a cache of Roo.data.Records.
10069      */
10070     readRecords : function(o){
10071         /**
10072          * After any data loads, the raw JSON data is available for further custom processing.
10073          * @type Object
10074          */
10075         this.o = o;
10076         var s = this.meta, Record = this.recordType,
10077             f = Record.prototype.fields, fi = f.items, fl = f.length;
10078
10079 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10080         if (!this.ef) {
10081             if(s.totalProperty) {
10082                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10083                 }
10084                 if(s.successProperty) {
10085                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10086                 }
10087                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10088                 if (s.id) {
10089                         var g = this.getJsonAccessor(s.id);
10090                         this.getId = function(rec) {
10091                                 var r = g(rec);
10092                                 return (r === undefined || r === "") ? null : r;
10093                         };
10094                 } else {
10095                         this.getId = function(){return null;};
10096                 }
10097             this.ef = [];
10098             for(var jj = 0; jj < fl; jj++){
10099                 f = fi[jj];
10100                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10101                 this.ef[jj] = this.getJsonAccessor(map);
10102             }
10103         }
10104
10105         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10106         if(s.totalProperty){
10107             var vt = parseInt(this.getTotal(o), 10);
10108             if(!isNaN(vt)){
10109                 totalRecords = vt;
10110             }
10111         }
10112         if(s.successProperty){
10113             var vs = this.getSuccess(o);
10114             if(vs === false || vs === 'false'){
10115                 success = false;
10116             }
10117         }
10118         var records = [];
10119             for(var i = 0; i < c; i++){
10120                     var n = root[i];
10121                 var values = {};
10122                 var id = this.getId(n);
10123                 for(var j = 0; j < fl; j++){
10124                     f = fi[j];
10125                 var v = this.ef[j](n);
10126                 if (!f.convert) {
10127                     Roo.log('missing convert for ' + f.name);
10128                     Roo.log(f);
10129                     continue;
10130                 }
10131                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10132                 }
10133                 var record = new Record(values, id);
10134                 record.json = n;
10135                 records[i] = record;
10136             }
10137             return {
10138             raw : o,
10139                 success : success,
10140                 records : records,
10141                 totalRecords : totalRecords
10142             };
10143     }
10144 });/*
10145  * Based on:
10146  * Ext JS Library 1.1.1
10147  * Copyright(c) 2006-2007, Ext JS, LLC.
10148  *
10149  * Originally Released Under LGPL - original licence link has changed is not relivant.
10150  *
10151  * Fork - LGPL
10152  * <script type="text/javascript">
10153  */
10154
10155 /**
10156  * @class Roo.data.ArrayReader
10157  * @extends Roo.data.DataReader
10158  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10159  * Each element of that Array represents a row of data fields. The
10160  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10161  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10162  * <p>
10163  * Example code:.
10164  * <pre><code>
10165 var RecordDef = Roo.data.Record.create([
10166     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10167     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10168 ]);
10169 var myReader = new Roo.data.ArrayReader({
10170     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10171 }, RecordDef);
10172 </code></pre>
10173  * <p>
10174  * This would consume an Array like this:
10175  * <pre><code>
10176 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10177   </code></pre>
10178  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10179  * @constructor
10180  * Create a new JsonReader
10181  * @param {Object} meta Metadata configuration options.
10182  * @param {Object} recordType Either an Array of field definition objects
10183  * as specified to {@link Roo.data.Record#create},
10184  * or an {@link Roo.data.Record} object
10185  * created using {@link Roo.data.Record#create}.
10186  */
10187 Roo.data.ArrayReader = function(meta, recordType){
10188     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10189 };
10190
10191 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10192     /**
10193      * Create a data block containing Roo.data.Records from an XML document.
10194      * @param {Object} o An Array of row objects which represents the dataset.
10195      * @return {Object} data A data block which is used by an Roo.data.Store object as
10196      * a cache of Roo.data.Records.
10197      */
10198     readRecords : function(o){
10199         var sid = this.meta ? this.meta.id : null;
10200         var recordType = this.recordType, fields = recordType.prototype.fields;
10201         var records = [];
10202         var root = o;
10203             for(var i = 0; i < root.length; i++){
10204                     var n = root[i];
10205                 var values = {};
10206                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10207                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10208                 var f = fields.items[j];
10209                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10210                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10211                 v = f.convert(v);
10212                 values[f.name] = v;
10213             }
10214                 var record = new recordType(values, id);
10215                 record.json = n;
10216                 records[records.length] = record;
10217             }
10218             return {
10219                 records : records,
10220                 totalRecords : records.length
10221             };
10222     }
10223 });/*
10224  * - LGPL
10225  * * 
10226  */
10227
10228 /**
10229  * @class Roo.bootstrap.ComboBox
10230  * @extends Roo.bootstrap.TriggerField
10231  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10232  * @cfg {Boolean} append (true|false) default false
10233  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10234  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10235  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10236  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10237  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10238  * @constructor
10239  * Create a new ComboBox.
10240  * @param {Object} config Configuration options
10241  */
10242 Roo.bootstrap.ComboBox = function(config){
10243     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10244     this.addEvents({
10245         /**
10246          * @event expand
10247          * Fires when the dropdown list is expanded
10248              * @param {Roo.bootstrap.ComboBox} combo This combo box
10249              */
10250         'expand' : true,
10251         /**
10252          * @event collapse
10253          * Fires when the dropdown list is collapsed
10254              * @param {Roo.bootstrap.ComboBox} combo This combo box
10255              */
10256         'collapse' : true,
10257         /**
10258          * @event beforeselect
10259          * Fires before a list item is selected. Return false to cancel the selection.
10260              * @param {Roo.bootstrap.ComboBox} combo This combo box
10261              * @param {Roo.data.Record} record The data record returned from the underlying store
10262              * @param {Number} index The index of the selected item in the dropdown list
10263              */
10264         'beforeselect' : true,
10265         /**
10266          * @event select
10267          * Fires when a list item is selected
10268              * @param {Roo.bootstrap.ComboBox} combo This combo box
10269              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10270              * @param {Number} index The index of the selected item in the dropdown list
10271              */
10272         'select' : true,
10273         /**
10274          * @event beforequery
10275          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10276          * The event object passed has these properties:
10277              * @param {Roo.bootstrap.ComboBox} combo This combo box
10278              * @param {String} query The query
10279              * @param {Boolean} forceAll true to force "all" query
10280              * @param {Boolean} cancel true to cancel the query
10281              * @param {Object} e The query event object
10282              */
10283         'beforequery': true,
10284          /**
10285          * @event add
10286          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10287              * @param {Roo.bootstrap.ComboBox} combo This combo box
10288              */
10289         'add' : true,
10290         /**
10291          * @event edit
10292          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10293              * @param {Roo.bootstrap.ComboBox} combo This combo box
10294              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10295              */
10296         'edit' : true,
10297         /**
10298          * @event remove
10299          * Fires when the remove value from the combobox array
10300              * @param {Roo.bootstrap.ComboBox} combo This combo box
10301              */
10302         'remove' : true
10303         
10304     });
10305     
10306     this.item = [];
10307     this.tickItems = [];
10308     
10309     this.selectedIndex = -1;
10310     if(this.mode == 'local'){
10311         if(config.queryDelay === undefined){
10312             this.queryDelay = 10;
10313         }
10314         if(config.minChars === undefined){
10315             this.minChars = 0;
10316         }
10317     }
10318 };
10319
10320 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10321      
10322     /**
10323      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10324      * rendering into an Roo.Editor, defaults to false)
10325      */
10326     /**
10327      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10328      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10329      */
10330     /**
10331      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10332      */
10333     /**
10334      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10335      * the dropdown list (defaults to undefined, with no header element)
10336      */
10337
10338      /**
10339      * @cfg {String/Roo.Template} tpl The template to use to render the output
10340      */
10341      
10342      /**
10343      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10344      */
10345     listWidth: undefined,
10346     /**
10347      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10348      * mode = 'remote' or 'text' if mode = 'local')
10349      */
10350     displayField: undefined,
10351     /**
10352      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10353      * mode = 'remote' or 'value' if mode = 'local'). 
10354      * Note: use of a valueField requires the user make a selection
10355      * in order for a value to be mapped.
10356      */
10357     valueField: undefined,
10358     
10359     
10360     /**
10361      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10362      * field's data value (defaults to the underlying DOM element's name)
10363      */
10364     hiddenName: undefined,
10365     /**
10366      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10367      */
10368     listClass: '',
10369     /**
10370      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10371      */
10372     selectedClass: 'active',
10373     
10374     /**
10375      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10376      */
10377     shadow:'sides',
10378     /**
10379      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10380      * anchor positions (defaults to 'tl-bl')
10381      */
10382     listAlign: 'tl-bl?',
10383     /**
10384      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10385      */
10386     maxHeight: 300,
10387     /**
10388      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10389      * query specified by the allQuery config option (defaults to 'query')
10390      */
10391     triggerAction: 'query',
10392     /**
10393      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10394      * (defaults to 4, does not apply if editable = false)
10395      */
10396     minChars : 4,
10397     /**
10398      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10399      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10400      */
10401     typeAhead: false,
10402     /**
10403      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10404      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10405      */
10406     queryDelay: 500,
10407     /**
10408      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10409      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10410      */
10411     pageSize: 0,
10412     /**
10413      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10414      * when editable = true (defaults to false)
10415      */
10416     selectOnFocus:false,
10417     /**
10418      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10419      */
10420     queryParam: 'query',
10421     /**
10422      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10423      * when mode = 'remote' (defaults to 'Loading...')
10424      */
10425     loadingText: 'Loading...',
10426     /**
10427      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10428      */
10429     resizable: false,
10430     /**
10431      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10432      */
10433     handleHeight : 8,
10434     /**
10435      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10436      * traditional select (defaults to true)
10437      */
10438     editable: true,
10439     /**
10440      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10441      */
10442     allQuery: '',
10443     /**
10444      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10445      */
10446     mode: 'remote',
10447     /**
10448      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10449      * listWidth has a higher value)
10450      */
10451     minListWidth : 70,
10452     /**
10453      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10454      * allow the user to set arbitrary text into the field (defaults to false)
10455      */
10456     forceSelection:false,
10457     /**
10458      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10459      * if typeAhead = true (defaults to 250)
10460      */
10461     typeAheadDelay : 250,
10462     /**
10463      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10464      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10465      */
10466     valueNotFoundText : undefined,
10467     /**
10468      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10469      */
10470     blockFocus : false,
10471     
10472     /**
10473      * @cfg {Boolean} disableClear Disable showing of clear button.
10474      */
10475     disableClear : false,
10476     /**
10477      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10478      */
10479     alwaysQuery : false,
10480     
10481     /**
10482      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10483      */
10484     multiple : false,
10485     
10486     //private
10487     addicon : false,
10488     editicon: false,
10489     
10490     page: 0,
10491     hasQuery: false,
10492     append: false,
10493     loadNext: false,
10494     autoFocus : true,
10495     tickable : false,
10496     btnPosition : 'right',
10497     triggerList : true,
10498     showToggleBtn : true,
10499     // element that contains real text value.. (when hidden is used..)
10500     
10501     getAutoCreate : function()
10502     {
10503         var cfg = false;
10504         
10505         /*
10506          *  Normal ComboBox
10507          */
10508         if(!this.tickable){
10509             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10510             return cfg;
10511         }
10512         
10513         /*
10514          *  ComboBox with tickable selections
10515          */
10516              
10517         var align = this.labelAlign || this.parentLabelAlign();
10518         
10519         cfg = {
10520             cls : 'form-group roo-combobox-tickable' //input-group
10521         };
10522         
10523         
10524         var buttons = {
10525             tag : 'div',
10526             cls : 'tickable-buttons',
10527             cn : [
10528                 {
10529                     tag : 'button',
10530                     type : 'button',
10531                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10532                     html : 'Edit'
10533                 },
10534                 {
10535                     tag : 'button',
10536                     type : 'button',
10537                     name : 'ok',
10538                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10539                     html : 'Done'
10540                 },
10541                 {
10542                     tag : 'button',
10543                     type : 'button',
10544                     name : 'cancel',
10545                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10546                     html : 'Cancel'
10547                 }
10548             ]
10549         };
10550         
10551         var _this = this;
10552         Roo.each(buttons.cn, function(c){
10553             if (_this.size) {
10554                 c.cls += ' btn-' + _this.size;
10555             }
10556
10557             if (_this.disabled) {
10558                 c.disabled = true;
10559             }
10560         });
10561         
10562         var box = {
10563             tag: 'div',
10564             cn: [
10565                 {
10566                     tag: 'input',
10567                     type : 'hidden',
10568                     cls: 'form-hidden-field'
10569                 },
10570                 {
10571                     tag: 'ul',
10572                     cls: 'select2-choices',
10573                     cn:[
10574                         {
10575                             tag: 'li',
10576                             cls: 'select2-search-field',
10577                             cn: [
10578
10579                                 buttons
10580                             ]
10581                         }
10582                     ]
10583                 }
10584             ]
10585         }
10586         
10587         var combobox = {
10588             cls: 'select2-container input-group select2-container-multi',
10589             cn: [
10590                 box
10591 //                {
10592 //                    tag: 'ul',
10593 //                    cls: 'typeahead typeahead-long dropdown-menu',
10594 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10595 //                }
10596             ]
10597         };
10598         
10599         if (align ==='left' && this.fieldLabel.length) {
10600             
10601                 Roo.log("left and has label");
10602                 cfg.cn = [
10603                     
10604                     {
10605                         tag: 'label',
10606                         'for' :  id,
10607                         cls : 'control-label col-sm-' + this.labelWidth,
10608                         html : this.fieldLabel
10609                         
10610                     },
10611                     {
10612                         cls : "col-sm-" + (12 - this.labelWidth), 
10613                         cn: [
10614                             combobox
10615                         ]
10616                     }
10617                     
10618                 ];
10619         } else if ( this.fieldLabel.length) {
10620                 Roo.log(" label");
10621                  cfg.cn = [
10622                    
10623                     {
10624                         tag: 'label',
10625                         //cls : 'input-group-addon',
10626                         html : this.fieldLabel
10627                         
10628                     },
10629                     
10630                     combobox
10631                     
10632                 ];
10633
10634         } else {
10635             
10636                 Roo.log(" no label && no align");
10637                 cfg = combobox
10638                      
10639                 
10640         }
10641          
10642         var settings=this;
10643         ['xs','sm','md','lg'].map(function(size){
10644             if (settings[size]) {
10645                 cfg.cls += ' col-' + size + '-' + settings[size];
10646             }
10647         });
10648         
10649         return cfg;
10650         
10651     },
10652     
10653     // private
10654     initEvents: function()
10655     {
10656         
10657         if (!this.store) {
10658             throw "can not find store for combo";
10659         }
10660         this.store = Roo.factory(this.store, Roo.data);
10661         
10662         if(this.tickable){
10663             this.initTickableEvents();
10664             return;
10665         }
10666         
10667         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10668         
10669         if(this.hiddenName){
10670             
10671             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10672             
10673             this.hiddenField.dom.value =
10674                 this.hiddenValue !== undefined ? this.hiddenValue :
10675                 this.value !== undefined ? this.value : '';
10676
10677             // prevent input submission
10678             this.el.dom.removeAttribute('name');
10679             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10680              
10681              
10682         }
10683         //if(Roo.isGecko){
10684         //    this.el.dom.setAttribute('autocomplete', 'off');
10685         //}
10686         
10687         var cls = 'x-combo-list';
10688         
10689         //this.list = new Roo.Layer({
10690         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10691         //});
10692         
10693         var _this = this;
10694         
10695         (function(){
10696             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10697             _this.list.setWidth(lw);
10698         }).defer(100);
10699         
10700         this.list.on('mouseover', this.onViewOver, this);
10701         this.list.on('mousemove', this.onViewMove, this);
10702         
10703         this.list.on('scroll', this.onViewScroll, this);
10704         
10705         /*
10706         this.list.swallowEvent('mousewheel');
10707         this.assetHeight = 0;
10708
10709         if(this.title){
10710             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10711             this.assetHeight += this.header.getHeight();
10712         }
10713
10714         this.innerList = this.list.createChild({cls:cls+'-inner'});
10715         this.innerList.on('mouseover', this.onViewOver, this);
10716         this.innerList.on('mousemove', this.onViewMove, this);
10717         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10718         
10719         if(this.allowBlank && !this.pageSize && !this.disableClear){
10720             this.footer = this.list.createChild({cls:cls+'-ft'});
10721             this.pageTb = new Roo.Toolbar(this.footer);
10722            
10723         }
10724         if(this.pageSize){
10725             this.footer = this.list.createChild({cls:cls+'-ft'});
10726             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10727                     {pageSize: this.pageSize});
10728             
10729         }
10730         
10731         if (this.pageTb && this.allowBlank && !this.disableClear) {
10732             var _this = this;
10733             this.pageTb.add(new Roo.Toolbar.Fill(), {
10734                 cls: 'x-btn-icon x-btn-clear',
10735                 text: '&#160;',
10736                 handler: function()
10737                 {
10738                     _this.collapse();
10739                     _this.clearValue();
10740                     _this.onSelect(false, -1);
10741                 }
10742             });
10743         }
10744         if (this.footer) {
10745             this.assetHeight += this.footer.getHeight();
10746         }
10747         */
10748             
10749         if(!this.tpl){
10750             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10751         }
10752
10753         this.view = new Roo.View(this.list, this.tpl, {
10754             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10755         });
10756         //this.view.wrapEl.setDisplayed(false);
10757         this.view.on('click', this.onViewClick, this);
10758         
10759         
10760         
10761         this.store.on('beforeload', this.onBeforeLoad, this);
10762         this.store.on('load', this.onLoad, this);
10763         this.store.on('loadexception', this.onLoadException, this);
10764         /*
10765         if(this.resizable){
10766             this.resizer = new Roo.Resizable(this.list,  {
10767                pinned:true, handles:'se'
10768             });
10769             this.resizer.on('resize', function(r, w, h){
10770                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10771                 this.listWidth = w;
10772                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10773                 this.restrictHeight();
10774             }, this);
10775             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10776         }
10777         */
10778         if(!this.editable){
10779             this.editable = true;
10780             this.setEditable(false);
10781         }
10782         
10783         /*
10784         
10785         if (typeof(this.events.add.listeners) != 'undefined') {
10786             
10787             this.addicon = this.wrap.createChild(
10788                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10789        
10790             this.addicon.on('click', function(e) {
10791                 this.fireEvent('add', this);
10792             }, this);
10793         }
10794         if (typeof(this.events.edit.listeners) != 'undefined') {
10795             
10796             this.editicon = this.wrap.createChild(
10797                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10798             if (this.addicon) {
10799                 this.editicon.setStyle('margin-left', '40px');
10800             }
10801             this.editicon.on('click', function(e) {
10802                 
10803                 // we fire even  if inothing is selected..
10804                 this.fireEvent('edit', this, this.lastData );
10805                 
10806             }, this);
10807         }
10808         */
10809         
10810         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10811             "up" : function(e){
10812                 this.inKeyMode = true;
10813                 this.selectPrev();
10814             },
10815
10816             "down" : function(e){
10817                 if(!this.isExpanded()){
10818                     this.onTriggerClick();
10819                 }else{
10820                     this.inKeyMode = true;
10821                     this.selectNext();
10822                 }
10823             },
10824
10825             "enter" : function(e){
10826 //                this.onViewClick();
10827                 //return true;
10828                 this.collapse();
10829                 
10830                 if(this.fireEvent("specialkey", this, e)){
10831                     this.onViewClick(false);
10832                 }
10833                 
10834                 return true;
10835             },
10836
10837             "esc" : function(e){
10838                 this.collapse();
10839             },
10840
10841             "tab" : function(e){
10842                 this.collapse();
10843                 
10844                 if(this.fireEvent("specialkey", this, e)){
10845                     this.onViewClick(false);
10846                 }
10847                 
10848                 return true;
10849             },
10850
10851             scope : this,
10852
10853             doRelay : function(foo, bar, hname){
10854                 if(hname == 'down' || this.scope.isExpanded()){
10855                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10856                 }
10857                 return true;
10858             },
10859
10860             forceKeyDown: true
10861         });
10862         
10863         
10864         this.queryDelay = Math.max(this.queryDelay || 10,
10865                 this.mode == 'local' ? 10 : 250);
10866         
10867         
10868         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10869         
10870         if(this.typeAhead){
10871             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10872         }
10873         if(this.editable !== false){
10874             this.inputEl().on("keyup", this.onKeyUp, this);
10875         }
10876         if(this.forceSelection){
10877             this.inputEl().on('blur', this.doForce, this);
10878         }
10879         
10880         if(this.multiple){
10881             this.choices = this.el.select('ul.select2-choices', true).first();
10882             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10883         }
10884     },
10885     
10886     initTickableEvents: function()
10887     {   
10888         this.createList();
10889         
10890         if(this.hiddenName){
10891             
10892             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10893             
10894             this.hiddenField.dom.value =
10895                 this.hiddenValue !== undefined ? this.hiddenValue :
10896                 this.value !== undefined ? this.value : '';
10897
10898             // prevent input submission
10899             this.el.dom.removeAttribute('name');
10900             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10901              
10902              
10903         }
10904         
10905 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10906         
10907         this.choices = this.el.select('ul.select2-choices', true).first();
10908         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10909         if(this.triggerList){
10910             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10911         }
10912          
10913         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10914         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10915         
10916         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10917         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10918         
10919         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10920         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10921         
10922         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10923         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10924         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10925         
10926         this.okBtn.hide();
10927         this.cancelBtn.hide();
10928         
10929         var _this = this;
10930         
10931         (function(){
10932             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10933             _this.list.setWidth(lw);
10934         }).defer(100);
10935         
10936         this.list.on('mouseover', this.onViewOver, this);
10937         this.list.on('mousemove', this.onViewMove, this);
10938         
10939         this.list.on('scroll', this.onViewScroll, this);
10940         
10941         if(!this.tpl){
10942             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>';
10943         }
10944
10945         this.view = new Roo.View(this.list, this.tpl, {
10946             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10947         });
10948         
10949         //this.view.wrapEl.setDisplayed(false);
10950         this.view.on('click', this.onViewClick, this);
10951         
10952         
10953         
10954         this.store.on('beforeload', this.onBeforeLoad, this);
10955         this.store.on('load', this.onLoad, this);
10956         this.store.on('loadexception', this.onLoadException, this);
10957         
10958 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10959 //            "up" : function(e){
10960 //                this.inKeyMode = true;
10961 //                this.selectPrev();
10962 //            },
10963 //
10964 //            "down" : function(e){
10965 //                if(!this.isExpanded()){
10966 //                    this.onTriggerClick();
10967 //                }else{
10968 //                    this.inKeyMode = true;
10969 //                    this.selectNext();
10970 //                }
10971 //            },
10972 //
10973 //            "enter" : function(e){
10974 ////                this.onViewClick();
10975 //                //return true;
10976 //                this.collapse();
10977 //                
10978 //                if(this.fireEvent("specialkey", this, e)){
10979 //                    this.onViewClick(false);
10980 //                }
10981 //                
10982 //                return true;
10983 //            },
10984 //
10985 //            "esc" : function(e){
10986 //                this.collapse();
10987 //            },
10988 //
10989 //            "tab" : function(e){
10990 //                this.collapse();
10991 //                
10992 //                if(this.fireEvent("specialkey", this, e)){
10993 //                    this.onViewClick(false);
10994 //                }
10995 //                
10996 //                return true;
10997 //            },
10998 //
10999 //            scope : this,
11000 //
11001 //            doRelay : function(foo, bar, hname){
11002 //                if(hname == 'down' || this.scope.isExpanded()){
11003 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11004 //                }
11005 //                return true;
11006 //            },
11007 //
11008 //            forceKeyDown: true
11009 //        });
11010         
11011         
11012         this.queryDelay = Math.max(this.queryDelay || 10,
11013                 this.mode == 'local' ? 10 : 250);
11014         
11015         
11016         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11017         
11018         if(this.typeAhead){
11019             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11020         }
11021     },
11022
11023     onDestroy : function(){
11024         if(this.view){
11025             this.view.setStore(null);
11026             this.view.el.removeAllListeners();
11027             this.view.el.remove();
11028             this.view.purgeListeners();
11029         }
11030         if(this.list){
11031             this.list.dom.innerHTML  = '';
11032         }
11033         
11034         if(this.store){
11035             this.store.un('beforeload', this.onBeforeLoad, this);
11036             this.store.un('load', this.onLoad, this);
11037             this.store.un('loadexception', this.onLoadException, this);
11038         }
11039         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11040     },
11041
11042     // private
11043     fireKey : function(e){
11044         if(e.isNavKeyPress() && !this.list.isVisible()){
11045             this.fireEvent("specialkey", this, e);
11046         }
11047     },
11048
11049     // private
11050     onResize: function(w, h){
11051 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11052 //        
11053 //        if(typeof w != 'number'){
11054 //            // we do not handle it!?!?
11055 //            return;
11056 //        }
11057 //        var tw = this.trigger.getWidth();
11058 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11059 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11060 //        var x = w - tw;
11061 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11062 //            
11063 //        //this.trigger.setStyle('left', x+'px');
11064 //        
11065 //        if(this.list && this.listWidth === undefined){
11066 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11067 //            this.list.setWidth(lw);
11068 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11069 //        }
11070         
11071     
11072         
11073     },
11074
11075     /**
11076      * Allow or prevent the user from directly editing the field text.  If false is passed,
11077      * the user will only be able to select from the items defined in the dropdown list.  This method
11078      * is the runtime equivalent of setting the 'editable' config option at config time.
11079      * @param {Boolean} value True to allow the user to directly edit the field text
11080      */
11081     setEditable : function(value){
11082         if(value == this.editable){
11083             return;
11084         }
11085         this.editable = value;
11086         if(!value){
11087             this.inputEl().dom.setAttribute('readOnly', true);
11088             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11089             this.inputEl().addClass('x-combo-noedit');
11090         }else{
11091             this.inputEl().dom.setAttribute('readOnly', false);
11092             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11093             this.inputEl().removeClass('x-combo-noedit');
11094         }
11095     },
11096
11097     // private
11098     
11099     onBeforeLoad : function(combo,opts){
11100         if(!this.hasFocus){
11101             return;
11102         }
11103          if (!opts.add) {
11104             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11105          }
11106 //        this.restrictHeight();
11107         this.selectedIndex = -1;
11108     },
11109
11110     // private
11111     onLoad : function(){
11112         
11113         this.hasQuery = false;
11114         
11115         if(!this.hasFocus){
11116             return;
11117         }
11118         
11119         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11120             this.loading.hide();
11121         }
11122         
11123         if(this.store.getCount() > 0){
11124             this.expand();
11125 //            this.restrictHeight();
11126             if(this.lastQuery == this.allQuery){
11127                 if(this.editable && !this.tickable){
11128                     this.inputEl().dom.select();
11129                 }
11130                 
11131                 if(
11132                     !this.selectByValue(this.value, true) &&
11133                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11134                     this.store.lastOptions.add != true)
11135                 ){
11136                     this.select(0, true);
11137                 }
11138             }else{
11139                 if(this.autoFocus){
11140                     this.selectNext();
11141                 }
11142                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11143                     this.taTask.delay(this.typeAheadDelay);
11144                 }
11145             }
11146         }else{
11147             this.onEmptyResults();
11148         }
11149         
11150         //this.el.focus();
11151     },
11152     // private
11153     onLoadException : function()
11154     {
11155         this.hasQuery = false;
11156         
11157         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11158             this.loading.hide();
11159         }
11160         
11161         this.collapse();
11162         Roo.log(this.store.reader.jsonData);
11163         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11164             // fixme
11165             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11166         }
11167         
11168         
11169     },
11170     // private
11171     onTypeAhead : function(){
11172         if(this.store.getCount() > 0){
11173             var r = this.store.getAt(0);
11174             var newValue = r.data[this.displayField];
11175             var len = newValue.length;
11176             var selStart = this.getRawValue().length;
11177             
11178             if(selStart != len){
11179                 this.setRawValue(newValue);
11180                 this.selectText(selStart, newValue.length);
11181             }
11182         }
11183     },
11184
11185     // private
11186     onSelect : function(record, index){
11187         
11188         if(this.fireEvent('beforeselect', this, record, index) !== false){
11189         
11190             this.setFromData(index > -1 ? record.data : false);
11191             
11192             this.collapse();
11193             this.fireEvent('select', this, record, index);
11194         }
11195     },
11196
11197     /**
11198      * Returns the currently selected field value or empty string if no value is set.
11199      * @return {String} value The selected value
11200      */
11201     getValue : function(){
11202         
11203         if(this.multiple){
11204             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11205         }
11206         
11207         if(this.valueField){
11208             return typeof this.value != 'undefined' ? this.value : '';
11209         }else{
11210             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11211         }
11212     },
11213
11214     /**
11215      * Clears any text/value currently set in the field
11216      */
11217     clearValue : function(){
11218         if(this.hiddenField){
11219             this.hiddenField.dom.value = '';
11220         }
11221         this.value = '';
11222         this.setRawValue('');
11223         this.lastSelectionText = '';
11224         
11225     },
11226
11227     /**
11228      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11229      * will be displayed in the field.  If the value does not match the data value of an existing item,
11230      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11231      * Otherwise the field will be blank (although the value will still be set).
11232      * @param {String} value The value to match
11233      */
11234     setValue : function(v){
11235         if(this.multiple){
11236             this.syncValue();
11237             return;
11238         }
11239         
11240         var text = v;
11241         if(this.valueField){
11242             var r = this.findRecord(this.valueField, v);
11243             if(r){
11244                 text = r.data[this.displayField];
11245             }else if(this.valueNotFoundText !== undefined){
11246                 text = this.valueNotFoundText;
11247             }
11248         }
11249         this.lastSelectionText = text;
11250         if(this.hiddenField){
11251             this.hiddenField.dom.value = v;
11252         }
11253         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11254         this.value = v;
11255     },
11256     /**
11257      * @property {Object} the last set data for the element
11258      */
11259     
11260     lastData : false,
11261     /**
11262      * Sets the value of the field based on a object which is related to the record format for the store.
11263      * @param {Object} value the value to set as. or false on reset?
11264      */
11265     setFromData : function(o){
11266         
11267         if(this.multiple){
11268             if(typeof o.display_name !== 'string'){
11269                 for(var i=0;i<o.display_name.length;i++){
11270                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11271                 }
11272                 return;
11273             }
11274             this.addItem(o);
11275             return;
11276         }
11277             
11278         var dv = ''; // display value
11279         var vv = ''; // value value..
11280         this.lastData = o;
11281         if (this.displayField) {
11282             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11283         } else {
11284             // this is an error condition!!!
11285             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11286         }
11287         
11288         if(this.valueField){
11289             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11290         }
11291         
11292         if(this.hiddenField){
11293             this.hiddenField.dom.value = vv;
11294             
11295             this.lastSelectionText = dv;
11296             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11297             this.value = vv;
11298             return;
11299         }
11300         // no hidden field.. - we store the value in 'value', but still display
11301         // display field!!!!
11302         this.lastSelectionText = dv;
11303         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11304         this.value = vv;
11305         
11306         
11307     },
11308     // private
11309     reset : function(){
11310         // overridden so that last data is reset..
11311         this.setValue(this.originalValue);
11312         this.clearInvalid();
11313         this.lastData = false;
11314         if (this.view) {
11315             this.view.clearSelections();
11316         }
11317     },
11318     // private
11319     findRecord : function(prop, value){
11320         var record;
11321         if(this.store.getCount() > 0){
11322             this.store.each(function(r){
11323                 if(r.data[prop] == value){
11324                     record = r;
11325                     return false;
11326                 }
11327                 return true;
11328             });
11329         }
11330         return record;
11331     },
11332     
11333     getName: function()
11334     {
11335         // returns hidden if it's set..
11336         if (!this.rendered) {return ''};
11337         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11338         
11339     },
11340     // private
11341     onViewMove : function(e, t){
11342         this.inKeyMode = false;
11343     },
11344
11345     // private
11346     onViewOver : function(e, t){
11347         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11348             return;
11349         }
11350         var item = this.view.findItemFromChild(t);
11351         
11352         if(item){
11353             var index = this.view.indexOf(item);
11354             this.select(index, false);
11355         }
11356     },
11357
11358     // private
11359     onViewClick : function(view, doFocus, el, e)
11360     {
11361         var index = this.view.getSelectedIndexes()[0];
11362         
11363         var r = this.store.getAt(index);
11364         
11365         if(this.tickable){
11366             
11367             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11368                 return;
11369             }
11370             
11371             var rm = false;
11372             var _this = this;
11373             
11374             Roo.each(this.tickItems, function(v,k){
11375                 
11376                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11377                     _this.tickItems.splice(k, 1);
11378                     rm = true;
11379                     return;
11380                 }
11381             })
11382             
11383             if(rm){
11384                 return;
11385             }
11386             
11387             this.tickItems.push(r.data);
11388             return;
11389         }
11390         
11391         if(r){
11392             this.onSelect(r, index);
11393         }
11394         if(doFocus !== false && !this.blockFocus){
11395             this.inputEl().focus();
11396         }
11397     },
11398
11399     // private
11400     restrictHeight : function(){
11401         //this.innerList.dom.style.height = '';
11402         //var inner = this.innerList.dom;
11403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11405         //this.list.beginUpdate();
11406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11407         this.list.alignTo(this.inputEl(), this.listAlign);
11408         this.list.alignTo(this.inputEl(), this.listAlign);
11409         //this.list.endUpdate();
11410     },
11411
11412     // private
11413     onEmptyResults : function(){
11414         this.collapse();
11415     },
11416
11417     /**
11418      * Returns true if the dropdown list is expanded, else false.
11419      */
11420     isExpanded : function(){
11421         return this.list.isVisible();
11422     },
11423
11424     /**
11425      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11426      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11427      * @param {String} value The data value of the item to select
11428      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11429      * selected item if it is not currently in view (defaults to true)
11430      * @return {Boolean} True if the value matched an item in the list, else false
11431      */
11432     selectByValue : function(v, scrollIntoView){
11433         if(v !== undefined && v !== null){
11434             var r = this.findRecord(this.valueField || this.displayField, v);
11435             if(r){
11436                 this.select(this.store.indexOf(r), scrollIntoView);
11437                 return true;
11438             }
11439         }
11440         return false;
11441     },
11442
11443     /**
11444      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11445      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11446      * @param {Number} index The zero-based index of the list item to select
11447      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11448      * selected item if it is not currently in view (defaults to true)
11449      */
11450     select : function(index, scrollIntoView){
11451         this.selectedIndex = index;
11452         this.view.select(index);
11453         if(scrollIntoView !== false){
11454             var el = this.view.getNode(index);
11455             if(el && !this.multiple && !this.tickable){
11456                 this.list.scrollChildIntoView(el, false);
11457             }
11458         }
11459     },
11460
11461     // private
11462     selectNext : function(){
11463         var ct = this.store.getCount();
11464         if(ct > 0){
11465             if(this.selectedIndex == -1){
11466                 this.select(0);
11467             }else if(this.selectedIndex < ct-1){
11468                 this.select(this.selectedIndex+1);
11469             }
11470         }
11471     },
11472
11473     // private
11474     selectPrev : function(){
11475         var ct = this.store.getCount();
11476         if(ct > 0){
11477             if(this.selectedIndex == -1){
11478                 this.select(0);
11479             }else if(this.selectedIndex != 0){
11480                 this.select(this.selectedIndex-1);
11481             }
11482         }
11483     },
11484
11485     // private
11486     onKeyUp : function(e){
11487         if(this.editable !== false && !e.isSpecialKey()){
11488             this.lastKey = e.getKey();
11489             this.dqTask.delay(this.queryDelay);
11490         }
11491     },
11492
11493     // private
11494     validateBlur : function(){
11495         return !this.list || !this.list.isVisible();   
11496     },
11497
11498     // private
11499     initQuery : function(){
11500         this.doQuery(this.getRawValue());
11501     },
11502
11503     // private
11504     doForce : function(){
11505         if(this.inputEl().dom.value.length > 0){
11506             this.inputEl().dom.value =
11507                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11508              
11509         }
11510     },
11511
11512     /**
11513      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11514      * query allowing the query action to be canceled if needed.
11515      * @param {String} query The SQL query to execute
11516      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11517      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11518      * saved in the current store (defaults to false)
11519      */
11520     doQuery : function(q, forceAll){
11521         
11522         if(q === undefined || q === null){
11523             q = '';
11524         }
11525         var qe = {
11526             query: q,
11527             forceAll: forceAll,
11528             combo: this,
11529             cancel:false
11530         };
11531         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11532             return false;
11533         }
11534         q = qe.query;
11535         
11536         forceAll = qe.forceAll;
11537         if(forceAll === true || (q.length >= this.minChars)){
11538             
11539             this.hasQuery = true;
11540             
11541             if(this.lastQuery != q || this.alwaysQuery){
11542                 this.lastQuery = q;
11543                 if(this.mode == 'local'){
11544                     this.selectedIndex = -1;
11545                     if(forceAll){
11546                         this.store.clearFilter();
11547                     }else{
11548                         this.store.filter(this.displayField, q);
11549                     }
11550                     this.onLoad();
11551                 }else{
11552                     this.store.baseParams[this.queryParam] = q;
11553                     
11554                     var options = {params : this.getParams(q)};
11555                     
11556                     if(this.loadNext){
11557                         options.add = true;
11558                         options.params.start = this.page * this.pageSize;
11559                     }
11560                     
11561                     this.store.load(options);
11562                     /*
11563                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11564                      *  we should expand the list on onLoad
11565                      *  so command out it
11566                      */
11567 //                    this.expand();
11568                 }
11569             }else{
11570                 this.selectedIndex = -1;
11571                 this.onLoad();   
11572             }
11573         }
11574         
11575         this.loadNext = false;
11576     },
11577
11578     // private
11579     getParams : function(q){
11580         var p = {};
11581         //p[this.queryParam] = q;
11582         
11583         if(this.pageSize){
11584             p.start = 0;
11585             p.limit = this.pageSize;
11586         }
11587         return p;
11588     },
11589
11590     /**
11591      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11592      */
11593     collapse : function(){
11594         if(!this.isExpanded()){
11595             return;
11596         }
11597         
11598         this.list.hide();
11599         
11600         if(this.tickable){
11601             this.okBtn.hide();
11602             this.cancelBtn.hide();
11603             this.trigger.show();
11604         }
11605         
11606         Roo.get(document).un('mousedown', this.collapseIf, this);
11607         Roo.get(document).un('mousewheel', this.collapseIf, this);
11608         if (!this.editable) {
11609             Roo.get(document).un('keydown', this.listKeyPress, this);
11610         }
11611         this.fireEvent('collapse', this);
11612     },
11613
11614     // private
11615     collapseIf : function(e){
11616         var in_combo  = e.within(this.el);
11617         var in_list =  e.within(this.list);
11618         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11619         
11620         if (in_combo || in_list || is_list) {
11621             //e.stopPropagation();
11622             return;
11623         }
11624         
11625         if(this.tickable){
11626             this.onTickableFooterButtonClick(e, false, false);
11627         }
11628
11629         this.collapse();
11630         
11631     },
11632
11633     /**
11634      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11635      */
11636     expand : function(){
11637        
11638         if(this.isExpanded() || !this.hasFocus){
11639             return;
11640         }
11641         
11642         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11643         this.list.setWidth(lw);
11644         
11645         
11646          Roo.log('expand');
11647         
11648         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11649         this.list.setWidth(lw);
11650             
11651         this.list.show();
11652         
11653         this.restrictHeight();
11654         
11655         if(this.tickable){
11656             
11657             this.tickItems = Roo.apply([], this.item);
11658             
11659             this.okBtn.show();
11660             this.cancelBtn.show();
11661             this.trigger.hide();
11662             
11663         }
11664         
11665         Roo.get(document).on('mousedown', this.collapseIf, this);
11666         Roo.get(document).on('mousewheel', this.collapseIf, this);
11667         if (!this.editable) {
11668             Roo.get(document).on('keydown', this.listKeyPress, this);
11669         }
11670         
11671         this.fireEvent('expand', this);
11672     },
11673
11674     // private
11675     // Implements the default empty TriggerField.onTriggerClick function
11676     onTriggerClick : function(e)
11677     {
11678         Roo.log('trigger click');
11679         
11680         if(this.disabled || !this.triggerList){
11681             return;
11682         }
11683         
11684         this.page = 0;
11685         this.loadNext = false;
11686         
11687         if(this.isExpanded()){
11688             this.collapse();
11689             if (!this.blockFocus) {
11690                 this.inputEl().focus();
11691             }
11692             
11693         }else {
11694             this.hasFocus = true;
11695             if(this.triggerAction == 'all') {
11696                 this.doQuery(this.allQuery, true);
11697             } else {
11698                 this.doQuery(this.getRawValue());
11699             }
11700             if (!this.blockFocus) {
11701                 this.inputEl().focus();
11702             }
11703         }
11704     },
11705     
11706     onTickableTriggerClick : function(e)
11707     {
11708         if(this.disabled){
11709             return;
11710         }
11711         
11712         this.page = 0;
11713         this.loadNext = false;
11714         this.hasFocus = true;
11715         
11716         if(this.triggerAction == 'all') {
11717             this.doQuery(this.allQuery, true);
11718         } else {
11719             this.doQuery(this.getRawValue());
11720         }
11721     },
11722     
11723     onSearchFieldClick : function(e)
11724     {
11725         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11726             return;
11727         }
11728         
11729         this.page = 0;
11730         this.loadNext = false;
11731         this.hasFocus = true;
11732         
11733         if(this.triggerAction == 'all') {
11734             this.doQuery(this.allQuery, true);
11735         } else {
11736             this.doQuery(this.getRawValue());
11737         }
11738     },
11739     
11740     listKeyPress : function(e)
11741     {
11742         //Roo.log('listkeypress');
11743         // scroll to first matching element based on key pres..
11744         if (e.isSpecialKey()) {
11745             return false;
11746         }
11747         var k = String.fromCharCode(e.getKey()).toUpperCase();
11748         //Roo.log(k);
11749         var match  = false;
11750         var csel = this.view.getSelectedNodes();
11751         var cselitem = false;
11752         if (csel.length) {
11753             var ix = this.view.indexOf(csel[0]);
11754             cselitem  = this.store.getAt(ix);
11755             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11756                 cselitem = false;
11757             }
11758             
11759         }
11760         
11761         this.store.each(function(v) { 
11762             if (cselitem) {
11763                 // start at existing selection.
11764                 if (cselitem.id == v.id) {
11765                     cselitem = false;
11766                 }
11767                 return true;
11768             }
11769                 
11770             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11771                 match = this.store.indexOf(v);
11772                 return false;
11773             }
11774             return true;
11775         }, this);
11776         
11777         if (match === false) {
11778             return true; // no more action?
11779         }
11780         // scroll to?
11781         this.view.select(match);
11782         var sn = Roo.get(this.view.getSelectedNodes()[0])
11783         //sn.scrollIntoView(sn.dom.parentNode, false);
11784     },
11785     
11786     onViewScroll : function(e, t){
11787         
11788         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){
11789             return;
11790         }
11791         
11792         this.hasQuery = true;
11793         
11794         this.loading = this.list.select('.loading', true).first();
11795         
11796         if(this.loading === null){
11797             this.list.createChild({
11798                 tag: 'div',
11799                 cls: 'loading select2-more-results select2-active',
11800                 html: 'Loading more results...'
11801             })
11802             
11803             this.loading = this.list.select('.loading', true).first();
11804             
11805             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11806             
11807             this.loading.hide();
11808         }
11809         
11810         this.loading.show();
11811         
11812         var _combo = this;
11813         
11814         this.page++;
11815         this.loadNext = true;
11816         
11817         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11818         
11819         return;
11820     },
11821     
11822     addItem : function(o)
11823     {   
11824         var dv = ''; // display value
11825         
11826         if (this.displayField) {
11827             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11828         } else {
11829             // this is an error condition!!!
11830             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11831         }
11832         
11833         if(!dv.length){
11834             return;
11835         }
11836         
11837         var choice = this.choices.createChild({
11838             tag: 'li',
11839             cls: 'select2-search-choice',
11840             cn: [
11841                 {
11842                     tag: 'div',
11843                     html: dv
11844                 },
11845                 {
11846                     tag: 'a',
11847                     href: '#',
11848                     cls: 'select2-search-choice-close',
11849                     tabindex: '-1'
11850                 }
11851             ]
11852             
11853         }, this.searchField);
11854         
11855         var close = choice.select('a.select2-search-choice-close', true).first()
11856         
11857         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11858         
11859         this.item.push(o);
11860         
11861         this.lastData = o;
11862         
11863         this.syncValue();
11864         
11865         this.inputEl().dom.value = '';
11866         
11867     },
11868     
11869     onRemoveItem : function(e, _self, o)
11870     {
11871         e.preventDefault();
11872         var index = this.item.indexOf(o.data) * 1;
11873         
11874         if( index < 0){
11875             Roo.log('not this item?!');
11876             return;
11877         }
11878         
11879         this.item.splice(index, 1);
11880         o.item.remove();
11881         
11882         this.syncValue();
11883         
11884         this.fireEvent('remove', this, e);
11885         
11886     },
11887     
11888     syncValue : function()
11889     {
11890         if(!this.item.length){
11891             this.clearValue();
11892             return;
11893         }
11894             
11895         var value = [];
11896         var _this = this;
11897         Roo.each(this.item, function(i){
11898             if(_this.valueField){
11899                 value.push(i[_this.valueField]);
11900                 return;
11901             }
11902
11903             value.push(i);
11904         });
11905
11906         this.value = value.join(',');
11907
11908         if(this.hiddenField){
11909             this.hiddenField.dom.value = this.value;
11910         }
11911     },
11912     
11913     clearItem : function()
11914     {
11915         if(!this.multiple){
11916             return;
11917         }
11918         
11919         this.item = [];
11920         
11921         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11922            c.remove();
11923         });
11924         
11925         this.syncValue();
11926     },
11927     
11928     inputEl: function ()
11929     {
11930         if(this.tickable){
11931             return this.searchField;
11932         }
11933         return this.el.select('input.form-control',true).first();
11934     },
11935     
11936     
11937     onTickableFooterButtonClick : function(e, btn, el)
11938     {
11939         e.preventDefault();
11940         
11941         if(btn && btn.name == 'cancel'){
11942             this.tickItems = Roo.apply([], this.item);
11943             this.collapse();
11944             return;
11945         }
11946         
11947         this.clearItem();
11948         
11949         var _this = this;
11950         
11951         Roo.each(this.tickItems, function(o){
11952             _this.addItem(o);
11953         });
11954         
11955         this.collapse();
11956         
11957     }
11958     
11959     
11960
11961     /** 
11962     * @cfg {Boolean} grow 
11963     * @hide 
11964     */
11965     /** 
11966     * @cfg {Number} growMin 
11967     * @hide 
11968     */
11969     /** 
11970     * @cfg {Number} growMax 
11971     * @hide 
11972     */
11973     /**
11974      * @hide
11975      * @method autoSize
11976      */
11977 });
11978 /*
11979  * Based on:
11980  * Ext JS Library 1.1.1
11981  * Copyright(c) 2006-2007, Ext JS, LLC.
11982  *
11983  * Originally Released Under LGPL - original licence link has changed is not relivant.
11984  *
11985  * Fork - LGPL
11986  * <script type="text/javascript">
11987  */
11988
11989 /**
11990  * @class Roo.View
11991  * @extends Roo.util.Observable
11992  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11993  * This class also supports single and multi selection modes. <br>
11994  * Create a data model bound view:
11995  <pre><code>
11996  var store = new Roo.data.Store(...);
11997
11998  var view = new Roo.View({
11999     el : "my-element",
12000     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12001  
12002     singleSelect: true,
12003     selectedClass: "ydataview-selected",
12004     store: store
12005  });
12006
12007  // listen for node click?
12008  view.on("click", function(vw, index, node, e){
12009  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12010  });
12011
12012  // load XML data
12013  dataModel.load("foobar.xml");
12014  </code></pre>
12015  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12016  * <br><br>
12017  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12018  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12019  * 
12020  * Note: old style constructor is still suported (container, template, config)
12021  * 
12022  * @constructor
12023  * Create a new View
12024  * @param {Object} config The config object
12025  * 
12026  */
12027 Roo.View = function(config, depreciated_tpl, depreciated_config){
12028     
12029     this.parent = false;
12030     
12031     if (typeof(depreciated_tpl) == 'undefined') {
12032         // new way.. - universal constructor.
12033         Roo.apply(this, config);
12034         this.el  = Roo.get(this.el);
12035     } else {
12036         // old format..
12037         this.el  = Roo.get(config);
12038         this.tpl = depreciated_tpl;
12039         Roo.apply(this, depreciated_config);
12040     }
12041     this.wrapEl  = this.el.wrap().wrap();
12042     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12043     
12044     
12045     if(typeof(this.tpl) == "string"){
12046         this.tpl = new Roo.Template(this.tpl);
12047     } else {
12048         // support xtype ctors..
12049         this.tpl = new Roo.factory(this.tpl, Roo);
12050     }
12051     
12052     
12053     this.tpl.compile();
12054     
12055     /** @private */
12056     this.addEvents({
12057         /**
12058          * @event beforeclick
12059          * Fires before a click is processed. Returns false to cancel the default action.
12060          * @param {Roo.View} this
12061          * @param {Number} index The index of the target node
12062          * @param {HTMLElement} node The target node
12063          * @param {Roo.EventObject} e The raw event object
12064          */
12065             "beforeclick" : true,
12066         /**
12067          * @event click
12068          * Fires when a template node is clicked.
12069          * @param {Roo.View} this
12070          * @param {Number} index The index of the target node
12071          * @param {HTMLElement} node The target node
12072          * @param {Roo.EventObject} e The raw event object
12073          */
12074             "click" : true,
12075         /**
12076          * @event dblclick
12077          * Fires when a template node is double clicked.
12078          * @param {Roo.View} this
12079          * @param {Number} index The index of the target node
12080          * @param {HTMLElement} node The target node
12081          * @param {Roo.EventObject} e The raw event object
12082          */
12083             "dblclick" : true,
12084         /**
12085          * @event contextmenu
12086          * Fires when a template node is right clicked.
12087          * @param {Roo.View} this
12088          * @param {Number} index The index of the target node
12089          * @param {HTMLElement} node The target node
12090          * @param {Roo.EventObject} e The raw event object
12091          */
12092             "contextmenu" : true,
12093         /**
12094          * @event selectionchange
12095          * Fires when the selected nodes change.
12096          * @param {Roo.View} this
12097          * @param {Array} selections Array of the selected nodes
12098          */
12099             "selectionchange" : true,
12100     
12101         /**
12102          * @event beforeselect
12103          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12104          * @param {Roo.View} this
12105          * @param {HTMLElement} node The node to be selected
12106          * @param {Array} selections Array of currently selected nodes
12107          */
12108             "beforeselect" : true,
12109         /**
12110          * @event preparedata
12111          * Fires on every row to render, to allow you to change the data.
12112          * @param {Roo.View} this
12113          * @param {Object} data to be rendered (change this)
12114          */
12115           "preparedata" : true
12116           
12117           
12118         });
12119
12120
12121
12122     this.el.on({
12123         "click": this.onClick,
12124         "dblclick": this.onDblClick,
12125         "contextmenu": this.onContextMenu,
12126         scope:this
12127     });
12128
12129     this.selections = [];
12130     this.nodes = [];
12131     this.cmp = new Roo.CompositeElementLite([]);
12132     if(this.store){
12133         this.store = Roo.factory(this.store, Roo.data);
12134         this.setStore(this.store, true);
12135     }
12136     
12137     if ( this.footer && this.footer.xtype) {
12138            
12139          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12140         
12141         this.footer.dataSource = this.store
12142         this.footer.container = fctr;
12143         this.footer = Roo.factory(this.footer, Roo);
12144         fctr.insertFirst(this.el);
12145         
12146         // this is a bit insane - as the paging toolbar seems to detach the el..
12147 //        dom.parentNode.parentNode.parentNode
12148          // they get detached?
12149     }
12150     
12151     
12152     Roo.View.superclass.constructor.call(this);
12153     
12154     
12155 };
12156
12157 Roo.extend(Roo.View, Roo.util.Observable, {
12158     
12159      /**
12160      * @cfg {Roo.data.Store} store Data store to load data from.
12161      */
12162     store : false,
12163     
12164     /**
12165      * @cfg {String|Roo.Element} el The container element.
12166      */
12167     el : '',
12168     
12169     /**
12170      * @cfg {String|Roo.Template} tpl The template used by this View 
12171      */
12172     tpl : false,
12173     /**
12174      * @cfg {String} dataName the named area of the template to use as the data area
12175      *                          Works with domtemplates roo-name="name"
12176      */
12177     dataName: false,
12178     /**
12179      * @cfg {String} selectedClass The css class to add to selected nodes
12180      */
12181     selectedClass : "x-view-selected",
12182      /**
12183      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12184      */
12185     emptyText : "",
12186     
12187     /**
12188      * @cfg {String} text to display on mask (default Loading)
12189      */
12190     mask : false,
12191     /**
12192      * @cfg {Boolean} multiSelect Allow multiple selection
12193      */
12194     multiSelect : false,
12195     /**
12196      * @cfg {Boolean} singleSelect Allow single selection
12197      */
12198     singleSelect:  false,
12199     
12200     /**
12201      * @cfg {Boolean} toggleSelect - selecting 
12202      */
12203     toggleSelect : false,
12204     
12205     /**
12206      * @cfg {Boolean} tickable - selecting 
12207      */
12208     tickable : false,
12209     
12210     /**
12211      * Returns the element this view is bound to.
12212      * @return {Roo.Element}
12213      */
12214     getEl : function(){
12215         return this.wrapEl;
12216     },
12217     
12218     
12219
12220     /**
12221      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12222      */
12223     refresh : function(){
12224         Roo.log('refresh');
12225         var t = this.tpl;
12226         
12227         // if we are using something like 'domtemplate', then
12228         // the what gets used is:
12229         // t.applySubtemplate(NAME, data, wrapping data..)
12230         // the outer template then get' applied with
12231         //     the store 'extra data'
12232         // and the body get's added to the
12233         //      roo-name="data" node?
12234         //      <span class='roo-tpl-{name}'></span> ?????
12235         
12236         
12237         
12238         this.clearSelections();
12239         this.el.update("");
12240         var html = [];
12241         var records = this.store.getRange();
12242         if(records.length < 1) {
12243             
12244             // is this valid??  = should it render a template??
12245             
12246             this.el.update(this.emptyText);
12247             return;
12248         }
12249         var el = this.el;
12250         if (this.dataName) {
12251             this.el.update(t.apply(this.store.meta)); //????
12252             el = this.el.child('.roo-tpl-' + this.dataName);
12253         }
12254         
12255         for(var i = 0, len = records.length; i < len; i++){
12256             var data = this.prepareData(records[i].data, i, records[i]);
12257             this.fireEvent("preparedata", this, data, i, records[i]);
12258             
12259             var d = Roo.apply({}, data);
12260             
12261             if(this.tickable){
12262                 Roo.apply(d, {'roo-id' : Roo.id()});
12263                 
12264                 var _this = this;
12265             
12266                 Roo.each(this.parent.item, function(item){
12267                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12268                         return;
12269                     }
12270                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12271                 });
12272             }
12273             
12274             html[html.length] = Roo.util.Format.trim(
12275                 this.dataName ?
12276                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12277                     t.apply(d)
12278             );
12279         }
12280         
12281         
12282         
12283         el.update(html.join(""));
12284         this.nodes = el.dom.childNodes;
12285         this.updateIndexes(0);
12286     },
12287     
12288
12289     /**
12290      * Function to override to reformat the data that is sent to
12291      * the template for each node.
12292      * DEPRICATED - use the preparedata event handler.
12293      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12294      * a JSON object for an UpdateManager bound view).
12295      */
12296     prepareData : function(data, index, record)
12297     {
12298         this.fireEvent("preparedata", this, data, index, record);
12299         return data;
12300     },
12301
12302     onUpdate : function(ds, record){
12303          Roo.log('on update');   
12304         this.clearSelections();
12305         var index = this.store.indexOf(record);
12306         var n = this.nodes[index];
12307         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12308         n.parentNode.removeChild(n);
12309         this.updateIndexes(index, index);
12310     },
12311
12312     
12313     
12314 // --------- FIXME     
12315     onAdd : function(ds, records, index)
12316     {
12317         Roo.log(['on Add', ds, records, index] );        
12318         this.clearSelections();
12319         if(this.nodes.length == 0){
12320             this.refresh();
12321             return;
12322         }
12323         var n = this.nodes[index];
12324         for(var i = 0, len = records.length; i < len; i++){
12325             var d = this.prepareData(records[i].data, i, records[i]);
12326             if(n){
12327                 this.tpl.insertBefore(n, d);
12328             }else{
12329                 
12330                 this.tpl.append(this.el, d);
12331             }
12332         }
12333         this.updateIndexes(index);
12334     },
12335
12336     onRemove : function(ds, record, index){
12337         Roo.log('onRemove');
12338         this.clearSelections();
12339         var el = this.dataName  ?
12340             this.el.child('.roo-tpl-' + this.dataName) :
12341             this.el; 
12342         
12343         el.dom.removeChild(this.nodes[index]);
12344         this.updateIndexes(index);
12345     },
12346
12347     /**
12348      * Refresh an individual node.
12349      * @param {Number} index
12350      */
12351     refreshNode : function(index){
12352         this.onUpdate(this.store, this.store.getAt(index));
12353     },
12354
12355     updateIndexes : function(startIndex, endIndex){
12356         var ns = this.nodes;
12357         startIndex = startIndex || 0;
12358         endIndex = endIndex || ns.length - 1;
12359         for(var i = startIndex; i <= endIndex; i++){
12360             ns[i].nodeIndex = i;
12361         }
12362     },
12363
12364     /**
12365      * Changes the data store this view uses and refresh the view.
12366      * @param {Store} store
12367      */
12368     setStore : function(store, initial){
12369         if(!initial && this.store){
12370             this.store.un("datachanged", this.refresh);
12371             this.store.un("add", this.onAdd);
12372             this.store.un("remove", this.onRemove);
12373             this.store.un("update", this.onUpdate);
12374             this.store.un("clear", this.refresh);
12375             this.store.un("beforeload", this.onBeforeLoad);
12376             this.store.un("load", this.onLoad);
12377             this.store.un("loadexception", this.onLoad);
12378         }
12379         if(store){
12380           
12381             store.on("datachanged", this.refresh, this);
12382             store.on("add", this.onAdd, this);
12383             store.on("remove", this.onRemove, this);
12384             store.on("update", this.onUpdate, this);
12385             store.on("clear", this.refresh, this);
12386             store.on("beforeload", this.onBeforeLoad, this);
12387             store.on("load", this.onLoad, this);
12388             store.on("loadexception", this.onLoad, this);
12389         }
12390         
12391         if(store){
12392             this.refresh();
12393         }
12394     },
12395     /**
12396      * onbeforeLoad - masks the loading area.
12397      *
12398      */
12399     onBeforeLoad : function(store,opts)
12400     {
12401          Roo.log('onBeforeLoad');   
12402         if (!opts.add) {
12403             this.el.update("");
12404         }
12405         this.el.mask(this.mask ? this.mask : "Loading" ); 
12406     },
12407     onLoad : function ()
12408     {
12409         this.el.unmask();
12410     },
12411     
12412
12413     /**
12414      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12415      * @param {HTMLElement} node
12416      * @return {HTMLElement} The template node
12417      */
12418     findItemFromChild : function(node){
12419         var el = this.dataName  ?
12420             this.el.child('.roo-tpl-' + this.dataName,true) :
12421             this.el.dom; 
12422         
12423         if(!node || node.parentNode == el){
12424                     return node;
12425             }
12426             var p = node.parentNode;
12427             while(p && p != el){
12428             if(p.parentNode == el){
12429                 return p;
12430             }
12431             p = p.parentNode;
12432         }
12433             return null;
12434     },
12435
12436     /** @ignore */
12437     onClick : function(e){
12438         var item = this.findItemFromChild(e.getTarget());
12439         if(item){
12440             var index = this.indexOf(item);
12441             if(this.onItemClick(item, index, e) !== false){
12442                 this.fireEvent("click", this, index, item, e);
12443             }
12444         }else{
12445             this.clearSelections();
12446         }
12447     },
12448
12449     /** @ignore */
12450     onContextMenu : function(e){
12451         var item = this.findItemFromChild(e.getTarget());
12452         if(item){
12453             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12454         }
12455     },
12456
12457     /** @ignore */
12458     onDblClick : function(e){
12459         var item = this.findItemFromChild(e.getTarget());
12460         if(item){
12461             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12462         }
12463     },
12464
12465     onItemClick : function(item, index, e)
12466     {
12467         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12468             return false;
12469         }
12470         if (this.toggleSelect) {
12471             var m = this.isSelected(item) ? 'unselect' : 'select';
12472             Roo.log(m);
12473             var _t = this;
12474             _t[m](item, true, false);
12475             return true;
12476         }
12477         if(this.multiSelect || this.singleSelect){
12478             if(this.multiSelect && e.shiftKey && this.lastSelection){
12479                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12480             }else{
12481                 this.select(item, this.multiSelect && e.ctrlKey);
12482                 this.lastSelection = item;
12483             }
12484             
12485             if(!this.tickable){
12486                 e.preventDefault();
12487             }
12488             
12489         }
12490         return true;
12491     },
12492
12493     /**
12494      * Get the number of selected nodes.
12495      * @return {Number}
12496      */
12497     getSelectionCount : function(){
12498         return this.selections.length;
12499     },
12500
12501     /**
12502      * Get the currently selected nodes.
12503      * @return {Array} An array of HTMLElements
12504      */
12505     getSelectedNodes : function(){
12506         return this.selections;
12507     },
12508
12509     /**
12510      * Get the indexes of the selected nodes.
12511      * @return {Array}
12512      */
12513     getSelectedIndexes : function(){
12514         var indexes = [], s = this.selections;
12515         for(var i = 0, len = s.length; i < len; i++){
12516             indexes.push(s[i].nodeIndex);
12517         }
12518         return indexes;
12519     },
12520
12521     /**
12522      * Clear all selections
12523      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12524      */
12525     clearSelections : function(suppressEvent){
12526         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12527             this.cmp.elements = this.selections;
12528             this.cmp.removeClass(this.selectedClass);
12529             this.selections = [];
12530             if(!suppressEvent){
12531                 this.fireEvent("selectionchange", this, this.selections);
12532             }
12533         }
12534     },
12535
12536     /**
12537      * Returns true if the passed node is selected
12538      * @param {HTMLElement/Number} node The node or node index
12539      * @return {Boolean}
12540      */
12541     isSelected : function(node){
12542         var s = this.selections;
12543         if(s.length < 1){
12544             return false;
12545         }
12546         node = this.getNode(node);
12547         return s.indexOf(node) !== -1;
12548     },
12549
12550     /**
12551      * Selects nodes.
12552      * @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
12553      * @param {Boolean} keepExisting (optional) true to keep existing selections
12554      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12555      */
12556     select : function(nodeInfo, keepExisting, suppressEvent){
12557         if(nodeInfo instanceof Array){
12558             if(!keepExisting){
12559                 this.clearSelections(true);
12560             }
12561             for(var i = 0, len = nodeInfo.length; i < len; i++){
12562                 this.select(nodeInfo[i], true, true);
12563             }
12564             return;
12565         } 
12566         var node = this.getNode(nodeInfo);
12567         if(!node || this.isSelected(node)){
12568             return; // already selected.
12569         }
12570         if(!keepExisting){
12571             this.clearSelections(true);
12572         }
12573         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12574             Roo.fly(node).addClass(this.selectedClass);
12575             this.selections.push(node);
12576             if(!suppressEvent){
12577                 this.fireEvent("selectionchange", this, this.selections);
12578             }
12579         }
12580         
12581         
12582     },
12583       /**
12584      * Unselects nodes.
12585      * @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
12586      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12587      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12588      */
12589     unselect : function(nodeInfo, keepExisting, suppressEvent)
12590     {
12591         if(nodeInfo instanceof Array){
12592             Roo.each(this.selections, function(s) {
12593                 this.unselect(s, nodeInfo);
12594             }, this);
12595             return;
12596         }
12597         var node = this.getNode(nodeInfo);
12598         if(!node || !this.isSelected(node)){
12599             Roo.log("not selected");
12600             return; // not selected.
12601         }
12602         // fireevent???
12603         var ns = [];
12604         Roo.each(this.selections, function(s) {
12605             if (s == node ) {
12606                 Roo.fly(node).removeClass(this.selectedClass);
12607
12608                 return;
12609             }
12610             ns.push(s);
12611         },this);
12612         
12613         this.selections= ns;
12614         this.fireEvent("selectionchange", this, this.selections);
12615     },
12616
12617     /**
12618      * Gets a template node.
12619      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12620      * @return {HTMLElement} The node or null if it wasn't found
12621      */
12622     getNode : function(nodeInfo){
12623         if(typeof nodeInfo == "string"){
12624             return document.getElementById(nodeInfo);
12625         }else if(typeof nodeInfo == "number"){
12626             return this.nodes[nodeInfo];
12627         }
12628         return nodeInfo;
12629     },
12630
12631     /**
12632      * Gets a range template nodes.
12633      * @param {Number} startIndex
12634      * @param {Number} endIndex
12635      * @return {Array} An array of nodes
12636      */
12637     getNodes : function(start, end){
12638         var ns = this.nodes;
12639         start = start || 0;
12640         end = typeof end == "undefined" ? ns.length - 1 : end;
12641         var nodes = [];
12642         if(start <= end){
12643             for(var i = start; i <= end; i++){
12644                 nodes.push(ns[i]);
12645             }
12646         } else{
12647             for(var i = start; i >= end; i--){
12648                 nodes.push(ns[i]);
12649             }
12650         }
12651         return nodes;
12652     },
12653
12654     /**
12655      * Finds the index of the passed node
12656      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12657      * @return {Number} The index of the node or -1
12658      */
12659     indexOf : function(node){
12660         node = this.getNode(node);
12661         if(typeof node.nodeIndex == "number"){
12662             return node.nodeIndex;
12663         }
12664         var ns = this.nodes;
12665         for(var i = 0, len = ns.length; i < len; i++){
12666             if(ns[i] == node){
12667                 return i;
12668             }
12669         }
12670         return -1;
12671     }
12672 });
12673 /*
12674  * - LGPL
12675  *
12676  * based on jquery fullcalendar
12677  * 
12678  */
12679
12680 Roo.bootstrap = Roo.bootstrap || {};
12681 /**
12682  * @class Roo.bootstrap.Calendar
12683  * @extends Roo.bootstrap.Component
12684  * Bootstrap Calendar class
12685  * @cfg {Boolean} loadMask (true|false) default false
12686  * @cfg {Object} header generate the user specific header of the calendar, default false
12687
12688  * @constructor
12689  * Create a new Container
12690  * @param {Object} config The config object
12691  */
12692
12693
12694
12695 Roo.bootstrap.Calendar = function(config){
12696     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12697      this.addEvents({
12698         /**
12699              * @event select
12700              * Fires when a date is selected
12701              * @param {DatePicker} this
12702              * @param {Date} date The selected date
12703              */
12704         'select': true,
12705         /**
12706              * @event monthchange
12707              * Fires when the displayed month changes 
12708              * @param {DatePicker} this
12709              * @param {Date} date The selected month
12710              */
12711         'monthchange': true,
12712         /**
12713              * @event evententer
12714              * Fires when mouse over an event
12715              * @param {Calendar} this
12716              * @param {event} Event
12717              */
12718         'evententer': true,
12719         /**
12720              * @event eventleave
12721              * Fires when the mouse leaves an
12722              * @param {Calendar} this
12723              * @param {event}
12724              */
12725         'eventleave': true,
12726         /**
12727              * @event eventclick
12728              * Fires when the mouse click an
12729              * @param {Calendar} this
12730              * @param {event}
12731              */
12732         'eventclick': true
12733         
12734     });
12735
12736 };
12737
12738 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12739     
12740      /**
12741      * @cfg {Number} startDay
12742      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12743      */
12744     startDay : 0,
12745     
12746     loadMask : false,
12747     
12748     header : false,
12749       
12750     getAutoCreate : function(){
12751         
12752         
12753         var fc_button = function(name, corner, style, content ) {
12754             return Roo.apply({},{
12755                 tag : 'span',
12756                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12757                          (corner.length ?
12758                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12759                             ''
12760                         ),
12761                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12762                 unselectable: 'on'
12763             });
12764         };
12765         
12766         var header = {};
12767         
12768         if(!this.header){
12769             header = {
12770                 tag : 'table',
12771                 cls : 'fc-header',
12772                 style : 'width:100%',
12773                 cn : [
12774                     {
12775                         tag: 'tr',
12776                         cn : [
12777                             {
12778                                 tag : 'td',
12779                                 cls : 'fc-header-left',
12780                                 cn : [
12781                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12782                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12783                                     { tag: 'span', cls: 'fc-header-space' },
12784                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12785
12786
12787                                 ]
12788                             },
12789
12790                             {
12791                                 tag : 'td',
12792                                 cls : 'fc-header-center',
12793                                 cn : [
12794                                     {
12795                                         tag: 'span',
12796                                         cls: 'fc-header-title',
12797                                         cn : {
12798                                             tag: 'H2',
12799                                             html : 'month / year'
12800                                         }
12801                                     }
12802
12803                                 ]
12804                             },
12805                             {
12806                                 tag : 'td',
12807                                 cls : 'fc-header-right',
12808                                 cn : [
12809                               /*      fc_button('month', 'left', '', 'month' ),
12810                                     fc_button('week', '', '', 'week' ),
12811                                     fc_button('day', 'right', '', 'day' )
12812                                 */    
12813
12814                                 ]
12815                             }
12816
12817                         ]
12818                     }
12819                 ]
12820             };
12821         }
12822         
12823         header = this.header;
12824         
12825        
12826         var cal_heads = function() {
12827             var ret = [];
12828             // fixme - handle this.
12829             
12830             for (var i =0; i < Date.dayNames.length; i++) {
12831                 var d = Date.dayNames[i];
12832                 ret.push({
12833                     tag: 'th',
12834                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12835                     html : d.substring(0,3)
12836                 });
12837                 
12838             }
12839             ret[0].cls += ' fc-first';
12840             ret[6].cls += ' fc-last';
12841             return ret;
12842         };
12843         var cal_cell = function(n) {
12844             return  {
12845                 tag: 'td',
12846                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12847                 cn : [
12848                     {
12849                         cn : [
12850                             {
12851                                 cls: 'fc-day-number',
12852                                 html: 'D'
12853                             },
12854                             {
12855                                 cls: 'fc-day-content',
12856                              
12857                                 cn : [
12858                                      {
12859                                         style: 'position: relative;' // height: 17px;
12860                                     }
12861                                 ]
12862                             }
12863                             
12864                             
12865                         ]
12866                     }
12867                 ]
12868                 
12869             }
12870         };
12871         var cal_rows = function() {
12872             
12873             var ret = []
12874             for (var r = 0; r < 6; r++) {
12875                 var row= {
12876                     tag : 'tr',
12877                     cls : 'fc-week',
12878                     cn : []
12879                 };
12880                 
12881                 for (var i =0; i < Date.dayNames.length; i++) {
12882                     var d = Date.dayNames[i];
12883                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12884
12885                 }
12886                 row.cn[0].cls+=' fc-first';
12887                 row.cn[0].cn[0].style = 'min-height:90px';
12888                 row.cn[6].cls+=' fc-last';
12889                 ret.push(row);
12890                 
12891             }
12892             ret[0].cls += ' fc-first';
12893             ret[4].cls += ' fc-prev-last';
12894             ret[5].cls += ' fc-last';
12895             return ret;
12896             
12897         };
12898         
12899         var cal_table = {
12900             tag: 'table',
12901             cls: 'fc-border-separate',
12902             style : 'width:100%',
12903             cellspacing  : 0,
12904             cn : [
12905                 { 
12906                     tag: 'thead',
12907                     cn : [
12908                         { 
12909                             tag: 'tr',
12910                             cls : 'fc-first fc-last',
12911                             cn : cal_heads()
12912                         }
12913                     ]
12914                 },
12915                 { 
12916                     tag: 'tbody',
12917                     cn : cal_rows()
12918                 }
12919                   
12920             ]
12921         };
12922          
12923          var cfg = {
12924             cls : 'fc fc-ltr',
12925             cn : [
12926                 header,
12927                 {
12928                     cls : 'fc-content',
12929                     style : "position: relative;",
12930                     cn : [
12931                         {
12932                             cls : 'fc-view fc-view-month fc-grid',
12933                             style : 'position: relative',
12934                             unselectable : 'on',
12935                             cn : [
12936                                 {
12937                                     cls : 'fc-event-container',
12938                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12939                                 },
12940                                 cal_table
12941                             ]
12942                         }
12943                     ]
12944     
12945                 }
12946            ] 
12947             
12948         };
12949         
12950          
12951         
12952         return cfg;
12953     },
12954     
12955     
12956     initEvents : function()
12957     {
12958         if(!this.store){
12959             throw "can not find store for calendar";
12960         }
12961         
12962         var mark = {
12963             tag: "div",
12964             cls:"x-dlg-mask",
12965             style: "text-align:center",
12966             cn: [
12967                 {
12968                     tag: "div",
12969                     style: "background-color:white;width:50%;margin:250 auto",
12970                     cn: [
12971                         {
12972                             tag: "img",
12973                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12974                         },
12975                         {
12976                             tag: "span",
12977                             html: "Loading"
12978                         }
12979                         
12980                     ]
12981                 }
12982             ]
12983         }
12984         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12985         
12986         var size = this.el.select('.fc-content', true).first().getSize();
12987         this.maskEl.setSize(size.width, size.height);
12988         this.maskEl.enableDisplayMode("block");
12989         if(!this.loadMask){
12990             this.maskEl.hide();
12991         }
12992         
12993         this.store = Roo.factory(this.store, Roo.data);
12994         this.store.on('load', this.onLoad, this);
12995         this.store.on('beforeload', this.onBeforeLoad, this);
12996         
12997         this.resize();
12998         
12999         this.cells = this.el.select('.fc-day',true);
13000         //Roo.log(this.cells);
13001         this.textNodes = this.el.query('.fc-day-number');
13002         this.cells.addClassOnOver('fc-state-hover');
13003         
13004         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13005         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13006         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13007         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13008         
13009         this.on('monthchange', this.onMonthChange, this);
13010         
13011         this.update(new Date().clearTime());
13012     },
13013     
13014     resize : function() {
13015         var sz  = this.el.getSize();
13016         
13017         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13018         this.el.select('.fc-day-content div',true).setHeight(34);
13019     },
13020     
13021     
13022     // private
13023     showPrevMonth : function(e){
13024         this.update(this.activeDate.add("mo", -1));
13025     },
13026     showToday : function(e){
13027         this.update(new Date().clearTime());
13028     },
13029     // private
13030     showNextMonth : function(e){
13031         this.update(this.activeDate.add("mo", 1));
13032     },
13033
13034     // private
13035     showPrevYear : function(){
13036         this.update(this.activeDate.add("y", -1));
13037     },
13038
13039     // private
13040     showNextYear : function(){
13041         this.update(this.activeDate.add("y", 1));
13042     },
13043
13044     
13045    // private
13046     update : function(date)
13047     {
13048         var vd = this.activeDate;
13049         this.activeDate = date;
13050 //        if(vd && this.el){
13051 //            var t = date.getTime();
13052 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13053 //                Roo.log('using add remove');
13054 //                
13055 //                this.fireEvent('monthchange', this, date);
13056 //                
13057 //                this.cells.removeClass("fc-state-highlight");
13058 //                this.cells.each(function(c){
13059 //                   if(c.dateValue == t){
13060 //                       c.addClass("fc-state-highlight");
13061 //                       setTimeout(function(){
13062 //                            try{c.dom.firstChild.focus();}catch(e){}
13063 //                       }, 50);
13064 //                       return false;
13065 //                   }
13066 //                   return true;
13067 //                });
13068 //                return;
13069 //            }
13070 //        }
13071         
13072         var days = date.getDaysInMonth();
13073         
13074         var firstOfMonth = date.getFirstDateOfMonth();
13075         var startingPos = firstOfMonth.getDay()-this.startDay;
13076         
13077         if(startingPos < this.startDay){
13078             startingPos += 7;
13079         }
13080         
13081         var pm = date.add(Date.MONTH, -1);
13082         var prevStart = pm.getDaysInMonth()-startingPos;
13083 //        
13084         this.cells = this.el.select('.fc-day',true);
13085         this.textNodes = this.el.query('.fc-day-number');
13086         this.cells.addClassOnOver('fc-state-hover');
13087         
13088         var cells = this.cells.elements;
13089         var textEls = this.textNodes;
13090         
13091         Roo.each(cells, function(cell){
13092             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13093         });
13094         
13095         days += startingPos;
13096
13097         // convert everything to numbers so it's fast
13098         var day = 86400000;
13099         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13100         //Roo.log(d);
13101         //Roo.log(pm);
13102         //Roo.log(prevStart);
13103         
13104         var today = new Date().clearTime().getTime();
13105         var sel = date.clearTime().getTime();
13106         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13107         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13108         var ddMatch = this.disabledDatesRE;
13109         var ddText = this.disabledDatesText;
13110         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13111         var ddaysText = this.disabledDaysText;
13112         var format = this.format;
13113         
13114         var setCellClass = function(cal, cell){
13115             cell.row = 0;
13116             cell.events = [];
13117             cell.more = [];
13118             //Roo.log('set Cell Class');
13119             cell.title = "";
13120             var t = d.getTime();
13121             
13122             //Roo.log(d);
13123             
13124             cell.dateValue = t;
13125             if(t == today){
13126                 cell.className += " fc-today";
13127                 cell.className += " fc-state-highlight";
13128                 cell.title = cal.todayText;
13129             }
13130             if(t == sel){
13131                 // disable highlight in other month..
13132                 //cell.className += " fc-state-highlight";
13133                 
13134             }
13135             // disabling
13136             if(t < min) {
13137                 cell.className = " fc-state-disabled";
13138                 cell.title = cal.minText;
13139                 return;
13140             }
13141             if(t > max) {
13142                 cell.className = " fc-state-disabled";
13143                 cell.title = cal.maxText;
13144                 return;
13145             }
13146             if(ddays){
13147                 if(ddays.indexOf(d.getDay()) != -1){
13148                     cell.title = ddaysText;
13149                     cell.className = " fc-state-disabled";
13150                 }
13151             }
13152             if(ddMatch && format){
13153                 var fvalue = d.dateFormat(format);
13154                 if(ddMatch.test(fvalue)){
13155                     cell.title = ddText.replace("%0", fvalue);
13156                     cell.className = " fc-state-disabled";
13157                 }
13158             }
13159             
13160             if (!cell.initialClassName) {
13161                 cell.initialClassName = cell.dom.className;
13162             }
13163             
13164             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13165         };
13166
13167         var i = 0;
13168         
13169         for(; i < startingPos; i++) {
13170             textEls[i].innerHTML = (++prevStart);
13171             d.setDate(d.getDate()+1);
13172             
13173             cells[i].className = "fc-past fc-other-month";
13174             setCellClass(this, cells[i]);
13175         }
13176         
13177         var intDay = 0;
13178         
13179         for(; i < days; i++){
13180             intDay = i - startingPos + 1;
13181             textEls[i].innerHTML = (intDay);
13182             d.setDate(d.getDate()+1);
13183             
13184             cells[i].className = ''; // "x-date-active";
13185             setCellClass(this, cells[i]);
13186         }
13187         var extraDays = 0;
13188         
13189         for(; i < 42; i++) {
13190             textEls[i].innerHTML = (++extraDays);
13191             d.setDate(d.getDate()+1);
13192             
13193             cells[i].className = "fc-future fc-other-month";
13194             setCellClass(this, cells[i]);
13195         }
13196         
13197         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13198         
13199         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13200         
13201         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13202         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13203         
13204         if(totalRows != 6){
13205             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13206             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13207         }
13208         
13209         this.fireEvent('monthchange', this, date);
13210         
13211         
13212         /*
13213         if(!this.internalRender){
13214             var main = this.el.dom.firstChild;
13215             var w = main.offsetWidth;
13216             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13217             Roo.fly(main).setWidth(w);
13218             this.internalRender = true;
13219             // opera does not respect the auto grow header center column
13220             // then, after it gets a width opera refuses to recalculate
13221             // without a second pass
13222             if(Roo.isOpera && !this.secondPass){
13223                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13224                 this.secondPass = true;
13225                 this.update.defer(10, this, [date]);
13226             }
13227         }
13228         */
13229         
13230     },
13231     
13232     findCell : function(dt) {
13233         dt = dt.clearTime().getTime();
13234         var ret = false;
13235         this.cells.each(function(c){
13236             //Roo.log("check " +c.dateValue + '?=' + dt);
13237             if(c.dateValue == dt){
13238                 ret = c;
13239                 return false;
13240             }
13241             return true;
13242         });
13243         
13244         return ret;
13245     },
13246     
13247     findCells : function(ev) {
13248         var s = ev.start.clone().clearTime().getTime();
13249        // Roo.log(s);
13250         var e= ev.end.clone().clearTime().getTime();
13251        // Roo.log(e);
13252         var ret = [];
13253         this.cells.each(function(c){
13254              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13255             
13256             if(c.dateValue > e){
13257                 return ;
13258             }
13259             if(c.dateValue < s){
13260                 return ;
13261             }
13262             ret.push(c);
13263         });
13264         
13265         return ret;    
13266     },
13267     
13268 //    findBestRow: function(cells)
13269 //    {
13270 //        var ret = 0;
13271 //        
13272 //        for (var i =0 ; i < cells.length;i++) {
13273 //            ret  = Math.max(cells[i].rows || 0,ret);
13274 //        }
13275 //        return ret;
13276 //        
13277 //    },
13278     
13279     
13280     addItem : function(ev)
13281     {
13282         // look for vertical location slot in
13283         var cells = this.findCells(ev);
13284         
13285 //        ev.row = this.findBestRow(cells);
13286         
13287         // work out the location.
13288         
13289         var crow = false;
13290         var rows = [];
13291         for(var i =0; i < cells.length; i++) {
13292             
13293             cells[i].row = cells[0].row;
13294             
13295             if(i == 0){
13296                 cells[i].row = cells[i].row + 1;
13297             }
13298             
13299             if (!crow) {
13300                 crow = {
13301                     start : cells[i],
13302                     end :  cells[i]
13303                 };
13304                 continue;
13305             }
13306             if (crow.start.getY() == cells[i].getY()) {
13307                 // on same row.
13308                 crow.end = cells[i];
13309                 continue;
13310             }
13311             // different row.
13312             rows.push(crow);
13313             crow = {
13314                 start: cells[i],
13315                 end : cells[i]
13316             };
13317             
13318         }
13319         
13320         rows.push(crow);
13321         ev.els = [];
13322         ev.rows = rows;
13323         ev.cells = cells;
13324         
13325         cells[0].events.push(ev);
13326         
13327         this.calevents.push(ev);
13328     },
13329     
13330     clearEvents: function() {
13331         
13332         if(!this.calevents){
13333             return;
13334         }
13335         
13336         Roo.each(this.cells.elements, function(c){
13337             c.row = 0;
13338             c.events = [];
13339             c.more = [];
13340         });
13341         
13342         Roo.each(this.calevents, function(e) {
13343             Roo.each(e.els, function(el) {
13344                 el.un('mouseenter' ,this.onEventEnter, this);
13345                 el.un('mouseleave' ,this.onEventLeave, this);
13346                 el.remove();
13347             },this);
13348         },this);
13349         
13350         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13351             e.remove();
13352         });
13353         
13354     },
13355     
13356     renderEvents: function()
13357     {   
13358         var _this = this;
13359         
13360         this.cells.each(function(c) {
13361             
13362             if(c.row < 5){
13363                 return;
13364             }
13365             
13366             var ev = c.events;
13367             
13368             var r = 4;
13369             if(c.row != c.events.length){
13370                 r = 4 - (4 - (c.row - c.events.length));
13371             }
13372             
13373             c.events = ev.slice(0, r);
13374             c.more = ev.slice(r);
13375             
13376             if(c.more.length && c.more.length == 1){
13377                 c.events.push(c.more.pop());
13378             }
13379             
13380             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13381             
13382         });
13383             
13384         this.cells.each(function(c) {
13385             
13386             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13387             
13388             
13389             for (var e = 0; e < c.events.length; e++){
13390                 var ev = c.events[e];
13391                 var rows = ev.rows;
13392                 
13393                 for(var i = 0; i < rows.length; i++) {
13394                 
13395                     // how many rows should it span..
13396
13397                     var  cfg = {
13398                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13399                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13400
13401                         unselectable : "on",
13402                         cn : [
13403                             {
13404                                 cls: 'fc-event-inner',
13405                                 cn : [
13406     //                                {
13407     //                                  tag:'span',
13408     //                                  cls: 'fc-event-time',
13409     //                                  html : cells.length > 1 ? '' : ev.time
13410     //                                },
13411                                     {
13412                                       tag:'span',
13413                                       cls: 'fc-event-title',
13414                                       html : String.format('{0}', ev.title)
13415                                     }
13416
13417
13418                                 ]
13419                             },
13420                             {
13421                                 cls: 'ui-resizable-handle ui-resizable-e',
13422                                 html : '&nbsp;&nbsp;&nbsp'
13423                             }
13424
13425                         ]
13426                     };
13427
13428                     if (i == 0) {
13429                         cfg.cls += ' fc-event-start';
13430                     }
13431                     if ((i+1) == rows.length) {
13432                         cfg.cls += ' fc-event-end';
13433                     }
13434
13435                     var ctr = _this.el.select('.fc-event-container',true).first();
13436                     var cg = ctr.createChild(cfg);
13437
13438                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13439                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13440
13441                     var r = (c.more.length) ? 1 : 0;
13442                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13443                     cg.setWidth(ebox.right - sbox.x -2);
13444
13445                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13446                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13447                     cg.on('click', _this.onEventClick, _this, ev);
13448
13449                     ev.els.push(cg);
13450                     
13451                 }
13452                 
13453             }
13454             
13455             
13456             if(c.more.length){
13457                 var  cfg = {
13458                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13459                     style : 'position: absolute',
13460                     unselectable : "on",
13461                     cn : [
13462                         {
13463                             cls: 'fc-event-inner',
13464                             cn : [
13465                                 {
13466                                   tag:'span',
13467                                   cls: 'fc-event-title',
13468                                   html : 'More'
13469                                 }
13470
13471
13472                             ]
13473                         },
13474                         {
13475                             cls: 'ui-resizable-handle ui-resizable-e',
13476                             html : '&nbsp;&nbsp;&nbsp'
13477                         }
13478
13479                     ]
13480                 };
13481
13482                 var ctr = _this.el.select('.fc-event-container',true).first();
13483                 var cg = ctr.createChild(cfg);
13484
13485                 var sbox = c.select('.fc-day-content',true).first().getBox();
13486                 var ebox = c.select('.fc-day-content',true).first().getBox();
13487                 //Roo.log(cg);
13488                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13489                 cg.setWidth(ebox.right - sbox.x -2);
13490
13491                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13492                 
13493             }
13494             
13495         });
13496         
13497         
13498         
13499     },
13500     
13501     onEventEnter: function (e, el,event,d) {
13502         this.fireEvent('evententer', this, el, event);
13503     },
13504     
13505     onEventLeave: function (e, el,event,d) {
13506         this.fireEvent('eventleave', this, el, event);
13507     },
13508     
13509     onEventClick: function (e, el,event,d) {
13510         this.fireEvent('eventclick', this, el, event);
13511     },
13512     
13513     onMonthChange: function () {
13514         this.store.load();
13515     },
13516     
13517     onMoreEventClick: function(e, el, more)
13518     {
13519         var _this = this;
13520         
13521         this.calpopover.placement = 'right';
13522         this.calpopover.setTitle('More');
13523         
13524         this.calpopover.setContent('');
13525         
13526         var ctr = this.calpopover.el.select('.popover-content', true).first();
13527         
13528         Roo.each(more, function(m){
13529             var cfg = {
13530                 cls : 'fc-event-hori fc-event-draggable',
13531                 html : m.title
13532             }
13533             var cg = ctr.createChild(cfg);
13534             
13535             cg.on('click', _this.onEventClick, _this, m);
13536         });
13537         
13538         this.calpopover.show(el);
13539         
13540         
13541     },
13542     
13543     onLoad: function () 
13544     {   
13545         this.calevents = [];
13546         var cal = this;
13547         
13548         if(this.store.getCount() > 0){
13549             this.store.data.each(function(d){
13550                cal.addItem({
13551                     id : d.data.id,
13552                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13553                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13554                     time : d.data.start_time,
13555                     title : d.data.title,
13556                     description : d.data.description,
13557                     venue : d.data.venue
13558                 });
13559             });
13560         }
13561         
13562         this.renderEvents();
13563         
13564         if(this.calevents.length && this.loadMask){
13565             this.maskEl.hide();
13566         }
13567     },
13568     
13569     onBeforeLoad: function()
13570     {
13571         this.clearEvents();
13572         if(this.loadMask){
13573             this.maskEl.show();
13574         }
13575     }
13576 });
13577
13578  
13579  /*
13580  * - LGPL
13581  *
13582  * element
13583  * 
13584  */
13585
13586 /**
13587  * @class Roo.bootstrap.Popover
13588  * @extends Roo.bootstrap.Component
13589  * Bootstrap Popover class
13590  * @cfg {String} html contents of the popover   (or false to use children..)
13591  * @cfg {String} title of popover (or false to hide)
13592  * @cfg {String} placement how it is placed
13593  * @cfg {String} trigger click || hover (or false to trigger manually)
13594  * @cfg {String} over what (parent or false to trigger manually.)
13595  * 
13596  * @constructor
13597  * Create a new Popover
13598  * @param {Object} config The config object
13599  */
13600
13601 Roo.bootstrap.Popover = function(config){
13602     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13603 };
13604
13605 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13606     
13607     title: 'Fill in a title',
13608     html: false,
13609     
13610     placement : 'right',
13611     trigger : 'hover', // hover
13612     
13613     over: 'parent',
13614     
13615     can_build_overlaid : false,
13616     
13617     getChildContainer : function()
13618     {
13619         return this.el.select('.popover-content',true).first();
13620     },
13621     
13622     getAutoCreate : function(){
13623          Roo.log('make popover?');
13624         var cfg = {
13625            cls : 'popover roo-dynamic',
13626            style: 'display:block',
13627            cn : [
13628                 {
13629                     cls : 'arrow'
13630                 },
13631                 {
13632                     cls : 'popover-inner',
13633                     cn : [
13634                         {
13635                             tag: 'h3',
13636                             cls: 'popover-title',
13637                             html : this.title
13638                         },
13639                         {
13640                             cls : 'popover-content',
13641                             html : this.html
13642                         }
13643                     ]
13644                     
13645                 }
13646            ]
13647         };
13648         
13649         return cfg;
13650     },
13651     setTitle: function(str)
13652     {
13653         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13654     },
13655     setContent: function(str)
13656     {
13657         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13658     },
13659     // as it get's added to the bottom of the page.
13660     onRender : function(ct, position)
13661     {
13662         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13663         if(!this.el){
13664             var cfg = Roo.apply({},  this.getAutoCreate());
13665             cfg.id = Roo.id();
13666             
13667             if (this.cls) {
13668                 cfg.cls += ' ' + this.cls;
13669             }
13670             if (this.style) {
13671                 cfg.style = this.style;
13672             }
13673             Roo.log("adding to ")
13674             this.el = Roo.get(document.body).createChild(cfg, position);
13675             Roo.log(this.el);
13676         }
13677         this.initEvents();
13678     },
13679     
13680     initEvents : function()
13681     {
13682         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13683         this.el.enableDisplayMode('block');
13684         this.el.hide();
13685         if (this.over === false) {
13686             return; 
13687         }
13688         if (this.triggers === false) {
13689             return;
13690         }
13691         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13692         var triggers = this.trigger ? this.trigger.split(' ') : [];
13693         Roo.each(triggers, function(trigger) {
13694         
13695             if (trigger == 'click') {
13696                 on_el.on('click', this.toggle, this);
13697             } else if (trigger != 'manual') {
13698                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13699                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13700       
13701                 on_el.on(eventIn  ,this.enter, this);
13702                 on_el.on(eventOut, this.leave, this);
13703             }
13704         }, this);
13705         
13706     },
13707     
13708     
13709     // private
13710     timeout : null,
13711     hoverState : null,
13712     
13713     toggle : function () {
13714         this.hoverState == 'in' ? this.leave() : this.enter();
13715     },
13716     
13717     enter : function () {
13718        
13719     
13720         clearTimeout(this.timeout);
13721     
13722         this.hoverState = 'in'
13723     
13724         if (!this.delay || !this.delay.show) {
13725             this.show();
13726             return 
13727         }
13728         var _t = this;
13729         this.timeout = setTimeout(function () {
13730             if (_t.hoverState == 'in') {
13731                 _t.show();
13732             }
13733         }, this.delay.show)
13734     },
13735     leave : function() {
13736         clearTimeout(this.timeout);
13737     
13738         this.hoverState = 'out'
13739     
13740         if (!this.delay || !this.delay.hide) {
13741             this.hide();
13742             return 
13743         }
13744         var _t = this;
13745         this.timeout = setTimeout(function () {
13746             if (_t.hoverState == 'out') {
13747                 _t.hide();
13748             }
13749         }, this.delay.hide)
13750     },
13751     
13752     show : function (on_el)
13753     {
13754         if (!on_el) {
13755             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13756         }
13757         // set content.
13758         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13759         if (this.html !== false) {
13760             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13761         }
13762         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13763         if (!this.title.length) {
13764             this.el.select('.popover-title',true).hide();
13765         }
13766         
13767         var placement = typeof this.placement == 'function' ?
13768             this.placement.call(this, this.el, on_el) :
13769             this.placement;
13770             
13771         var autoToken = /\s?auto?\s?/i;
13772         var autoPlace = autoToken.test(placement);
13773         if (autoPlace) {
13774             placement = placement.replace(autoToken, '') || 'top';
13775         }
13776         
13777         //this.el.detach()
13778         //this.el.setXY([0,0]);
13779         this.el.show();
13780         this.el.dom.style.display='block';
13781         this.el.addClass(placement);
13782         
13783         //this.el.appendTo(on_el);
13784         
13785         var p = this.getPosition();
13786         var box = this.el.getBox();
13787         
13788         if (autoPlace) {
13789             // fixme..
13790         }
13791         var align = Roo.bootstrap.Popover.alignment[placement]
13792         this.el.alignTo(on_el, align[0],align[1]);
13793         //var arrow = this.el.select('.arrow',true).first();
13794         //arrow.set(align[2], 
13795         
13796         this.el.addClass('in');
13797         this.hoverState = null;
13798         
13799         if (this.el.hasClass('fade')) {
13800             // fade it?
13801         }
13802         
13803     },
13804     hide : function()
13805     {
13806         this.el.setXY([0,0]);
13807         this.el.removeClass('in');
13808         this.el.hide();
13809         
13810     }
13811     
13812 });
13813
13814 Roo.bootstrap.Popover.alignment = {
13815     'left' : ['r-l', [-10,0], 'right'],
13816     'right' : ['l-r', [10,0], 'left'],
13817     'bottom' : ['t-b', [0,10], 'top'],
13818     'top' : [ 'b-t', [0,-10], 'bottom']
13819 };
13820
13821  /*
13822  * - LGPL
13823  *
13824  * Progress
13825  * 
13826  */
13827
13828 /**
13829  * @class Roo.bootstrap.Progress
13830  * @extends Roo.bootstrap.Component
13831  * Bootstrap Progress class
13832  * @cfg {Boolean} striped striped of the progress bar
13833  * @cfg {Boolean} active animated of the progress bar
13834  * 
13835  * 
13836  * @constructor
13837  * Create a new Progress
13838  * @param {Object} config The config object
13839  */
13840
13841 Roo.bootstrap.Progress = function(config){
13842     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13843 };
13844
13845 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13846     
13847     striped : false,
13848     active: false,
13849     
13850     getAutoCreate : function(){
13851         var cfg = {
13852             tag: 'div',
13853             cls: 'progress'
13854         };
13855         
13856         
13857         if(this.striped){
13858             cfg.cls += ' progress-striped';
13859         }
13860       
13861         if(this.active){
13862             cfg.cls += ' active';
13863         }
13864         
13865         
13866         return cfg;
13867     }
13868    
13869 });
13870
13871  
13872
13873  /*
13874  * - LGPL
13875  *
13876  * ProgressBar
13877  * 
13878  */
13879
13880 /**
13881  * @class Roo.bootstrap.ProgressBar
13882  * @extends Roo.bootstrap.Component
13883  * Bootstrap ProgressBar class
13884  * @cfg {Number} aria_valuenow aria-value now
13885  * @cfg {Number} aria_valuemin aria-value min
13886  * @cfg {Number} aria_valuemax aria-value max
13887  * @cfg {String} label label for the progress bar
13888  * @cfg {String} panel (success | info | warning | danger )
13889  * @cfg {String} role role of the progress bar
13890  * @cfg {String} sr_only text
13891  * 
13892  * 
13893  * @constructor
13894  * Create a new ProgressBar
13895  * @param {Object} config The config object
13896  */
13897
13898 Roo.bootstrap.ProgressBar = function(config){
13899     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13900 };
13901
13902 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13903     
13904     aria_valuenow : 0,
13905     aria_valuemin : 0,
13906     aria_valuemax : 100,
13907     label : false,
13908     panel : false,
13909     role : false,
13910     sr_only: false,
13911     
13912     getAutoCreate : function()
13913     {
13914         
13915         var cfg = {
13916             tag: 'div',
13917             cls: 'progress-bar',
13918             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13919         };
13920         
13921         if(this.sr_only){
13922             cfg.cn = {
13923                 tag: 'span',
13924                 cls: 'sr-only',
13925                 html: this.sr_only
13926             }
13927         }
13928         
13929         if(this.role){
13930             cfg.role = this.role;
13931         }
13932         
13933         if(this.aria_valuenow){
13934             cfg['aria-valuenow'] = this.aria_valuenow;
13935         }
13936         
13937         if(this.aria_valuemin){
13938             cfg['aria-valuemin'] = this.aria_valuemin;
13939         }
13940         
13941         if(this.aria_valuemax){
13942             cfg['aria-valuemax'] = this.aria_valuemax;
13943         }
13944         
13945         if(this.label && !this.sr_only){
13946             cfg.html = this.label;
13947         }
13948         
13949         if(this.panel){
13950             cfg.cls += ' progress-bar-' + this.panel;
13951         }
13952         
13953         return cfg;
13954     },
13955     
13956     update : function(aria_valuenow)
13957     {
13958         this.aria_valuenow = aria_valuenow;
13959         
13960         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13961     }
13962    
13963 });
13964
13965  
13966
13967  /*
13968  * - LGPL
13969  *
13970  * column
13971  * 
13972  */
13973
13974 /**
13975  * @class Roo.bootstrap.TabGroup
13976  * @extends Roo.bootstrap.Column
13977  * Bootstrap Column class
13978  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13979  * @cfg {Boolean} carousel true to make the group behave like a carousel
13980  * 
13981  * @constructor
13982  * Create a new TabGroup
13983  * @param {Object} config The config object
13984  */
13985
13986 Roo.bootstrap.TabGroup = function(config){
13987     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13988     if (!this.navId) {
13989         this.navId = Roo.id();
13990     }
13991     this.tabs = [];
13992     Roo.bootstrap.TabGroup.register(this);
13993     
13994 };
13995
13996 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13997     
13998     carousel : false,
13999     transition : false,
14000      
14001     getAutoCreate : function()
14002     {
14003         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14004         
14005         cfg.cls += ' tab-content';
14006         
14007         if (this.carousel) {
14008             cfg.cls += ' carousel slide';
14009             cfg.cn = [{
14010                cls : 'carousel-inner'
14011             }]
14012         }
14013         
14014         
14015         return cfg;
14016     },
14017     getChildContainer : function()
14018     {
14019         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14020     },
14021     
14022     /**
14023     * register a Navigation item
14024     * @param {Roo.bootstrap.NavItem} the navitem to add
14025     */
14026     register : function(item)
14027     {
14028         this.tabs.push( item);
14029         item.navId = this.navId; // not really needed..
14030     
14031     },
14032     
14033     getActivePanel : function()
14034     {
14035         var r = false;
14036         Roo.each(this.tabs, function(t) {
14037             if (t.active) {
14038                 r = t;
14039                 return false;
14040             }
14041             return null;
14042         });
14043         return r;
14044         
14045     },
14046     getPanelByName : function(n)
14047     {
14048         var r = false;
14049         Roo.each(this.tabs, function(t) {
14050             if (t.tabId == n) {
14051                 r = t;
14052                 return false;
14053             }
14054             return null;
14055         });
14056         return r;
14057     },
14058     indexOfPanel : function(p)
14059     {
14060         var r = false;
14061         Roo.each(this.tabs, function(t,i) {
14062             if (t.tabId == p.tabId) {
14063                 r = i;
14064                 return false;
14065             }
14066             return null;
14067         });
14068         return r;
14069     },
14070     /**
14071      * show a specific panel
14072      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14073      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14074      */
14075     showPanel : function (pan)
14076     {
14077         
14078         if (typeof(pan) == 'number') {
14079             pan = this.tabs[pan];
14080         }
14081         if (typeof(pan) == 'string') {
14082             pan = this.getPanelByName(pan);
14083         }
14084         if (pan.tabId == this.getActivePanel().tabId) {
14085             return true;
14086         }
14087         var cur = this.getActivePanel();
14088         
14089         if (false === cur.fireEvent('beforedeactivate')) {
14090             return false;
14091         }
14092         
14093         if (this.carousel) {
14094             this.transition = true;
14095             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14096             var lr = dir == 'next' ? 'left' : 'right';
14097             pan.el.addClass(dir); // or prev
14098             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14099             cur.el.addClass(lr); // or right
14100             pan.el.addClass(lr);
14101             
14102             var _this = this;
14103             cur.el.on('transitionend', function() {
14104                 Roo.log("trans end?");
14105                 
14106                 pan.el.removeClass([lr,dir]);
14107                 pan.setActive(true);
14108                 
14109                 cur.el.removeClass([lr]);
14110                 cur.setActive(false);
14111                 
14112                 _this.transition = false;
14113                 
14114             }, this, { single:  true } );
14115             return true;
14116         }
14117         
14118         cur.setActive(false);
14119         pan.setActive(true);
14120         return true;
14121         
14122     },
14123     showPanelNext : function()
14124     {
14125         var i = this.indexOfPanel(this.getActivePanel());
14126         if (i > this.tabs.length) {
14127             return;
14128         }
14129         this.showPanel(this.tabs[i+1]);
14130     },
14131     showPanelPrev : function()
14132     {
14133         var i = this.indexOfPanel(this.getActivePanel());
14134         if (i  < 1) {
14135             return;
14136         }
14137         this.showPanel(this.tabs[i-1]);
14138     }
14139     
14140     
14141   
14142 });
14143
14144  
14145
14146  
14147  
14148 Roo.apply(Roo.bootstrap.TabGroup, {
14149     
14150     groups: {},
14151      /**
14152     * register a Navigation Group
14153     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14154     */
14155     register : function(navgrp)
14156     {
14157         this.groups[navgrp.navId] = navgrp;
14158         
14159     },
14160     /**
14161     * fetch a Navigation Group based on the navigation ID
14162     * if one does not exist , it will get created.
14163     * @param {string} the navgroup to add
14164     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14165     */
14166     get: function(navId) {
14167         if (typeof(this.groups[navId]) == 'undefined') {
14168             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14169         }
14170         return this.groups[navId] ;
14171     }
14172     
14173     
14174     
14175 });
14176
14177  /*
14178  * - LGPL
14179  *
14180  * TabPanel
14181  * 
14182  */
14183
14184 /**
14185  * @class Roo.bootstrap.TabPanel
14186  * @extends Roo.bootstrap.Component
14187  * Bootstrap TabPanel class
14188  * @cfg {Boolean} active panel active
14189  * @cfg {String} html panel content
14190  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14191  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14192  * 
14193  * 
14194  * @constructor
14195  * Create a new TabPanel
14196  * @param {Object} config The config object
14197  */
14198
14199 Roo.bootstrap.TabPanel = function(config){
14200     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14201     this.addEvents({
14202         /**
14203              * @event changed
14204              * Fires when the active status changes
14205              * @param {Roo.bootstrap.TabPanel} this
14206              * @param {Boolean} state the new state
14207             
14208          */
14209         'changed': true,
14210         /**
14211              * @event beforedeactivate
14212              * Fires before a tab is de-activated - can be used to do validation on a form.
14213              * @param {Roo.bootstrap.TabPanel} this
14214              * @return {Boolean} false if there is an error
14215             
14216          */
14217         'beforedeactivate': true
14218      });
14219     
14220     this.tabId = this.tabId || Roo.id();
14221   
14222 };
14223
14224 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14225     
14226     active: false,
14227     html: false,
14228     tabId: false,
14229     navId : false,
14230     
14231     getAutoCreate : function(){
14232         var cfg = {
14233             tag: 'div',
14234             // item is needed for carousel - not sure if it has any effect otherwise
14235             cls: 'tab-pane item',
14236             html: this.html || ''
14237         };
14238         
14239         if(this.active){
14240             cfg.cls += ' active';
14241         }
14242         
14243         if(this.tabId){
14244             cfg.tabId = this.tabId;
14245         }
14246         
14247         
14248         return cfg;
14249     },
14250     
14251     initEvents:  function()
14252     {
14253         Roo.log('-------- init events on tab panel ---------');
14254         
14255         var p = this.parent();
14256         this.navId = this.navId || p.navId;
14257         
14258         if (typeof(this.navId) != 'undefined') {
14259             // not really needed.. but just in case.. parent should be a NavGroup.
14260             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14261             Roo.log(['register', tg, this]);
14262             tg.register(this);
14263         }
14264     },
14265     
14266     
14267     onRender : function(ct, position)
14268     {
14269        // Roo.log("Call onRender: " + this.xtype);
14270         
14271         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14272         
14273         
14274         
14275         
14276         
14277     },
14278     
14279     setActive: function(state)
14280     {
14281         Roo.log("panel - set active " + this.tabId + "=" + state);
14282         
14283         this.active = state;
14284         if (!state) {
14285             this.el.removeClass('active');
14286             
14287         } else  if (!this.el.hasClass('active')) {
14288             this.el.addClass('active');
14289         }
14290         this.fireEvent('changed', this, state);
14291     }
14292     
14293     
14294 });
14295  
14296
14297  
14298
14299  /*
14300  * - LGPL
14301  *
14302  * DateField
14303  * 
14304  */
14305
14306 /**
14307  * @class Roo.bootstrap.DateField
14308  * @extends Roo.bootstrap.Input
14309  * Bootstrap DateField class
14310  * @cfg {Number} weekStart default 0
14311  * @cfg {Number} weekStart default 0
14312  * @cfg {Number} viewMode default empty, (months|years)
14313  * @cfg {Number} minViewMode default empty, (months|years)
14314  * @cfg {Number} startDate default -Infinity
14315  * @cfg {Number} endDate default Infinity
14316  * @cfg {Boolean} todayHighlight default false
14317  * @cfg {Boolean} todayBtn default false
14318  * @cfg {Boolean} calendarWeeks default false
14319  * @cfg {Object} daysOfWeekDisabled default empty
14320  * 
14321  * @cfg {Boolean} keyboardNavigation default true
14322  * @cfg {String} language default en
14323  * 
14324  * @constructor
14325  * Create a new DateField
14326  * @param {Object} config The config object
14327  */
14328
14329 Roo.bootstrap.DateField = function(config){
14330     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14331      this.addEvents({
14332             /**
14333              * @event show
14334              * Fires when this field show.
14335              * @param {Roo.bootstrap.DateField} this
14336              * @param {Mixed} date The date value
14337              */
14338             show : true,
14339             /**
14340              * @event show
14341              * Fires when this field hide.
14342              * @param {Roo.bootstrap.DateField} this
14343              * @param {Mixed} date The date value
14344              */
14345             hide : true,
14346             /**
14347              * @event select
14348              * Fires when select a date.
14349              * @param {Roo.bootstrap.DateField} this
14350              * @param {Mixed} date The date value
14351              */
14352             select : true
14353         });
14354 };
14355
14356 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14357     
14358     /**
14359      * @cfg {String} format
14360      * The default date format string which can be overriden for localization support.  The format must be
14361      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14362      */
14363     format : "m/d/y",
14364     /**
14365      * @cfg {String} altFormats
14366      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14367      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14368      */
14369     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14370     
14371     weekStart : 0,
14372     
14373     viewMode : '',
14374     
14375     minViewMode : '',
14376     
14377     todayHighlight : false,
14378     
14379     todayBtn: false,
14380     
14381     language: 'en',
14382     
14383     keyboardNavigation: true,
14384     
14385     calendarWeeks: false,
14386     
14387     startDate: -Infinity,
14388     
14389     endDate: Infinity,
14390     
14391     daysOfWeekDisabled: [],
14392     
14393     _events: [],
14394     
14395     UTCDate: function()
14396     {
14397         return new Date(Date.UTC.apply(Date, arguments));
14398     },
14399     
14400     UTCToday: function()
14401     {
14402         var today = new Date();
14403         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14404     },
14405     
14406     getDate: function() {
14407             var d = this.getUTCDate();
14408             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14409     },
14410     
14411     getUTCDate: function() {
14412             return this.date;
14413     },
14414     
14415     setDate: function(d) {
14416             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14417     },
14418     
14419     setUTCDate: function(d) {
14420             this.date = d;
14421             this.setValue(this.formatDate(this.date));
14422     },
14423         
14424     onRender: function(ct, position)
14425     {
14426         
14427         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14428         
14429         this.language = this.language || 'en';
14430         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14431         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14432         
14433         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14434         this.format = this.format || 'm/d/y';
14435         this.isInline = false;
14436         this.isInput = true;
14437         this.component = this.el.select('.add-on', true).first() || false;
14438         this.component = (this.component && this.component.length === 0) ? false : this.component;
14439         this.hasInput = this.component && this.inputEL().length;
14440         
14441         if (typeof(this.minViewMode === 'string')) {
14442             switch (this.minViewMode) {
14443                 case 'months':
14444                     this.minViewMode = 1;
14445                     break;
14446                 case 'years':
14447                     this.minViewMode = 2;
14448                     break;
14449                 default:
14450                     this.minViewMode = 0;
14451                     break;
14452             }
14453         }
14454         
14455         if (typeof(this.viewMode === 'string')) {
14456             switch (this.viewMode) {
14457                 case 'months':
14458                     this.viewMode = 1;
14459                     break;
14460                 case 'years':
14461                     this.viewMode = 2;
14462                     break;
14463                 default:
14464                     this.viewMode = 0;
14465                     break;
14466             }
14467         }
14468                 
14469         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14470         
14471 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14472         
14473         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14474         
14475         this.picker().on('mousedown', this.onMousedown, this);
14476         this.picker().on('click', this.onClick, this);
14477         
14478         this.picker().addClass('datepicker-dropdown');
14479         
14480         this.startViewMode = this.viewMode;
14481         
14482         
14483         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14484             if(!this.calendarWeeks){
14485                 v.remove();
14486                 return;
14487             };
14488             
14489             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14490             v.attr('colspan', function(i, val){
14491                 return parseInt(val) + 1;
14492             });
14493         })
14494                         
14495         
14496         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14497         
14498         this.setStartDate(this.startDate);
14499         this.setEndDate(this.endDate);
14500         
14501         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14502         
14503         this.fillDow();
14504         this.fillMonths();
14505         this.update();
14506         this.showMode();
14507         
14508         if(this.isInline) {
14509             this.show();
14510         }
14511     },
14512     
14513     picker : function()
14514     {
14515         return this.pickerEl;
14516 //        return this.el.select('.datepicker', true).first();
14517     },
14518     
14519     fillDow: function()
14520     {
14521         var dowCnt = this.weekStart;
14522         
14523         var dow = {
14524             tag: 'tr',
14525             cn: [
14526                 
14527             ]
14528         };
14529         
14530         if(this.calendarWeeks){
14531             dow.cn.push({
14532                 tag: 'th',
14533                 cls: 'cw',
14534                 html: '&nbsp;'
14535             })
14536         }
14537         
14538         while (dowCnt < this.weekStart + 7) {
14539             dow.cn.push({
14540                 tag: 'th',
14541                 cls: 'dow',
14542                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14543             });
14544         }
14545         
14546         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14547     },
14548     
14549     fillMonths: function()
14550     {    
14551         var i = 0
14552         var months = this.picker().select('>.datepicker-months td', true).first();
14553         
14554         months.dom.innerHTML = '';
14555         
14556         while (i < 12) {
14557             var month = {
14558                 tag: 'span',
14559                 cls: 'month',
14560                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14561             }
14562             
14563             months.createChild(month);
14564         }
14565         
14566     },
14567     
14568     update: function()
14569     {
14570         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;
14571         
14572         if (this.date < this.startDate) {
14573             this.viewDate = new Date(this.startDate);
14574         } else if (this.date > this.endDate) {
14575             this.viewDate = new Date(this.endDate);
14576         } else {
14577             this.viewDate = new Date(this.date);
14578         }
14579         
14580         this.fill();
14581     },
14582     
14583     fill: function() 
14584     {
14585         var d = new Date(this.viewDate),
14586                 year = d.getUTCFullYear(),
14587                 month = d.getUTCMonth(),
14588                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14589                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14590                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14591                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14592                 currentDate = this.date && this.date.valueOf(),
14593                 today = this.UTCToday();
14594         
14595         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14596         
14597 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14598         
14599 //        this.picker.select('>tfoot th.today').
14600 //                                              .text(dates[this.language].today)
14601 //                                              .toggle(this.todayBtn !== false);
14602     
14603         this.updateNavArrows();
14604         this.fillMonths();
14605                                                 
14606         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14607         
14608         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14609          
14610         prevMonth.setUTCDate(day);
14611         
14612         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14613         
14614         var nextMonth = new Date(prevMonth);
14615         
14616         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14617         
14618         nextMonth = nextMonth.valueOf();
14619         
14620         var fillMonths = false;
14621         
14622         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14623         
14624         while(prevMonth.valueOf() < nextMonth) {
14625             var clsName = '';
14626             
14627             if (prevMonth.getUTCDay() === this.weekStart) {
14628                 if(fillMonths){
14629                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14630                 }
14631                     
14632                 fillMonths = {
14633                     tag: 'tr',
14634                     cn: []
14635                 };
14636                 
14637                 if(this.calendarWeeks){
14638                     // ISO 8601: First week contains first thursday.
14639                     // ISO also states week starts on Monday, but we can be more abstract here.
14640                     var
14641                     // Start of current week: based on weekstart/current date
14642                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14643                     // Thursday of this week
14644                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14645                     // First Thursday of year, year from thursday
14646                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14647                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14648                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14649                     
14650                     fillMonths.cn.push({
14651                         tag: 'td',
14652                         cls: 'cw',
14653                         html: calWeek
14654                     });
14655                 }
14656             }
14657             
14658             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14659                 clsName += ' old';
14660             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14661                 clsName += ' new';
14662             }
14663             if (this.todayHighlight &&
14664                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14665                 prevMonth.getUTCMonth() == today.getMonth() &&
14666                 prevMonth.getUTCDate() == today.getDate()) {
14667                 clsName += ' today';
14668             }
14669             
14670             if (currentDate && prevMonth.valueOf() === currentDate) {
14671                 clsName += ' active';
14672             }
14673             
14674             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14675                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14676                     clsName += ' disabled';
14677             }
14678             
14679             fillMonths.cn.push({
14680                 tag: 'td',
14681                 cls: 'day ' + clsName,
14682                 html: prevMonth.getDate()
14683             })
14684             
14685             prevMonth.setDate(prevMonth.getDate()+1);
14686         }
14687           
14688         var currentYear = this.date && this.date.getUTCFullYear();
14689         var currentMonth = this.date && this.date.getUTCMonth();
14690         
14691         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14692         
14693         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14694             v.removeClass('active');
14695             
14696             if(currentYear === year && k === currentMonth){
14697                 v.addClass('active');
14698             }
14699             
14700             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14701                 v.addClass('disabled');
14702             }
14703             
14704         });
14705         
14706         
14707         year = parseInt(year/10, 10) * 10;
14708         
14709         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14710         
14711         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14712         
14713         year -= 1;
14714         for (var i = -1; i < 11; i++) {
14715             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14716                 tag: 'span',
14717                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14718                 html: year
14719             })
14720             
14721             year += 1;
14722         }
14723     },
14724     
14725     showMode: function(dir) 
14726     {
14727         if (dir) {
14728             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14729         }
14730         Roo.each(this.picker().select('>div',true).elements, function(v){
14731             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14732             v.hide();
14733         });
14734         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14735     },
14736     
14737     place: function()
14738     {
14739         if(this.isInline) return;
14740         
14741         this.picker().removeClass(['bottom', 'top']);
14742         
14743         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14744             /*
14745              * place to the top of element!
14746              *
14747              */
14748             
14749             this.picker().addClass('top');
14750             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14751             
14752             return;
14753         }
14754         
14755         this.picker().addClass('bottom');
14756         
14757         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14758     },
14759     
14760     parseDate : function(value)
14761     {
14762         if(!value || value instanceof Date){
14763             return value;
14764         }
14765         var v = Date.parseDate(value, this.format);
14766         if (!v && this.useIso) {
14767             v = Date.parseDate(value, 'Y-m-d');
14768         }
14769         if(!v && this.altFormats){
14770             if(!this.altFormatsArray){
14771                 this.altFormatsArray = this.altFormats.split("|");
14772             }
14773             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14774                 v = Date.parseDate(value, this.altFormatsArray[i]);
14775             }
14776         }
14777         return v;
14778     },
14779     
14780     formatDate : function(date, fmt)
14781     {
14782         return (!date || !(date instanceof Date)) ?
14783         date : date.dateFormat(fmt || this.format);
14784     },
14785     
14786     onFocus : function()
14787     {
14788         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14789         this.show();
14790     },
14791     
14792     onBlur : function()
14793     {
14794         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14795         
14796         var d = this.inputEl().getValue();
14797         
14798         this.setValue(d);
14799                 
14800         this.hide();
14801     },
14802     
14803     show : function()
14804     {
14805         this.picker().show();
14806         this.update();
14807         this.place();
14808         
14809         this.fireEvent('show', this, this.date);
14810     },
14811     
14812     hide : function()
14813     {
14814         if(this.isInline) return;
14815         this.picker().hide();
14816         this.viewMode = this.startViewMode;
14817         this.showMode();
14818         
14819         this.fireEvent('hide', this, this.date);
14820         
14821     },
14822     
14823     onMousedown: function(e)
14824     {
14825         e.stopPropagation();
14826         e.preventDefault();
14827     },
14828     
14829     keyup: function(e)
14830     {
14831         Roo.bootstrap.DateField.superclass.keyup.call(this);
14832         this.update();
14833     },
14834
14835     setValue: function(v)
14836     {
14837         var d = new Date(v).clearTime();
14838         
14839         if(isNaN(d.getTime())){
14840             this.date = this.viewDate = '';
14841             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14842             return;
14843         }
14844         
14845         v = this.formatDate(d);
14846         
14847         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14848         
14849         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14850      
14851         this.update();
14852
14853         this.fireEvent('select', this, this.date);
14854         
14855     },
14856     
14857     getValue: function()
14858     {
14859         return this.formatDate(this.date);
14860     },
14861     
14862     fireKey: function(e)
14863     {
14864         if (!this.picker().isVisible()){
14865             if (e.keyCode == 27) // allow escape to hide and re-show picker
14866                 this.show();
14867             return;
14868         }
14869         
14870         var dateChanged = false,
14871         dir, day, month,
14872         newDate, newViewDate;
14873         
14874         switch(e.keyCode){
14875             case 27: // escape
14876                 this.hide();
14877                 e.preventDefault();
14878                 break;
14879             case 37: // left
14880             case 39: // right
14881                 if (!this.keyboardNavigation) break;
14882                 dir = e.keyCode == 37 ? -1 : 1;
14883                 
14884                 if (e.ctrlKey){
14885                     newDate = this.moveYear(this.date, dir);
14886                     newViewDate = this.moveYear(this.viewDate, dir);
14887                 } else if (e.shiftKey){
14888                     newDate = this.moveMonth(this.date, dir);
14889                     newViewDate = this.moveMonth(this.viewDate, dir);
14890                 } else {
14891                     newDate = new Date(this.date);
14892                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14893                     newViewDate = new Date(this.viewDate);
14894                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14895                 }
14896                 if (this.dateWithinRange(newDate)){
14897                     this.date = newDate;
14898                     this.viewDate = newViewDate;
14899                     this.setValue(this.formatDate(this.date));
14900 //                    this.update();
14901                     e.preventDefault();
14902                     dateChanged = true;
14903                 }
14904                 break;
14905             case 38: // up
14906             case 40: // down
14907                 if (!this.keyboardNavigation) break;
14908                 dir = e.keyCode == 38 ? -1 : 1;
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 * 7);
14918                     newViewDate = new Date(this.viewDate);
14919                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
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 13: // enter
14931                 this.setValue(this.formatDate(this.date));
14932                 this.hide();
14933                 e.preventDefault();
14934                 break;
14935             case 9: // tab
14936                 this.setValue(this.formatDate(this.date));
14937                 this.hide();
14938                 break;
14939             case 16: // shift
14940             case 17: // ctrl
14941             case 18: // alt
14942                 break;
14943             default :
14944                 this.hide();
14945                 
14946         }
14947     },
14948     
14949     
14950     onClick: function(e) 
14951     {
14952         e.stopPropagation();
14953         e.preventDefault();
14954         
14955         var target = e.getTarget();
14956         
14957         if(target.nodeName.toLowerCase() === 'i'){
14958             target = Roo.get(target).dom.parentNode;
14959         }
14960         
14961         var nodeName = target.nodeName;
14962         var className = target.className;
14963         var html = target.innerHTML;
14964         
14965         switch(nodeName.toLowerCase()) {
14966             case 'th':
14967                 switch(className) {
14968                     case 'switch':
14969                         this.showMode(1);
14970                         break;
14971                     case 'prev':
14972                     case 'next':
14973                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14974                         switch(this.viewMode){
14975                                 case 0:
14976                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14977                                         break;
14978                                 case 1:
14979                                 case 2:
14980                                         this.viewDate = this.moveYear(this.viewDate, dir);
14981                                         break;
14982                         }
14983                         this.fill();
14984                         break;
14985                     case 'today':
14986                         var date = new Date();
14987                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14988 //                        this.fill()
14989                         this.setValue(this.formatDate(this.date));
14990                         
14991                         this.hide();
14992                         break;
14993                 }
14994                 break;
14995             case 'span':
14996                 if (className.indexOf('disabled') === -1) {
14997                     this.viewDate.setUTCDate(1);
14998                     if (className.indexOf('month') !== -1) {
14999                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15000                     } else {
15001                         var year = parseInt(html, 10) || 0;
15002                         this.viewDate.setUTCFullYear(year);
15003                         
15004                     }
15005                     this.showMode(-1);
15006                     this.fill();
15007                 }
15008                 break;
15009                 
15010             case 'td':
15011                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15012                     var day = parseInt(html, 10) || 1;
15013                     var year = this.viewDate.getUTCFullYear(),
15014                         month = this.viewDate.getUTCMonth();
15015
15016                     if (className.indexOf('old') !== -1) {
15017                         if(month === 0 ){
15018                             month = 11;
15019                             year -= 1;
15020                         }else{
15021                             month -= 1;
15022                         }
15023                     } else if (className.indexOf('new') !== -1) {
15024                         if (month == 11) {
15025                             month = 0;
15026                             year += 1;
15027                         } else {
15028                             month += 1;
15029                         }
15030                     }
15031                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15032                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15033 //                    this.fill();
15034                     this.setValue(this.formatDate(this.date));
15035                     this.hide();
15036                 }
15037                 break;
15038         }
15039     },
15040     
15041     setStartDate: function(startDate)
15042     {
15043         this.startDate = startDate || -Infinity;
15044         if (this.startDate !== -Infinity) {
15045             this.startDate = this.parseDate(this.startDate);
15046         }
15047         this.update();
15048         this.updateNavArrows();
15049     },
15050
15051     setEndDate: function(endDate)
15052     {
15053         this.endDate = endDate || Infinity;
15054         if (this.endDate !== Infinity) {
15055             this.endDate = this.parseDate(this.endDate);
15056         }
15057         this.update();
15058         this.updateNavArrows();
15059     },
15060     
15061     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15062     {
15063         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15064         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15065             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15066         }
15067         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15068             return parseInt(d, 10);
15069         });
15070         this.update();
15071         this.updateNavArrows();
15072     },
15073     
15074     updateNavArrows: function() 
15075     {
15076         var d = new Date(this.viewDate),
15077         year = d.getUTCFullYear(),
15078         month = d.getUTCMonth();
15079         
15080         Roo.each(this.picker().select('.prev', true).elements, function(v){
15081             v.show();
15082             switch (this.viewMode) {
15083                 case 0:
15084
15085                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15086                         v.hide();
15087                     }
15088                     break;
15089                 case 1:
15090                 case 2:
15091                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15092                         v.hide();
15093                     }
15094                     break;
15095             }
15096         });
15097         
15098         Roo.each(this.picker().select('.next', true).elements, function(v){
15099             v.show();
15100             switch (this.viewMode) {
15101                 case 0:
15102
15103                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15104                         v.hide();
15105                     }
15106                     break;
15107                 case 1:
15108                 case 2:
15109                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15110                         v.hide();
15111                     }
15112                     break;
15113             }
15114         })
15115     },
15116     
15117     moveMonth: function(date, dir)
15118     {
15119         if (!dir) return date;
15120         var new_date = new Date(date.valueOf()),
15121         day = new_date.getUTCDate(),
15122         month = new_date.getUTCMonth(),
15123         mag = Math.abs(dir),
15124         new_month, test;
15125         dir = dir > 0 ? 1 : -1;
15126         if (mag == 1){
15127             test = dir == -1
15128             // If going back one month, make sure month is not current month
15129             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15130             ? function(){
15131                 return new_date.getUTCMonth() == month;
15132             }
15133             // If going forward one month, make sure month is as expected
15134             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15135             : function(){
15136                 return new_date.getUTCMonth() != new_month;
15137             };
15138             new_month = month + dir;
15139             new_date.setUTCMonth(new_month);
15140             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15141             if (new_month < 0 || new_month > 11)
15142                 new_month = (new_month + 12) % 12;
15143         } else {
15144             // For magnitudes >1, move one month at a time...
15145             for (var i=0; i<mag; i++)
15146                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15147                 new_date = this.moveMonth(new_date, dir);
15148             // ...then reset the day, keeping it in the new month
15149             new_month = new_date.getUTCMonth();
15150             new_date.setUTCDate(day);
15151             test = function(){
15152                 return new_month != new_date.getUTCMonth();
15153             };
15154         }
15155         // Common date-resetting loop -- if date is beyond end of month, make it
15156         // end of month
15157         while (test()){
15158             new_date.setUTCDate(--day);
15159             new_date.setUTCMonth(new_month);
15160         }
15161         return new_date;
15162     },
15163
15164     moveYear: function(date, dir)
15165     {
15166         return this.moveMonth(date, dir*12);
15167     },
15168
15169     dateWithinRange: function(date)
15170     {
15171         return date >= this.startDate && date <= this.endDate;
15172     },
15173
15174     
15175     remove: function() 
15176     {
15177         this.picker().remove();
15178     }
15179    
15180 });
15181
15182 Roo.apply(Roo.bootstrap.DateField,  {
15183     
15184     head : {
15185         tag: 'thead',
15186         cn: [
15187         {
15188             tag: 'tr',
15189             cn: [
15190             {
15191                 tag: 'th',
15192                 cls: 'prev',
15193                 html: '<i class="fa fa-arrow-left"/>'
15194             },
15195             {
15196                 tag: 'th',
15197                 cls: 'switch',
15198                 colspan: '5'
15199             },
15200             {
15201                 tag: 'th',
15202                 cls: 'next',
15203                 html: '<i class="fa fa-arrow-right"/>'
15204             }
15205
15206             ]
15207         }
15208         ]
15209     },
15210     
15211     content : {
15212         tag: 'tbody',
15213         cn: [
15214         {
15215             tag: 'tr',
15216             cn: [
15217             {
15218                 tag: 'td',
15219                 colspan: '7'
15220             }
15221             ]
15222         }
15223         ]
15224     },
15225     
15226     footer : {
15227         tag: 'tfoot',
15228         cn: [
15229         {
15230             tag: 'tr',
15231             cn: [
15232             {
15233                 tag: 'th',
15234                 colspan: '7',
15235                 cls: 'today'
15236             }
15237                     
15238             ]
15239         }
15240         ]
15241     },
15242     
15243     dates:{
15244         en: {
15245             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15246             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15247             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15248             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15249             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15250             today: "Today"
15251         }
15252     },
15253     
15254     modes: [
15255     {
15256         clsName: 'days',
15257         navFnc: 'Month',
15258         navStep: 1
15259     },
15260     {
15261         clsName: 'months',
15262         navFnc: 'FullYear',
15263         navStep: 1
15264     },
15265     {
15266         clsName: 'years',
15267         navFnc: 'FullYear',
15268         navStep: 10
15269     }]
15270 });
15271
15272 Roo.apply(Roo.bootstrap.DateField,  {
15273   
15274     template : {
15275         tag: 'div',
15276         cls: 'datepicker dropdown-menu',
15277         cn: [
15278         {
15279             tag: 'div',
15280             cls: 'datepicker-days',
15281             cn: [
15282             {
15283                 tag: 'table',
15284                 cls: 'table-condensed',
15285                 cn:[
15286                 Roo.bootstrap.DateField.head,
15287                 {
15288                     tag: 'tbody'
15289                 },
15290                 Roo.bootstrap.DateField.footer
15291                 ]
15292             }
15293             ]
15294         },
15295         {
15296             tag: 'div',
15297             cls: 'datepicker-months',
15298             cn: [
15299             {
15300                 tag: 'table',
15301                 cls: 'table-condensed',
15302                 cn:[
15303                 Roo.bootstrap.DateField.head,
15304                 Roo.bootstrap.DateField.content,
15305                 Roo.bootstrap.DateField.footer
15306                 ]
15307             }
15308             ]
15309         },
15310         {
15311             tag: 'div',
15312             cls: 'datepicker-years',
15313             cn: [
15314             {
15315                 tag: 'table',
15316                 cls: 'table-condensed',
15317                 cn:[
15318                 Roo.bootstrap.DateField.head,
15319                 Roo.bootstrap.DateField.content,
15320                 Roo.bootstrap.DateField.footer
15321                 ]
15322             }
15323             ]
15324         }
15325         ]
15326     }
15327 });
15328
15329  
15330
15331  /*
15332  * - LGPL
15333  *
15334  * TimeField
15335  * 
15336  */
15337
15338 /**
15339  * @class Roo.bootstrap.TimeField
15340  * @extends Roo.bootstrap.Input
15341  * Bootstrap DateField class
15342  * 
15343  * 
15344  * @constructor
15345  * Create a new TimeField
15346  * @param {Object} config The config object
15347  */
15348
15349 Roo.bootstrap.TimeField = function(config){
15350     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15351     this.addEvents({
15352             /**
15353              * @event show
15354              * Fires when this field show.
15355              * @param {Roo.bootstrap.DateField} this
15356              * @param {Mixed} date The date value
15357              */
15358             show : true,
15359             /**
15360              * @event show
15361              * Fires when this field hide.
15362              * @param {Roo.bootstrap.DateField} this
15363              * @param {Mixed} date The date value
15364              */
15365             hide : true,
15366             /**
15367              * @event select
15368              * Fires when select a date.
15369              * @param {Roo.bootstrap.DateField} this
15370              * @param {Mixed} date The date value
15371              */
15372             select : true
15373         });
15374 };
15375
15376 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15377     
15378     /**
15379      * @cfg {String} format
15380      * The default time format string which can be overriden for localization support.  The format must be
15381      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15382      */
15383     format : "H:i",
15384        
15385     onRender: function(ct, position)
15386     {
15387         
15388         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15389                 
15390         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15391         
15392         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15393         
15394         this.pop = this.picker().select('>.datepicker-time',true).first();
15395         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15396         
15397         this.picker().on('mousedown', this.onMousedown, this);
15398         this.picker().on('click', this.onClick, this);
15399         
15400         this.picker().addClass('datepicker-dropdown');
15401     
15402         this.fillTime();
15403         this.update();
15404             
15405         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15406         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15407         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15408         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15409         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15410         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15411
15412     },
15413     
15414     fireKey: function(e){
15415         if (!this.picker().isVisible()){
15416             if (e.keyCode == 27) // allow escape to hide and re-show picker
15417                 this.show();
15418             return;
15419         }
15420
15421         e.preventDefault();
15422         
15423         switch(e.keyCode){
15424             case 27: // escape
15425                 this.hide();
15426                 break;
15427             case 37: // left
15428             case 39: // right
15429                 this.onTogglePeriod();
15430                 break;
15431             case 38: // up
15432                 this.onIncrementMinutes();
15433                 break;
15434             case 40: // down
15435                 this.onDecrementMinutes();
15436                 break;
15437             case 13: // enter
15438             case 9: // tab
15439                 this.setTime();
15440                 break;
15441         }
15442     },
15443     
15444     onClick: function(e) {
15445         e.stopPropagation();
15446         e.preventDefault();
15447     },
15448     
15449     picker : function()
15450     {
15451         return this.el.select('.datepicker', true).first();
15452     },
15453     
15454     fillTime: function()
15455     {    
15456         var time = this.pop.select('tbody', true).first();
15457         
15458         time.dom.innerHTML = '';
15459         
15460         time.createChild({
15461             tag: 'tr',
15462             cn: [
15463                 {
15464                     tag: 'td',
15465                     cn: [
15466                         {
15467                             tag: 'a',
15468                             href: '#',
15469                             cls: 'btn',
15470                             cn: [
15471                                 {
15472                                     tag: 'span',
15473                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15474                                 }
15475                             ]
15476                         } 
15477                     ]
15478                 },
15479                 {
15480                     tag: 'td',
15481                     cls: 'separator'
15482                 },
15483                 {
15484                     tag: 'td',
15485                     cn: [
15486                         {
15487                             tag: 'a',
15488                             href: '#',
15489                             cls: 'btn',
15490                             cn: [
15491                                 {
15492                                     tag: 'span',
15493                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15494                                 }
15495                             ]
15496                         }
15497                     ]
15498                 },
15499                 {
15500                     tag: 'td',
15501                     cls: 'separator'
15502                 }
15503             ]
15504         });
15505         
15506         time.createChild({
15507             tag: 'tr',
15508             cn: [
15509                 {
15510                     tag: 'td',
15511                     cn: [
15512                         {
15513                             tag: 'span',
15514                             cls: 'timepicker-hour',
15515                             html: '00'
15516                         }  
15517                     ]
15518                 },
15519                 {
15520                     tag: 'td',
15521                     cls: 'separator',
15522                     html: ':'
15523                 },
15524                 {
15525                     tag: 'td',
15526                     cn: [
15527                         {
15528                             tag: 'span',
15529                             cls: 'timepicker-minute',
15530                             html: '00'
15531                         }  
15532                     ]
15533                 },
15534                 {
15535                     tag: 'td',
15536                     cls: 'separator'
15537                 },
15538                 {
15539                     tag: 'td',
15540                     cn: [
15541                         {
15542                             tag: 'button',
15543                             type: 'button',
15544                             cls: 'btn btn-primary period',
15545                             html: 'AM'
15546                             
15547                         }
15548                     ]
15549                 }
15550             ]
15551         });
15552         
15553         time.createChild({
15554             tag: 'tr',
15555             cn: [
15556                 {
15557                     tag: 'td',
15558                     cn: [
15559                         {
15560                             tag: 'a',
15561                             href: '#',
15562                             cls: 'btn',
15563                             cn: [
15564                                 {
15565                                     tag: 'span',
15566                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15567                                 }
15568                             ]
15569                         }
15570                     ]
15571                 },
15572                 {
15573                     tag: 'td',
15574                     cls: 'separator'
15575                 },
15576                 {
15577                     tag: 'td',
15578                     cn: [
15579                         {
15580                             tag: 'a',
15581                             href: '#',
15582                             cls: 'btn',
15583                             cn: [
15584                                 {
15585                                     tag: 'span',
15586                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15587                                 }
15588                             ]
15589                         }
15590                     ]
15591                 },
15592                 {
15593                     tag: 'td',
15594                     cls: 'separator'
15595                 }
15596             ]
15597         });
15598         
15599     },
15600     
15601     update: function()
15602     {
15603         
15604         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15605         
15606         this.fill();
15607     },
15608     
15609     fill: function() 
15610     {
15611         var hours = this.time.getHours();
15612         var minutes = this.time.getMinutes();
15613         var period = 'AM';
15614         
15615         if(hours > 11){
15616             period = 'PM';
15617         }
15618         
15619         if(hours == 0){
15620             hours = 12;
15621         }
15622         
15623         
15624         if(hours > 12){
15625             hours = hours - 12;
15626         }
15627         
15628         if(hours < 10){
15629             hours = '0' + hours;
15630         }
15631         
15632         if(minutes < 10){
15633             minutes = '0' + minutes;
15634         }
15635         
15636         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15637         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15638         this.pop.select('button', true).first().dom.innerHTML = period;
15639         
15640     },
15641     
15642     place: function()
15643     {   
15644         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15645         
15646         var cls = ['bottom'];
15647         
15648         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15649             cls.pop();
15650             cls.push('top');
15651         }
15652         
15653         cls.push('right');
15654         
15655         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15656             cls.pop();
15657             cls.push('left');
15658         }
15659         
15660         this.picker().addClass(cls.join('-'));
15661         
15662         var _this = this;
15663         
15664         Roo.each(cls, function(c){
15665             if(c == 'bottom'){
15666                 _this.picker().setTop(_this.inputEl().getHeight());
15667                 return;
15668             }
15669             if(c == 'top'){
15670                 _this.picker().setTop(0 - _this.picker().getHeight());
15671                 return;
15672             }
15673             
15674             if(c == 'left'){
15675                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15676                 return;
15677             }
15678             if(c == 'right'){
15679                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15680                 return;
15681             }
15682         });
15683         
15684     },
15685   
15686     onFocus : function()
15687     {
15688         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15689         this.show();
15690     },
15691     
15692     onBlur : function()
15693     {
15694         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15695         this.hide();
15696     },
15697     
15698     show : function()
15699     {
15700         this.picker().show();
15701         this.pop.show();
15702         this.update();
15703         this.place();
15704         
15705         this.fireEvent('show', this, this.date);
15706     },
15707     
15708     hide : function()
15709     {
15710         this.picker().hide();
15711         this.pop.hide();
15712         
15713         this.fireEvent('hide', this, this.date);
15714     },
15715     
15716     setTime : function()
15717     {
15718         this.hide();
15719         this.setValue(this.time.format(this.format));
15720         
15721         this.fireEvent('select', this, this.date);
15722         
15723         
15724     },
15725     
15726     onMousedown: function(e){
15727         e.stopPropagation();
15728         e.preventDefault();
15729     },
15730     
15731     onIncrementHours: function()
15732     {
15733         Roo.log('onIncrementHours');
15734         this.time = this.time.add(Date.HOUR, 1);
15735         this.update();
15736         
15737     },
15738     
15739     onDecrementHours: function()
15740     {
15741         Roo.log('onDecrementHours');
15742         this.time = this.time.add(Date.HOUR, -1);
15743         this.update();
15744     },
15745     
15746     onIncrementMinutes: function()
15747     {
15748         Roo.log('onIncrementMinutes');
15749         this.time = this.time.add(Date.MINUTE, 1);
15750         this.update();
15751     },
15752     
15753     onDecrementMinutes: function()
15754     {
15755         Roo.log('onDecrementMinutes');
15756         this.time = this.time.add(Date.MINUTE, -1);
15757         this.update();
15758     },
15759     
15760     onTogglePeriod: function()
15761     {
15762         Roo.log('onTogglePeriod');
15763         this.time = this.time.add(Date.HOUR, 12);
15764         this.update();
15765     }
15766     
15767    
15768 });
15769
15770 Roo.apply(Roo.bootstrap.TimeField,  {
15771     
15772     content : {
15773         tag: 'tbody',
15774         cn: [
15775             {
15776                 tag: 'tr',
15777                 cn: [
15778                 {
15779                     tag: 'td',
15780                     colspan: '7'
15781                 }
15782                 ]
15783             }
15784         ]
15785     },
15786     
15787     footer : {
15788         tag: 'tfoot',
15789         cn: [
15790             {
15791                 tag: 'tr',
15792                 cn: [
15793                 {
15794                     tag: 'th',
15795                     colspan: '7',
15796                     cls: '',
15797                     cn: [
15798                         {
15799                             tag: 'button',
15800                             cls: 'btn btn-info ok',
15801                             html: 'OK'
15802                         }
15803                     ]
15804                 }
15805
15806                 ]
15807             }
15808         ]
15809     }
15810 });
15811
15812 Roo.apply(Roo.bootstrap.TimeField,  {
15813   
15814     template : {
15815         tag: 'div',
15816         cls: 'datepicker dropdown-menu',
15817         cn: [
15818             {
15819                 tag: 'div',
15820                 cls: 'datepicker-time',
15821                 cn: [
15822                 {
15823                     tag: 'table',
15824                     cls: 'table-condensed',
15825                     cn:[
15826                     Roo.bootstrap.TimeField.content,
15827                     Roo.bootstrap.TimeField.footer
15828                     ]
15829                 }
15830                 ]
15831             }
15832         ]
15833     }
15834 });
15835
15836  
15837
15838  /*
15839  * - LGPL
15840  *
15841  * CheckBox
15842  * 
15843  */
15844
15845 /**
15846  * @class Roo.bootstrap.CheckBox
15847  * @extends Roo.bootstrap.Input
15848  * Bootstrap CheckBox class
15849  * 
15850  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15851  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15852  * @cfg {String} boxLabel The text that appears beside the checkbox
15853  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15854  * @cfg {Boolean} checked initnal the element
15855  * 
15856  * 
15857  * @constructor
15858  * Create a new CheckBox
15859  * @param {Object} config The config object
15860  */
15861
15862 Roo.bootstrap.CheckBox = function(config){
15863     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15864    
15865         this.addEvents({
15866             /**
15867             * @event check
15868             * Fires when the element is checked or unchecked.
15869             * @param {Roo.bootstrap.CheckBox} this This input
15870             * @param {Boolean} checked The new checked value
15871             */
15872            check : true
15873         });
15874 };
15875
15876 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15877     
15878     inputType: 'checkbox',
15879     inputValue: 1,
15880     valueOff: 0,
15881     boxLabel: false,
15882     checked: false,
15883     weight : false,
15884     
15885     getAutoCreate : function()
15886     {
15887         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15888         
15889         var id = Roo.id();
15890         
15891         var cfg = {};
15892         
15893         cfg.cls = 'form-group checkbox' //input-group
15894         
15895         
15896         
15897         
15898         var input =  {
15899             tag: 'input',
15900             id : id,
15901             type : this.inputType,
15902             value : (!this.checked) ? this.valueOff : this.inputValue,
15903             cls : 'roo-checkbox', //'form-box',
15904             placeholder : this.placeholder || ''
15905             
15906         };
15907         
15908         if (this.weight) { // Validity check?
15909             cfg.cls += " checkbox-" + this.weight;
15910         }
15911         
15912         if (this.disabled) {
15913             input.disabled=true;
15914         }
15915         
15916         if(this.checked){
15917             input.checked = this.checked;
15918         }
15919         
15920         if (this.name) {
15921             input.name = this.name;
15922         }
15923         
15924         if (this.size) {
15925             input.cls += ' input-' + this.size;
15926         }
15927         
15928         var settings=this;
15929         ['xs','sm','md','lg'].map(function(size){
15930             if (settings[size]) {
15931                 cfg.cls += ' col-' + size + '-' + settings[size];
15932             }
15933         });
15934         
15935        
15936         
15937         var inputblock = input;
15938         
15939         
15940         
15941         
15942         if (this.before || this.after) {
15943             
15944             inputblock = {
15945                 cls : 'input-group',
15946                 cn :  [] 
15947             };
15948             if (this.before) {
15949                 inputblock.cn.push({
15950                     tag :'span',
15951                     cls : 'input-group-addon',
15952                     html : this.before
15953                 });
15954             }
15955             inputblock.cn.push(input);
15956             if (this.after) {
15957                 inputblock.cn.push({
15958                     tag :'span',
15959                     cls : 'input-group-addon',
15960                     html : this.after
15961                 });
15962             }
15963             
15964         };
15965         
15966         if (align ==='left' && this.fieldLabel.length) {
15967                 Roo.log("left and has label");
15968                 cfg.cn = [
15969                     
15970                     {
15971                         tag: 'label',
15972                         'for' :  id,
15973                         cls : 'control-label col-md-' + this.labelWidth,
15974                         html : this.fieldLabel
15975                         
15976                     },
15977                     {
15978                         cls : "col-md-" + (12 - this.labelWidth), 
15979                         cn: [
15980                             inputblock
15981                         ]
15982                     }
15983                     
15984                 ];
15985         } else if ( this.fieldLabel.length) {
15986                 Roo.log(" label");
15987                 cfg.cn = [
15988                    
15989                     {
15990                         tag: this.boxLabel ? 'span' : 'label',
15991                         'for': id,
15992                         cls: 'control-label box-input-label',
15993                         //cls : 'input-group-addon',
15994                         html : this.fieldLabel
15995                         
15996                     },
15997                     
15998                     inputblock
15999                     
16000                 ];
16001
16002         } else {
16003             
16004                 Roo.log(" no label && no align");
16005                 cfg.cn = [  inputblock ] ;
16006                 
16007                 
16008         };
16009          if(this.boxLabel){
16010             cfg.cn.push( {
16011                 tag: 'label',
16012                 'for': id,
16013                 cls: 'box-label',
16014                 html: this.boxLabel
16015                 
16016             });
16017         }
16018         
16019         
16020        
16021         return cfg;
16022         
16023     },
16024     
16025     /**
16026      * return the real input element.
16027      */
16028     inputEl: function ()
16029     {
16030         return this.el.select('input.roo-checkbox',true).first();
16031     },
16032     
16033     label: function()
16034     {
16035         return this.el.select('label.control-label',true).first();
16036     },
16037     
16038     initEvents : function()
16039     {
16040 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16041         
16042         this.inputEl().on('click', this.onClick,  this);
16043         
16044     },
16045     
16046     onClick : function()
16047     {   
16048         this.setChecked(!this.checked);
16049     },
16050     
16051     setChecked : function(state,suppressEvent)
16052     {
16053         this.checked = state;
16054         
16055         this.inputEl().dom.checked = state;
16056         
16057         if(suppressEvent !== true){
16058             this.fireEvent('check', this, state);
16059         }
16060         
16061         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16062         
16063     },
16064     
16065     setValue : function(v,suppressEvent)
16066     {
16067         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16068     }
16069     
16070 });
16071
16072  
16073 /*
16074  * - LGPL
16075  *
16076  * Radio
16077  * 
16078  */
16079
16080 /**
16081  * @class Roo.bootstrap.Radio
16082  * @extends Roo.bootstrap.CheckBox
16083  * Bootstrap Radio class
16084
16085  * @constructor
16086  * Create a new Radio
16087  * @param {Object} config The config object
16088  */
16089
16090 Roo.bootstrap.Radio = function(config){
16091     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16092    
16093 };
16094
16095 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16096     
16097     inputType: 'radio',
16098     inputValue: '',
16099     valueOff: '',
16100     
16101     getAutoCreate : function()
16102     {
16103         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16104         
16105         var id = Roo.id();
16106         
16107         var cfg = {};
16108         
16109         cfg.cls = 'form-group radio' //input-group
16110         
16111         var input =  {
16112             tag: 'input',
16113             id : id,
16114             type : this.inputType,
16115             value : (!this.checked) ? this.valueOff : this.inputValue,
16116             cls : 'roo-radio',
16117             placeholder : this.placeholder || ''
16118             
16119         };
16120           if (this.weight) { // Validity check?
16121             cfg.cls += " radio-" + this.weight;
16122         }
16123         if (this.disabled) {
16124             input.disabled=true;
16125         }
16126         
16127         if(this.checked){
16128             input.checked = this.checked;
16129         }
16130         
16131         if (this.name) {
16132             input.name = this.name;
16133         }
16134         
16135         if (this.size) {
16136             input.cls += ' input-' + this.size;
16137         }
16138         
16139         var settings=this;
16140         ['xs','sm','md','lg'].map(function(size){
16141             if (settings[size]) {
16142                 cfg.cls += ' col-' + size + '-' + settings[size];
16143             }
16144         });
16145         
16146         var inputblock = input;
16147         
16148         if (this.before || this.after) {
16149             
16150             inputblock = {
16151                 cls : 'input-group',
16152                 cn :  [] 
16153             };
16154             if (this.before) {
16155                 inputblock.cn.push({
16156                     tag :'span',
16157                     cls : 'input-group-addon',
16158                     html : this.before
16159                 });
16160             }
16161             inputblock.cn.push(input);
16162             if (this.after) {
16163                 inputblock.cn.push({
16164                     tag :'span',
16165                     cls : 'input-group-addon',
16166                     html : this.after
16167                 });
16168             }
16169             
16170         };
16171         
16172         if (align ==='left' && this.fieldLabel.length) {
16173                 Roo.log("left and has label");
16174                 cfg.cn = [
16175                     
16176                     {
16177                         tag: 'label',
16178                         'for' :  id,
16179                         cls : 'control-label col-md-' + this.labelWidth,
16180                         html : this.fieldLabel
16181                         
16182                     },
16183                     {
16184                         cls : "col-md-" + (12 - this.labelWidth), 
16185                         cn: [
16186                             inputblock
16187                         ]
16188                     }
16189                     
16190                 ];
16191         } else if ( this.fieldLabel.length) {
16192                 Roo.log(" label");
16193                  cfg.cn = [
16194                    
16195                     {
16196                         tag: 'label',
16197                         'for': id,
16198                         cls: 'control-label box-input-label',
16199                         //cls : 'input-group-addon',
16200                         html : this.fieldLabel
16201                         
16202                     },
16203                     
16204                     inputblock
16205                     
16206                 ];
16207
16208         } else {
16209             
16210                    Roo.log(" no label && no align");
16211                 cfg.cn = [
16212                     
16213                         inputblock
16214                     
16215                 ];
16216                 
16217                 
16218         };
16219         
16220         if(this.boxLabel){
16221             cfg.cn.push({
16222                 tag: 'label',
16223                 'for': id,
16224                 cls: 'box-label',
16225                 html: this.boxLabel
16226             })
16227         }
16228         
16229         return cfg;
16230         
16231     },
16232     inputEl: function ()
16233     {
16234         return this.el.select('input.roo-radio',true).first();
16235     },
16236     onClick : function()
16237     {   
16238         this.setChecked(true);
16239     },
16240     
16241     setChecked : function(state,suppressEvent)
16242     {
16243         if(state){
16244             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16245                 v.dom.checked = false;
16246             });
16247         }
16248         
16249         this.checked = state;
16250         this.inputEl().dom.checked = state;
16251         
16252         if(suppressEvent !== true){
16253             this.fireEvent('check', this, state);
16254         }
16255         
16256         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16257         
16258     },
16259     
16260     getGroupValue : function()
16261     {
16262         var value = ''
16263         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16264             if(v.dom.checked == true){
16265                 value = v.dom.value;
16266             }
16267         });
16268         
16269         return value;
16270     },
16271     
16272     /**
16273      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16274      * @return {Mixed} value The field value
16275      */
16276     getValue : function(){
16277         return this.getGroupValue();
16278     }
16279     
16280 });
16281
16282  
16283 //<script type="text/javascript">
16284
16285 /*
16286  * Based  Ext JS Library 1.1.1
16287  * Copyright(c) 2006-2007, Ext JS, LLC.
16288  * LGPL
16289  *
16290  */
16291  
16292 /**
16293  * @class Roo.HtmlEditorCore
16294  * @extends Roo.Component
16295  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16296  *
16297  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16298  */
16299
16300 Roo.HtmlEditorCore = function(config){
16301     
16302     
16303     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16304     this.addEvents({
16305         /**
16306          * @event initialize
16307          * Fires when the editor is fully initialized (including the iframe)
16308          * @param {Roo.HtmlEditorCore} this
16309          */
16310         initialize: true,
16311         /**
16312          * @event activate
16313          * Fires when the editor is first receives the focus. Any insertion must wait
16314          * until after this event.
16315          * @param {Roo.HtmlEditorCore} this
16316          */
16317         activate: true,
16318          /**
16319          * @event beforesync
16320          * Fires before the textarea is updated with content from the editor iframe. Return false
16321          * to cancel the sync.
16322          * @param {Roo.HtmlEditorCore} this
16323          * @param {String} html
16324          */
16325         beforesync: true,
16326          /**
16327          * @event beforepush
16328          * Fires before the iframe editor is updated with content from the textarea. Return false
16329          * to cancel the push.
16330          * @param {Roo.HtmlEditorCore} this
16331          * @param {String} html
16332          */
16333         beforepush: true,
16334          /**
16335          * @event sync
16336          * Fires when the textarea is updated with content from the editor iframe.
16337          * @param {Roo.HtmlEditorCore} this
16338          * @param {String} html
16339          */
16340         sync: true,
16341          /**
16342          * @event push
16343          * Fires when the iframe editor is updated with content from the textarea.
16344          * @param {Roo.HtmlEditorCore} this
16345          * @param {String} html
16346          */
16347         push: true,
16348         
16349         /**
16350          * @event editorevent
16351          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16352          * @param {Roo.HtmlEditorCore} this
16353          */
16354         editorevent: true
16355     });
16356      
16357 };
16358
16359
16360 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16361
16362
16363      /**
16364      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16365      */
16366     
16367     owner : false,
16368     
16369      /**
16370      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16371      *                        Roo.resizable.
16372      */
16373     resizable : false,
16374      /**
16375      * @cfg {Number} height (in pixels)
16376      */   
16377     height: 300,
16378    /**
16379      * @cfg {Number} width (in pixels)
16380      */   
16381     width: 500,
16382     
16383     /**
16384      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16385      * 
16386      */
16387     stylesheets: false,
16388     
16389     // id of frame..
16390     frameId: false,
16391     
16392     // private properties
16393     validationEvent : false,
16394     deferHeight: true,
16395     initialized : false,
16396     activated : false,
16397     sourceEditMode : false,
16398     onFocus : Roo.emptyFn,
16399     iframePad:3,
16400     hideMode:'offsets',
16401     
16402     clearUp: true,
16403     
16404      
16405     
16406
16407     /**
16408      * Protected method that will not generally be called directly. It
16409      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16410      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16411      */
16412     getDocMarkup : function(){
16413         // body styles..
16414         var st = '';
16415         Roo.log(this.stylesheets);
16416         
16417         // inherit styels from page...?? 
16418         if (this.stylesheets === false) {
16419             
16420             Roo.get(document.head).select('style').each(function(node) {
16421                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16422             });
16423             
16424             Roo.get(document.head).select('link').each(function(node) { 
16425                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16426             });
16427             
16428         } else if (!this.stylesheets.length) {
16429                 // simple..
16430                 st = '<style type="text/css">' +
16431                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16432                    '</style>';
16433         } else {
16434             Roo.each(this.stylesheets, function(s) {
16435                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16436             });
16437             
16438         }
16439         
16440         st +=  '<style type="text/css">' +
16441             'IMG { cursor: pointer } ' +
16442         '</style>';
16443
16444         
16445         return '<html><head>' + st  +
16446             //<style type="text/css">' +
16447             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16448             //'</style>' +
16449             ' </head><body class="roo-htmleditor-body"></body></html>';
16450     },
16451
16452     // private
16453     onRender : function(ct, position)
16454     {
16455         var _t = this;
16456         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16457         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16458         
16459         
16460         this.el.dom.style.border = '0 none';
16461         this.el.dom.setAttribute('tabIndex', -1);
16462         this.el.addClass('x-hidden hide');
16463         
16464         
16465         
16466         if(Roo.isIE){ // fix IE 1px bogus margin
16467             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16468         }
16469        
16470         
16471         this.frameId = Roo.id();
16472         
16473          
16474         
16475         var iframe = this.owner.wrap.createChild({
16476             tag: 'iframe',
16477             cls: 'form-control', // bootstrap..
16478             id: this.frameId,
16479             name: this.frameId,
16480             frameBorder : 'no',
16481             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16482         }, this.el
16483         );
16484         
16485         
16486         this.iframe = iframe.dom;
16487
16488          this.assignDocWin();
16489         
16490         this.doc.designMode = 'on';
16491        
16492         this.doc.open();
16493         this.doc.write(this.getDocMarkup());
16494         this.doc.close();
16495
16496         
16497         var task = { // must defer to wait for browser to be ready
16498             run : function(){
16499                 //console.log("run task?" + this.doc.readyState);
16500                 this.assignDocWin();
16501                 if(this.doc.body || this.doc.readyState == 'complete'){
16502                     try {
16503                         this.doc.designMode="on";
16504                     } catch (e) {
16505                         return;
16506                     }
16507                     Roo.TaskMgr.stop(task);
16508                     this.initEditor.defer(10, this);
16509                 }
16510             },
16511             interval : 10,
16512             duration: 10000,
16513             scope: this
16514         };
16515         Roo.TaskMgr.start(task);
16516
16517         
16518          
16519     },
16520
16521     // private
16522     onResize : function(w, h)
16523     {
16524          Roo.log('resize: ' +w + ',' + h );
16525         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16526         if(!this.iframe){
16527             return;
16528         }
16529         if(typeof w == 'number'){
16530             
16531             this.iframe.style.width = w + 'px';
16532         }
16533         if(typeof h == 'number'){
16534             
16535             this.iframe.style.height = h + 'px';
16536             if(this.doc){
16537                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16538             }
16539         }
16540         
16541     },
16542
16543     /**
16544      * Toggles the editor between standard and source edit mode.
16545      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16546      */
16547     toggleSourceEdit : function(sourceEditMode){
16548         
16549         this.sourceEditMode = sourceEditMode === true;
16550         
16551         if(this.sourceEditMode){
16552  
16553             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16554             
16555         }else{
16556             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16557             //this.iframe.className = '';
16558             this.deferFocus();
16559         }
16560         //this.setSize(this.owner.wrap.getSize());
16561         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16562     },
16563
16564     
16565   
16566
16567     /**
16568      * Protected method that will not generally be called directly. If you need/want
16569      * custom HTML cleanup, this is the method you should override.
16570      * @param {String} html The HTML to be cleaned
16571      * return {String} The cleaned HTML
16572      */
16573     cleanHtml : function(html){
16574         html = String(html);
16575         if(html.length > 5){
16576             if(Roo.isSafari){ // strip safari nonsense
16577                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16578             }
16579         }
16580         if(html == '&nbsp;'){
16581             html = '';
16582         }
16583         return html;
16584     },
16585
16586     /**
16587      * HTML Editor -> Textarea
16588      * Protected method that will not generally be called directly. Syncs the contents
16589      * of the editor iframe with the textarea.
16590      */
16591     syncValue : function(){
16592         if(this.initialized){
16593             var bd = (this.doc.body || this.doc.documentElement);
16594             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16595             var html = bd.innerHTML;
16596             if(Roo.isSafari){
16597                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16598                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16599                 if(m && m[1]){
16600                     html = '<div style="'+m[0]+'">' + html + '</div>';
16601                 }
16602             }
16603             html = this.cleanHtml(html);
16604             // fix up the special chars.. normaly like back quotes in word...
16605             // however we do not want to do this with chinese..
16606             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16607                 var cc = b.charCodeAt();
16608                 if (
16609                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16610                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16611                     (cc >= 0xf900 && cc < 0xfb00 )
16612                 ) {
16613                         return b;
16614                 }
16615                 return "&#"+cc+";" 
16616             });
16617             if(this.owner.fireEvent('beforesync', this, html) !== false){
16618                 this.el.dom.value = html;
16619                 this.owner.fireEvent('sync', this, html);
16620             }
16621         }
16622     },
16623
16624     /**
16625      * Protected method that will not generally be called directly. Pushes the value of the textarea
16626      * into the iframe editor.
16627      */
16628     pushValue : function(){
16629         if(this.initialized){
16630             var v = this.el.dom.value.trim();
16631             
16632 //            if(v.length < 1){
16633 //                v = '&#160;';
16634 //            }
16635             
16636             if(this.owner.fireEvent('beforepush', this, v) !== false){
16637                 var d = (this.doc.body || this.doc.documentElement);
16638                 d.innerHTML = v;
16639                 this.cleanUpPaste();
16640                 this.el.dom.value = d.innerHTML;
16641                 this.owner.fireEvent('push', this, v);
16642             }
16643         }
16644     },
16645
16646     // private
16647     deferFocus : function(){
16648         this.focus.defer(10, this);
16649     },
16650
16651     // doc'ed in Field
16652     focus : function(){
16653         if(this.win && !this.sourceEditMode){
16654             this.win.focus();
16655         }else{
16656             this.el.focus();
16657         }
16658     },
16659     
16660     assignDocWin: function()
16661     {
16662         var iframe = this.iframe;
16663         
16664          if(Roo.isIE){
16665             this.doc = iframe.contentWindow.document;
16666             this.win = iframe.contentWindow;
16667         } else {
16668 //            if (!Roo.get(this.frameId)) {
16669 //                return;
16670 //            }
16671 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16672 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16673             
16674             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16675                 return;
16676             }
16677             
16678             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16679             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16680         }
16681     },
16682     
16683     // private
16684     initEditor : function(){
16685         //console.log("INIT EDITOR");
16686         this.assignDocWin();
16687         
16688         
16689         
16690         this.doc.designMode="on";
16691         this.doc.open();
16692         this.doc.write(this.getDocMarkup());
16693         this.doc.close();
16694         
16695         var dbody = (this.doc.body || this.doc.documentElement);
16696         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16697         // this copies styles from the containing element into thsi one..
16698         // not sure why we need all of this..
16699         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16700         
16701         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16702         //ss['background-attachment'] = 'fixed'; // w3c
16703         dbody.bgProperties = 'fixed'; // ie
16704         //Roo.DomHelper.applyStyles(dbody, ss);
16705         Roo.EventManager.on(this.doc, {
16706             //'mousedown': this.onEditorEvent,
16707             'mouseup': this.onEditorEvent,
16708             'dblclick': this.onEditorEvent,
16709             'click': this.onEditorEvent,
16710             'keyup': this.onEditorEvent,
16711             buffer:100,
16712             scope: this
16713         });
16714         if(Roo.isGecko){
16715             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16716         }
16717         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16718             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16719         }
16720         this.initialized = true;
16721
16722         this.owner.fireEvent('initialize', this);
16723         this.pushValue();
16724     },
16725
16726     // private
16727     onDestroy : function(){
16728         
16729         
16730         
16731         if(this.rendered){
16732             
16733             //for (var i =0; i < this.toolbars.length;i++) {
16734             //    // fixme - ask toolbars for heights?
16735             //    this.toolbars[i].onDestroy();
16736            // }
16737             
16738             //this.wrap.dom.innerHTML = '';
16739             //this.wrap.remove();
16740         }
16741     },
16742
16743     // private
16744     onFirstFocus : function(){
16745         
16746         this.assignDocWin();
16747         
16748         
16749         this.activated = true;
16750          
16751     
16752         if(Roo.isGecko){ // prevent silly gecko errors
16753             this.win.focus();
16754             var s = this.win.getSelection();
16755             if(!s.focusNode || s.focusNode.nodeType != 3){
16756                 var r = s.getRangeAt(0);
16757                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16758                 r.collapse(true);
16759                 this.deferFocus();
16760             }
16761             try{
16762                 this.execCmd('useCSS', true);
16763                 this.execCmd('styleWithCSS', false);
16764             }catch(e){}
16765         }
16766         this.owner.fireEvent('activate', this);
16767     },
16768
16769     // private
16770     adjustFont: function(btn){
16771         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16772         //if(Roo.isSafari){ // safari
16773         //    adjust *= 2;
16774        // }
16775         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16776         if(Roo.isSafari){ // safari
16777             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16778             v =  (v < 10) ? 10 : v;
16779             v =  (v > 48) ? 48 : v;
16780             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16781             
16782         }
16783         
16784         
16785         v = Math.max(1, v+adjust);
16786         
16787         this.execCmd('FontSize', v  );
16788     },
16789
16790     onEditorEvent : function(e){
16791         this.owner.fireEvent('editorevent', this, e);
16792       //  this.updateToolbar();
16793         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16794     },
16795
16796     insertTag : function(tg)
16797     {
16798         // could be a bit smarter... -> wrap the current selected tRoo..
16799         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16800             
16801             range = this.createRange(this.getSelection());
16802             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16803             wrappingNode.appendChild(range.extractContents());
16804             range.insertNode(wrappingNode);
16805
16806             return;
16807             
16808             
16809             
16810         }
16811         this.execCmd("formatblock",   tg);
16812         
16813     },
16814     
16815     insertText : function(txt)
16816     {
16817         
16818         
16819         var range = this.createRange();
16820         range.deleteContents();
16821                //alert(Sender.getAttribute('label'));
16822                
16823         range.insertNode(this.doc.createTextNode(txt));
16824     } ,
16825     
16826      
16827
16828     /**
16829      * Executes a Midas editor command on the editor document and performs necessary focus and
16830      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16831      * @param {String} cmd The Midas command
16832      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16833      */
16834     relayCmd : function(cmd, value){
16835         this.win.focus();
16836         this.execCmd(cmd, value);
16837         this.owner.fireEvent('editorevent', this);
16838         //this.updateToolbar();
16839         this.owner.deferFocus();
16840     },
16841
16842     /**
16843      * Executes a Midas editor command directly on the editor document.
16844      * For visual commands, you should use {@link #relayCmd} instead.
16845      * <b>This should only be called after the editor is initialized.</b>
16846      * @param {String} cmd The Midas command
16847      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16848      */
16849     execCmd : function(cmd, value){
16850         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16851         this.syncValue();
16852     },
16853  
16854  
16855    
16856     /**
16857      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16858      * to insert tRoo.
16859      * @param {String} text | dom node.. 
16860      */
16861     insertAtCursor : function(text)
16862     {
16863         
16864         
16865         
16866         if(!this.activated){
16867             return;
16868         }
16869         /*
16870         if(Roo.isIE){
16871             this.win.focus();
16872             var r = this.doc.selection.createRange();
16873             if(r){
16874                 r.collapse(true);
16875                 r.pasteHTML(text);
16876                 this.syncValue();
16877                 this.deferFocus();
16878             
16879             }
16880             return;
16881         }
16882         */
16883         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16884             this.win.focus();
16885             
16886             
16887             // from jquery ui (MIT licenced)
16888             var range, node;
16889             var win = this.win;
16890             
16891             if (win.getSelection && win.getSelection().getRangeAt) {
16892                 range = win.getSelection().getRangeAt(0);
16893                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16894                 range.insertNode(node);
16895             } else if (win.document.selection && win.document.selection.createRange) {
16896                 // no firefox support
16897                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16898                 win.document.selection.createRange().pasteHTML(txt);
16899             } else {
16900                 // no firefox support
16901                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16902                 this.execCmd('InsertHTML', txt);
16903             } 
16904             
16905             this.syncValue();
16906             
16907             this.deferFocus();
16908         }
16909     },
16910  // private
16911     mozKeyPress : function(e){
16912         if(e.ctrlKey){
16913             var c = e.getCharCode(), cmd;
16914           
16915             if(c > 0){
16916                 c = String.fromCharCode(c).toLowerCase();
16917                 switch(c){
16918                     case 'b':
16919                         cmd = 'bold';
16920                         break;
16921                     case 'i':
16922                         cmd = 'italic';
16923                         break;
16924                     
16925                     case 'u':
16926                         cmd = 'underline';
16927                         break;
16928                     
16929                     case 'v':
16930                         this.cleanUpPaste.defer(100, this);
16931                         return;
16932                         
16933                 }
16934                 if(cmd){
16935                     this.win.focus();
16936                     this.execCmd(cmd);
16937                     this.deferFocus();
16938                     e.preventDefault();
16939                 }
16940                 
16941             }
16942         }
16943     },
16944
16945     // private
16946     fixKeys : function(){ // load time branching for fastest keydown performance
16947         if(Roo.isIE){
16948             return function(e){
16949                 var k = e.getKey(), r;
16950                 if(k == e.TAB){
16951                     e.stopEvent();
16952                     r = this.doc.selection.createRange();
16953                     if(r){
16954                         r.collapse(true);
16955                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16956                         this.deferFocus();
16957                     }
16958                     return;
16959                 }
16960                 
16961                 if(k == e.ENTER){
16962                     r = this.doc.selection.createRange();
16963                     if(r){
16964                         var target = r.parentElement();
16965                         if(!target || target.tagName.toLowerCase() != 'li'){
16966                             e.stopEvent();
16967                             r.pasteHTML('<br />');
16968                             r.collapse(false);
16969                             r.select();
16970                         }
16971                     }
16972                 }
16973                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16974                     this.cleanUpPaste.defer(100, this);
16975                     return;
16976                 }
16977                 
16978                 
16979             };
16980         }else if(Roo.isOpera){
16981             return function(e){
16982                 var k = e.getKey();
16983                 if(k == e.TAB){
16984                     e.stopEvent();
16985                     this.win.focus();
16986                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16987                     this.deferFocus();
16988                 }
16989                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16990                     this.cleanUpPaste.defer(100, this);
16991                     return;
16992                 }
16993                 
16994             };
16995         }else if(Roo.isSafari){
16996             return function(e){
16997                 var k = e.getKey();
16998                 
16999                 if(k == e.TAB){
17000                     e.stopEvent();
17001                     this.execCmd('InsertText','\t');
17002                     this.deferFocus();
17003                     return;
17004                 }
17005                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17006                     this.cleanUpPaste.defer(100, this);
17007                     return;
17008                 }
17009                 
17010              };
17011         }
17012     }(),
17013     
17014     getAllAncestors: function()
17015     {
17016         var p = this.getSelectedNode();
17017         var a = [];
17018         if (!p) {
17019             a.push(p); // push blank onto stack..
17020             p = this.getParentElement();
17021         }
17022         
17023         
17024         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17025             a.push(p);
17026             p = p.parentNode;
17027         }
17028         a.push(this.doc.body);
17029         return a;
17030     },
17031     lastSel : false,
17032     lastSelNode : false,
17033     
17034     
17035     getSelection : function() 
17036     {
17037         this.assignDocWin();
17038         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17039     },
17040     
17041     getSelectedNode: function() 
17042     {
17043         // this may only work on Gecko!!!
17044         
17045         // should we cache this!!!!
17046         
17047         
17048         
17049          
17050         var range = this.createRange(this.getSelection()).cloneRange();
17051         
17052         if (Roo.isIE) {
17053             var parent = range.parentElement();
17054             while (true) {
17055                 var testRange = range.duplicate();
17056                 testRange.moveToElementText(parent);
17057                 if (testRange.inRange(range)) {
17058                     break;
17059                 }
17060                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17061                     break;
17062                 }
17063                 parent = parent.parentElement;
17064             }
17065             return parent;
17066         }
17067         
17068         // is ancestor a text element.
17069         var ac =  range.commonAncestorContainer;
17070         if (ac.nodeType == 3) {
17071             ac = ac.parentNode;
17072         }
17073         
17074         var ar = ac.childNodes;
17075          
17076         var nodes = [];
17077         var other_nodes = [];
17078         var has_other_nodes = false;
17079         for (var i=0;i<ar.length;i++) {
17080             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17081                 continue;
17082             }
17083             // fullly contained node.
17084             
17085             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17086                 nodes.push(ar[i]);
17087                 continue;
17088             }
17089             
17090             // probably selected..
17091             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17092                 other_nodes.push(ar[i]);
17093                 continue;
17094             }
17095             // outer..
17096             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17097                 continue;
17098             }
17099             
17100             
17101             has_other_nodes = true;
17102         }
17103         if (!nodes.length && other_nodes.length) {
17104             nodes= other_nodes;
17105         }
17106         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17107             return false;
17108         }
17109         
17110         return nodes[0];
17111     },
17112     createRange: function(sel)
17113     {
17114         // this has strange effects when using with 
17115         // top toolbar - not sure if it's a great idea.
17116         //this.editor.contentWindow.focus();
17117         if (typeof sel != "undefined") {
17118             try {
17119                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17120             } catch(e) {
17121                 return this.doc.createRange();
17122             }
17123         } else {
17124             return this.doc.createRange();
17125         }
17126     },
17127     getParentElement: function()
17128     {
17129         
17130         this.assignDocWin();
17131         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17132         
17133         var range = this.createRange(sel);
17134          
17135         try {
17136             var p = range.commonAncestorContainer;
17137             while (p.nodeType == 3) { // text node
17138                 p = p.parentNode;
17139             }
17140             return p;
17141         } catch (e) {
17142             return null;
17143         }
17144     
17145     },
17146     /***
17147      *
17148      * Range intersection.. the hard stuff...
17149      *  '-1' = before
17150      *  '0' = hits..
17151      *  '1' = after.
17152      *         [ -- selected range --- ]
17153      *   [fail]                        [fail]
17154      *
17155      *    basically..
17156      *      if end is before start or  hits it. fail.
17157      *      if start is after end or hits it fail.
17158      *
17159      *   if either hits (but other is outside. - then it's not 
17160      *   
17161      *    
17162      **/
17163     
17164     
17165     // @see http://www.thismuchiknow.co.uk/?p=64.
17166     rangeIntersectsNode : function(range, node)
17167     {
17168         var nodeRange = node.ownerDocument.createRange();
17169         try {
17170             nodeRange.selectNode(node);
17171         } catch (e) {
17172             nodeRange.selectNodeContents(node);
17173         }
17174     
17175         var rangeStartRange = range.cloneRange();
17176         rangeStartRange.collapse(true);
17177     
17178         var rangeEndRange = range.cloneRange();
17179         rangeEndRange.collapse(false);
17180     
17181         var nodeStartRange = nodeRange.cloneRange();
17182         nodeStartRange.collapse(true);
17183     
17184         var nodeEndRange = nodeRange.cloneRange();
17185         nodeEndRange.collapse(false);
17186     
17187         return rangeStartRange.compareBoundaryPoints(
17188                  Range.START_TO_START, nodeEndRange) == -1 &&
17189                rangeEndRange.compareBoundaryPoints(
17190                  Range.START_TO_START, nodeStartRange) == 1;
17191         
17192          
17193     },
17194     rangeCompareNode : function(range, node)
17195     {
17196         var nodeRange = node.ownerDocument.createRange();
17197         try {
17198             nodeRange.selectNode(node);
17199         } catch (e) {
17200             nodeRange.selectNodeContents(node);
17201         }
17202         
17203         
17204         range.collapse(true);
17205     
17206         nodeRange.collapse(true);
17207      
17208         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17209         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17210          
17211         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17212         
17213         var nodeIsBefore   =  ss == 1;
17214         var nodeIsAfter    = ee == -1;
17215         
17216         if (nodeIsBefore && nodeIsAfter)
17217             return 0; // outer
17218         if (!nodeIsBefore && nodeIsAfter)
17219             return 1; //right trailed.
17220         
17221         if (nodeIsBefore && !nodeIsAfter)
17222             return 2;  // left trailed.
17223         // fully contined.
17224         return 3;
17225     },
17226
17227     // private? - in a new class?
17228     cleanUpPaste :  function()
17229     {
17230         // cleans up the whole document..
17231         Roo.log('cleanuppaste');
17232         
17233         this.cleanUpChildren(this.doc.body);
17234         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17235         if (clean != this.doc.body.innerHTML) {
17236             this.doc.body.innerHTML = clean;
17237         }
17238         
17239     },
17240     
17241     cleanWordChars : function(input) {// change the chars to hex code
17242         var he = Roo.HtmlEditorCore;
17243         
17244         var output = input;
17245         Roo.each(he.swapCodes, function(sw) { 
17246             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17247             
17248             output = output.replace(swapper, sw[1]);
17249         });
17250         
17251         return output;
17252     },
17253     
17254     
17255     cleanUpChildren : function (n)
17256     {
17257         if (!n.childNodes.length) {
17258             return;
17259         }
17260         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17261            this.cleanUpChild(n.childNodes[i]);
17262         }
17263     },
17264     
17265     
17266         
17267     
17268     cleanUpChild : function (node)
17269     {
17270         var ed = this;
17271         //console.log(node);
17272         if (node.nodeName == "#text") {
17273             // clean up silly Windows -- stuff?
17274             return; 
17275         }
17276         if (node.nodeName == "#comment") {
17277             node.parentNode.removeChild(node);
17278             // clean up silly Windows -- stuff?
17279             return; 
17280         }
17281         
17282         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17283             // remove node.
17284             node.parentNode.removeChild(node);
17285             return;
17286             
17287         }
17288         
17289         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17290         
17291         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17292         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17293         
17294         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17295         //    remove_keep_children = true;
17296         //}
17297         
17298         if (remove_keep_children) {
17299             this.cleanUpChildren(node);
17300             // inserts everything just before this node...
17301             while (node.childNodes.length) {
17302                 var cn = node.childNodes[0];
17303                 node.removeChild(cn);
17304                 node.parentNode.insertBefore(cn, node);
17305             }
17306             node.parentNode.removeChild(node);
17307             return;
17308         }
17309         
17310         if (!node.attributes || !node.attributes.length) {
17311             this.cleanUpChildren(node);
17312             return;
17313         }
17314         
17315         function cleanAttr(n,v)
17316         {
17317             
17318             if (v.match(/^\./) || v.match(/^\//)) {
17319                 return;
17320             }
17321             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17322                 return;
17323             }
17324             if (v.match(/^#/)) {
17325                 return;
17326             }
17327 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17328             node.removeAttribute(n);
17329             
17330         }
17331         
17332         function cleanStyle(n,v)
17333         {
17334             if (v.match(/expression/)) { //XSS?? should we even bother..
17335                 node.removeAttribute(n);
17336                 return;
17337             }
17338             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17339             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17340             
17341             
17342             var parts = v.split(/;/);
17343             var clean = [];
17344             
17345             Roo.each(parts, function(p) {
17346                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17347                 if (!p.length) {
17348                     return true;
17349                 }
17350                 var l = p.split(':').shift().replace(/\s+/g,'');
17351                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17352                 
17353                 if ( cblack.indexOf(l) > -1) {
17354 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17355                     //node.removeAttribute(n);
17356                     return true;
17357                 }
17358                 //Roo.log()
17359                 // only allow 'c whitelisted system attributes'
17360                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17361 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17362                     //node.removeAttribute(n);
17363                     return true;
17364                 }
17365                 
17366                 
17367                  
17368                 
17369                 clean.push(p);
17370                 return true;
17371             });
17372             if (clean.length) { 
17373                 node.setAttribute(n, clean.join(';'));
17374             } else {
17375                 node.removeAttribute(n);
17376             }
17377             
17378         }
17379         
17380         
17381         for (var i = node.attributes.length-1; i > -1 ; i--) {
17382             var a = node.attributes[i];
17383             //console.log(a);
17384             
17385             if (a.name.toLowerCase().substr(0,2)=='on')  {
17386                 node.removeAttribute(a.name);
17387                 continue;
17388             }
17389             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17390                 node.removeAttribute(a.name);
17391                 continue;
17392             }
17393             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17394                 cleanAttr(a.name,a.value); // fixme..
17395                 continue;
17396             }
17397             if (a.name == 'style') {
17398                 cleanStyle(a.name,a.value);
17399                 continue;
17400             }
17401             /// clean up MS crap..
17402             // tecnically this should be a list of valid class'es..
17403             
17404             
17405             if (a.name == 'class') {
17406                 if (a.value.match(/^Mso/)) {
17407                     node.className = '';
17408                 }
17409                 
17410                 if (a.value.match(/body/)) {
17411                     node.className = '';
17412                 }
17413                 continue;
17414             }
17415             
17416             // style cleanup!?
17417             // class cleanup?
17418             
17419         }
17420         
17421         
17422         this.cleanUpChildren(node);
17423         
17424         
17425     },
17426     /**
17427      * Clean up MS wordisms...
17428      */
17429     cleanWord : function(node)
17430     {
17431         var _t = this;
17432         var cleanWordChildren = function()
17433         {
17434             if (!node.childNodes.length) {
17435                 return;
17436             }
17437             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17438                _t.cleanWord(node.childNodes[i]);
17439             }
17440         }
17441         
17442         
17443         if (!node) {
17444             this.cleanWord(this.doc.body);
17445             return;
17446         }
17447         if (node.nodeName == "#text") {
17448             // clean up silly Windows -- stuff?
17449             return; 
17450         }
17451         if (node.nodeName == "#comment") {
17452             node.parentNode.removeChild(node);
17453             // clean up silly Windows -- stuff?
17454             return; 
17455         }
17456         
17457         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17458             node.parentNode.removeChild(node);
17459             return;
17460         }
17461         
17462         // remove - but keep children..
17463         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17464             while (node.childNodes.length) {
17465                 var cn = node.childNodes[0];
17466                 node.removeChild(cn);
17467                 node.parentNode.insertBefore(cn, node);
17468             }
17469             node.parentNode.removeChild(node);
17470             cleanWordChildren();
17471             return;
17472         }
17473         // clean styles
17474         if (node.className.length) {
17475             
17476             var cn = node.className.split(/\W+/);
17477             var cna = [];
17478             Roo.each(cn, function(cls) {
17479                 if (cls.match(/Mso[a-zA-Z]+/)) {
17480                     return;
17481                 }
17482                 cna.push(cls);
17483             });
17484             node.className = cna.length ? cna.join(' ') : '';
17485             if (!cna.length) {
17486                 node.removeAttribute("class");
17487             }
17488         }
17489         
17490         if (node.hasAttribute("lang")) {
17491             node.removeAttribute("lang");
17492         }
17493         
17494         if (node.hasAttribute("style")) {
17495             
17496             var styles = node.getAttribute("style").split(";");
17497             var nstyle = [];
17498             Roo.each(styles, function(s) {
17499                 if (!s.match(/:/)) {
17500                     return;
17501                 }
17502                 var kv = s.split(":");
17503                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17504                     return;
17505                 }
17506                 // what ever is left... we allow.
17507                 nstyle.push(s);
17508             });
17509             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17510             if (!nstyle.length) {
17511                 node.removeAttribute('style');
17512             }
17513         }
17514         
17515         cleanWordChildren();
17516         
17517         
17518     },
17519     domToHTML : function(currentElement, depth, nopadtext) {
17520         
17521             depth = depth || 0;
17522             nopadtext = nopadtext || false;
17523         
17524             if (!currentElement) {
17525                 return this.domToHTML(this.doc.body);
17526             }
17527             
17528             //Roo.log(currentElement);
17529             var j;
17530             var allText = false;
17531             var nodeName = currentElement.nodeName;
17532             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17533             
17534             if  (nodeName == '#text') {
17535                 return currentElement.nodeValue;
17536             }
17537             
17538             
17539             var ret = '';
17540             if (nodeName != 'BODY') {
17541                  
17542                 var i = 0;
17543                 // Prints the node tagName, such as <A>, <IMG>, etc
17544                 if (tagName) {
17545                     var attr = [];
17546                     for(i = 0; i < currentElement.attributes.length;i++) {
17547                         // quoting?
17548                         var aname = currentElement.attributes.item(i).name;
17549                         if (!currentElement.attributes.item(i).value.length) {
17550                             continue;
17551                         }
17552                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17553                     }
17554                     
17555                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17556                 } 
17557                 else {
17558                     
17559                     // eack
17560                 }
17561             } else {
17562                 tagName = false;
17563             }
17564             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17565                 return ret;
17566             }
17567             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17568                 nopadtext = true;
17569             }
17570             
17571             
17572             // Traverse the tree
17573             i = 0;
17574             var currentElementChild = currentElement.childNodes.item(i);
17575             var allText = true;
17576             var innerHTML  = '';
17577             lastnode = '';
17578             while (currentElementChild) {
17579                 // Formatting code (indent the tree so it looks nice on the screen)
17580                 var nopad = nopadtext;
17581                 if (lastnode == 'SPAN') {
17582                     nopad  = true;
17583                 }
17584                 // text
17585                 if  (currentElementChild.nodeName == '#text') {
17586                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17587                     if (!nopad && toadd.length > 80) {
17588                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17589                     }
17590                     innerHTML  += toadd;
17591                     
17592                     i++;
17593                     currentElementChild = currentElement.childNodes.item(i);
17594                     lastNode = '';
17595                     continue;
17596                 }
17597                 allText = false;
17598                 
17599                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17600                     
17601                 // Recursively traverse the tree structure of the child node
17602                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17603                 lastnode = currentElementChild.nodeName;
17604                 i++;
17605                 currentElementChild=currentElement.childNodes.item(i);
17606             }
17607             
17608             ret += innerHTML;
17609             
17610             if (!allText) {
17611                     // The remaining code is mostly for formatting the tree
17612                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17613             }
17614             
17615             
17616             if (tagName) {
17617                 ret+= "</"+tagName+">";
17618             }
17619             return ret;
17620             
17621         }
17622     
17623     // hide stuff that is not compatible
17624     /**
17625      * @event blur
17626      * @hide
17627      */
17628     /**
17629      * @event change
17630      * @hide
17631      */
17632     /**
17633      * @event focus
17634      * @hide
17635      */
17636     /**
17637      * @event specialkey
17638      * @hide
17639      */
17640     /**
17641      * @cfg {String} fieldClass @hide
17642      */
17643     /**
17644      * @cfg {String} focusClass @hide
17645      */
17646     /**
17647      * @cfg {String} autoCreate @hide
17648      */
17649     /**
17650      * @cfg {String} inputType @hide
17651      */
17652     /**
17653      * @cfg {String} invalidClass @hide
17654      */
17655     /**
17656      * @cfg {String} invalidText @hide
17657      */
17658     /**
17659      * @cfg {String} msgFx @hide
17660      */
17661     /**
17662      * @cfg {String} validateOnBlur @hide
17663      */
17664 });
17665
17666 Roo.HtmlEditorCore.white = [
17667         'area', 'br', 'img', 'input', 'hr', 'wbr',
17668         
17669        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17670        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17671        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17672        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17673        'table',   'ul',         'xmp', 
17674        
17675        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17676       'thead',   'tr', 
17677      
17678       'dir', 'menu', 'ol', 'ul', 'dl',
17679        
17680       'embed',  'object'
17681 ];
17682
17683
17684 Roo.HtmlEditorCore.black = [
17685     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17686         'applet', // 
17687         'base',   'basefont', 'bgsound', 'blink',  'body', 
17688         'frame',  'frameset', 'head',    'html',   'ilayer', 
17689         'iframe', 'layer',  'link',     'meta',    'object',   
17690         'script', 'style' ,'title',  'xml' // clean later..
17691 ];
17692 Roo.HtmlEditorCore.clean = [
17693     'script', 'style', 'title', 'xml'
17694 ];
17695 Roo.HtmlEditorCore.remove = [
17696     'font'
17697 ];
17698 // attributes..
17699
17700 Roo.HtmlEditorCore.ablack = [
17701     'on'
17702 ];
17703     
17704 Roo.HtmlEditorCore.aclean = [ 
17705     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17706 ];
17707
17708 // protocols..
17709 Roo.HtmlEditorCore.pwhite= [
17710         'http',  'https',  'mailto'
17711 ];
17712
17713 // white listed style attributes.
17714 Roo.HtmlEditorCore.cwhite= [
17715       //  'text-align', /// default is to allow most things..
17716       
17717          
17718 //        'font-size'//??
17719 ];
17720
17721 // black listed style attributes.
17722 Roo.HtmlEditorCore.cblack= [
17723       //  'font-size' -- this can be set by the project 
17724 ];
17725
17726
17727 Roo.HtmlEditorCore.swapCodes   =[ 
17728     [    8211, "--" ], 
17729     [    8212, "--" ], 
17730     [    8216,  "'" ],  
17731     [    8217, "'" ],  
17732     [    8220, '"' ],  
17733     [    8221, '"' ],  
17734     [    8226, "*" ],  
17735     [    8230, "..." ]
17736 ]; 
17737
17738     /*
17739  * - LGPL
17740  *
17741  * HtmlEditor
17742  * 
17743  */
17744
17745 /**
17746  * @class Roo.bootstrap.HtmlEditor
17747  * @extends Roo.bootstrap.TextArea
17748  * Bootstrap HtmlEditor class
17749
17750  * @constructor
17751  * Create a new HtmlEditor
17752  * @param {Object} config The config object
17753  */
17754
17755 Roo.bootstrap.HtmlEditor = function(config){
17756     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17757     if (!this.toolbars) {
17758         this.toolbars = [];
17759     }
17760     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17761     this.addEvents({
17762             /**
17763              * @event initialize
17764              * Fires when the editor is fully initialized (including the iframe)
17765              * @param {HtmlEditor} this
17766              */
17767             initialize: true,
17768             /**
17769              * @event activate
17770              * Fires when the editor is first receives the focus. Any insertion must wait
17771              * until after this event.
17772              * @param {HtmlEditor} this
17773              */
17774             activate: true,
17775              /**
17776              * @event beforesync
17777              * Fires before the textarea is updated with content from the editor iframe. Return false
17778              * to cancel the sync.
17779              * @param {HtmlEditor} this
17780              * @param {String} html
17781              */
17782             beforesync: true,
17783              /**
17784              * @event beforepush
17785              * Fires before the iframe editor is updated with content from the textarea. Return false
17786              * to cancel the push.
17787              * @param {HtmlEditor} this
17788              * @param {String} html
17789              */
17790             beforepush: true,
17791              /**
17792              * @event sync
17793              * Fires when the textarea is updated with content from the editor iframe.
17794              * @param {HtmlEditor} this
17795              * @param {String} html
17796              */
17797             sync: true,
17798              /**
17799              * @event push
17800              * Fires when the iframe editor is updated with content from the textarea.
17801              * @param {HtmlEditor} this
17802              * @param {String} html
17803              */
17804             push: true,
17805              /**
17806              * @event editmodechange
17807              * Fires when the editor switches edit modes
17808              * @param {HtmlEditor} this
17809              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17810              */
17811             editmodechange: true,
17812             /**
17813              * @event editorevent
17814              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17815              * @param {HtmlEditor} this
17816              */
17817             editorevent: true,
17818             /**
17819              * @event firstfocus
17820              * Fires when on first focus - needed by toolbars..
17821              * @param {HtmlEditor} this
17822              */
17823             firstfocus: true,
17824             /**
17825              * @event autosave
17826              * Auto save the htmlEditor value as a file into Events
17827              * @param {HtmlEditor} this
17828              */
17829             autosave: true,
17830             /**
17831              * @event savedpreview
17832              * preview the saved version of htmlEditor
17833              * @param {HtmlEditor} this
17834              */
17835             savedpreview: true
17836         });
17837 };
17838
17839
17840 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17841     
17842     
17843       /**
17844      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17845      */
17846     toolbars : false,
17847    
17848      /**
17849      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17850      *                        Roo.resizable.
17851      */
17852     resizable : false,
17853      /**
17854      * @cfg {Number} height (in pixels)
17855      */   
17856     height: 300,
17857    /**
17858      * @cfg {Number} width (in pixels)
17859      */   
17860     width: false,
17861     
17862     /**
17863      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17864      * 
17865      */
17866     stylesheets: false,
17867     
17868     // id of frame..
17869     frameId: false,
17870     
17871     // private properties
17872     validationEvent : false,
17873     deferHeight: true,
17874     initialized : false,
17875     activated : false,
17876     
17877     onFocus : Roo.emptyFn,
17878     iframePad:3,
17879     hideMode:'offsets',
17880     
17881     
17882     tbContainer : false,
17883     
17884     toolbarContainer :function() {
17885         return this.wrap.select('.x-html-editor-tb',true).first();
17886     },
17887
17888     /**
17889      * Protected method that will not generally be called directly. It
17890      * is called when the editor creates its toolbar. Override this method if you need to
17891      * add custom toolbar buttons.
17892      * @param {HtmlEditor} editor
17893      */
17894     createToolbar : function(){
17895         
17896         Roo.log("create toolbars");
17897         
17898         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17899         this.toolbars[0].render(this.toolbarContainer());
17900         
17901         return;
17902         
17903 //        if (!editor.toolbars || !editor.toolbars.length) {
17904 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17905 //        }
17906 //        
17907 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17908 //            editor.toolbars[i] = Roo.factory(
17909 //                    typeof(editor.toolbars[i]) == 'string' ?
17910 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17911 //                Roo.bootstrap.HtmlEditor);
17912 //            editor.toolbars[i].init(editor);
17913 //        }
17914     },
17915
17916      
17917     // private
17918     onRender : function(ct, position)
17919     {
17920        // Roo.log("Call onRender: " + this.xtype);
17921         var _t = this;
17922         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17923       
17924         this.wrap = this.inputEl().wrap({
17925             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17926         });
17927         
17928         this.editorcore.onRender(ct, position);
17929          
17930         if (this.resizable) {
17931             this.resizeEl = new Roo.Resizable(this.wrap, {
17932                 pinned : true,
17933                 wrap: true,
17934                 dynamic : true,
17935                 minHeight : this.height,
17936                 height: this.height,
17937                 handles : this.resizable,
17938                 width: this.width,
17939                 listeners : {
17940                     resize : function(r, w, h) {
17941                         _t.onResize(w,h); // -something
17942                     }
17943                 }
17944             });
17945             
17946         }
17947         this.createToolbar(this);
17948        
17949         
17950         if(!this.width && this.resizable){
17951             this.setSize(this.wrap.getSize());
17952         }
17953         if (this.resizeEl) {
17954             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17955             // should trigger onReize..
17956         }
17957         
17958     },
17959
17960     // private
17961     onResize : function(w, h)
17962     {
17963         Roo.log('resize: ' +w + ',' + h );
17964         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17965         var ew = false;
17966         var eh = false;
17967         
17968         if(this.inputEl() ){
17969             if(typeof w == 'number'){
17970                 var aw = w - this.wrap.getFrameWidth('lr');
17971                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17972                 ew = aw;
17973             }
17974             if(typeof h == 'number'){
17975                  var tbh = -11;  // fixme it needs to tool bar size!
17976                 for (var i =0; i < this.toolbars.length;i++) {
17977                     // fixme - ask toolbars for heights?
17978                     tbh += this.toolbars[i].el.getHeight();
17979                     //if (this.toolbars[i].footer) {
17980                     //    tbh += this.toolbars[i].footer.el.getHeight();
17981                     //}
17982                 }
17983               
17984                 
17985                 
17986                 
17987                 
17988                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17989                 ah -= 5; // knock a few pixes off for look..
17990                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17991                 var eh = ah;
17992             }
17993         }
17994         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17995         this.editorcore.onResize(ew,eh);
17996         
17997     },
17998
17999     /**
18000      * Toggles the editor between standard and source edit mode.
18001      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18002      */
18003     toggleSourceEdit : function(sourceEditMode)
18004     {
18005         this.editorcore.toggleSourceEdit(sourceEditMode);
18006         
18007         if(this.editorcore.sourceEditMode){
18008             Roo.log('editor - showing textarea');
18009             
18010 //            Roo.log('in');
18011 //            Roo.log(this.syncValue());
18012             this.syncValue();
18013             this.inputEl().removeClass(['hide', 'x-hidden']);
18014             this.inputEl().dom.removeAttribute('tabIndex');
18015             this.inputEl().focus();
18016         }else{
18017             Roo.log('editor - hiding textarea');
18018 //            Roo.log('out')
18019 //            Roo.log(this.pushValue()); 
18020             this.pushValue();
18021             
18022             this.inputEl().addClass(['hide', 'x-hidden']);
18023             this.inputEl().dom.setAttribute('tabIndex', -1);
18024             //this.deferFocus();
18025         }
18026          
18027         if(this.resizable){
18028             this.setSize(this.wrap.getSize());
18029         }
18030         
18031         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18032     },
18033  
18034     // private (for BoxComponent)
18035     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18036
18037     // private (for BoxComponent)
18038     getResizeEl : function(){
18039         return this.wrap;
18040     },
18041
18042     // private (for BoxComponent)
18043     getPositionEl : function(){
18044         return this.wrap;
18045     },
18046
18047     // private
18048     initEvents : function(){
18049         this.originalValue = this.getValue();
18050     },
18051
18052 //    /**
18053 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18054 //     * @method
18055 //     */
18056 //    markInvalid : Roo.emptyFn,
18057 //    /**
18058 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18059 //     * @method
18060 //     */
18061 //    clearInvalid : Roo.emptyFn,
18062
18063     setValue : function(v){
18064         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18065         this.editorcore.pushValue();
18066     },
18067
18068      
18069     // private
18070     deferFocus : function(){
18071         this.focus.defer(10, this);
18072     },
18073
18074     // doc'ed in Field
18075     focus : function(){
18076         this.editorcore.focus();
18077         
18078     },
18079       
18080
18081     // private
18082     onDestroy : function(){
18083         
18084         
18085         
18086         if(this.rendered){
18087             
18088             for (var i =0; i < this.toolbars.length;i++) {
18089                 // fixme - ask toolbars for heights?
18090                 this.toolbars[i].onDestroy();
18091             }
18092             
18093             this.wrap.dom.innerHTML = '';
18094             this.wrap.remove();
18095         }
18096     },
18097
18098     // private
18099     onFirstFocus : function(){
18100         //Roo.log("onFirstFocus");
18101         this.editorcore.onFirstFocus();
18102          for (var i =0; i < this.toolbars.length;i++) {
18103             this.toolbars[i].onFirstFocus();
18104         }
18105         
18106     },
18107     
18108     // private
18109     syncValue : function()
18110     {   
18111         this.editorcore.syncValue();
18112     },
18113     
18114     pushValue : function()
18115     {   
18116         this.editorcore.pushValue();
18117     }
18118      
18119     
18120     // hide stuff that is not compatible
18121     /**
18122      * @event blur
18123      * @hide
18124      */
18125     /**
18126      * @event change
18127      * @hide
18128      */
18129     /**
18130      * @event focus
18131      * @hide
18132      */
18133     /**
18134      * @event specialkey
18135      * @hide
18136      */
18137     /**
18138      * @cfg {String} fieldClass @hide
18139      */
18140     /**
18141      * @cfg {String} focusClass @hide
18142      */
18143     /**
18144      * @cfg {String} autoCreate @hide
18145      */
18146     /**
18147      * @cfg {String} inputType @hide
18148      */
18149     /**
18150      * @cfg {String} invalidClass @hide
18151      */
18152     /**
18153      * @cfg {String} invalidText @hide
18154      */
18155     /**
18156      * @cfg {String} msgFx @hide
18157      */
18158     /**
18159      * @cfg {String} validateOnBlur @hide
18160      */
18161 });
18162  
18163     
18164    
18165    
18166    
18167       
18168 Roo.namespace('Roo.bootstrap.htmleditor');
18169 /**
18170  * @class Roo.bootstrap.HtmlEditorToolbar1
18171  * Basic Toolbar
18172  * 
18173  * Usage:
18174  *
18175  new Roo.bootstrap.HtmlEditor({
18176     ....
18177     toolbars : [
18178         new Roo.bootstrap.HtmlEditorToolbar1({
18179             disable : { fonts: 1 , format: 1, ..., ... , ...],
18180             btns : [ .... ]
18181         })
18182     }
18183      
18184  * 
18185  * @cfg {Object} disable List of elements to disable..
18186  * @cfg {Array} btns List of additional buttons.
18187  * 
18188  * 
18189  * NEEDS Extra CSS? 
18190  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18191  */
18192  
18193 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18194 {
18195     
18196     Roo.apply(this, config);
18197     
18198     // default disabled, based on 'good practice'..
18199     this.disable = this.disable || {};
18200     Roo.applyIf(this.disable, {
18201         fontSize : true,
18202         colors : true,
18203         specialElements : true
18204     });
18205     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18206     
18207     this.editor = config.editor;
18208     this.editorcore = config.editor.editorcore;
18209     
18210     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18211     
18212     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18213     // dont call parent... till later.
18214 }
18215 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18216      
18217     bar : true,
18218     
18219     editor : false,
18220     editorcore : false,
18221     
18222     
18223     formats : [
18224         "p" ,  
18225         "h1","h2","h3","h4","h5","h6", 
18226         "pre", "code", 
18227         "abbr", "acronym", "address", "cite", "samp", "var",
18228         'div','span'
18229     ],
18230     
18231     onRender : function(ct, position)
18232     {
18233        // Roo.log("Call onRender: " + this.xtype);
18234         
18235        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18236        Roo.log(this.el);
18237        this.el.dom.style.marginBottom = '0';
18238        var _this = this;
18239        var editorcore = this.editorcore;
18240        var editor= this.editor;
18241        
18242        var children = [];
18243        var btn = function(id,cmd , toggle, handler){
18244        
18245             var  event = toggle ? 'toggle' : 'click';
18246        
18247             var a = {
18248                 size : 'sm',
18249                 xtype: 'Button',
18250                 xns: Roo.bootstrap,
18251                 glyphicon : id,
18252                 cmd : id || cmd,
18253                 enableToggle:toggle !== false,
18254                 //html : 'submit'
18255                 pressed : toggle ? false : null,
18256                 listeners : {}
18257             }
18258             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18259                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18260             }
18261             children.push(a);
18262             return a;
18263        }
18264         
18265         var style = {
18266                 xtype: 'Button',
18267                 size : 'sm',
18268                 xns: Roo.bootstrap,
18269                 glyphicon : 'font',
18270                 //html : 'submit'
18271                 menu : {
18272                     xtype: 'Menu',
18273                     xns: Roo.bootstrap,
18274                     items:  []
18275                 }
18276         };
18277         Roo.each(this.formats, function(f) {
18278             style.menu.items.push({
18279                 xtype :'MenuItem',
18280                 xns: Roo.bootstrap,
18281                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18282                 tagname : f,
18283                 listeners : {
18284                     click : function()
18285                     {
18286                         editorcore.insertTag(this.tagname);
18287                         editor.focus();
18288                     }
18289                 }
18290                 
18291             });
18292         });
18293          children.push(style);   
18294             
18295             
18296         btn('bold',false,true);
18297         btn('italic',false,true);
18298         btn('align-left', 'justifyleft',true);
18299         btn('align-center', 'justifycenter',true);
18300         btn('align-right' , 'justifyright',true);
18301         btn('link', false, false, function(btn) {
18302             //Roo.log("create link?");
18303             var url = prompt(this.createLinkText, this.defaultLinkValue);
18304             if(url && url != 'http:/'+'/'){
18305                 this.editorcore.relayCmd('createlink', url);
18306             }
18307         }),
18308         btn('list','insertunorderedlist',true);
18309         btn('pencil', false,true, function(btn){
18310                 Roo.log(this);
18311                 
18312                 this.toggleSourceEdit(btn.pressed);
18313         });
18314         /*
18315         var cog = {
18316                 xtype: 'Button',
18317                 size : 'sm',
18318                 xns: Roo.bootstrap,
18319                 glyphicon : 'cog',
18320                 //html : 'submit'
18321                 menu : {
18322                     xtype: 'Menu',
18323                     xns: Roo.bootstrap,
18324                     items:  []
18325                 }
18326         };
18327         
18328         cog.menu.items.push({
18329             xtype :'MenuItem',
18330             xns: Roo.bootstrap,
18331             html : Clean styles,
18332             tagname : f,
18333             listeners : {
18334                 click : function()
18335                 {
18336                     editorcore.insertTag(this.tagname);
18337                     editor.focus();
18338                 }
18339             }
18340             
18341         });
18342        */
18343         
18344          
18345        this.xtype = 'NavSimplebar';
18346         
18347         for(var i=0;i< children.length;i++) {
18348             
18349             this.buttons.add(this.addxtypeChild(children[i]));
18350             
18351         }
18352         
18353         editor.on('editorevent', this.updateToolbar, this);
18354     },
18355     onBtnClick : function(id)
18356     {
18357        this.editorcore.relayCmd(id);
18358        this.editorcore.focus();
18359     },
18360     
18361     /**
18362      * Protected method that will not generally be called directly. It triggers
18363      * a toolbar update by reading the markup state of the current selection in the editor.
18364      */
18365     updateToolbar: function(){
18366
18367         if(!this.editorcore.activated){
18368             this.editor.onFirstFocus(); // is this neeed?
18369             return;
18370         }
18371
18372         var btns = this.buttons; 
18373         var doc = this.editorcore.doc;
18374         btns.get('bold').setActive(doc.queryCommandState('bold'));
18375         btns.get('italic').setActive(doc.queryCommandState('italic'));
18376         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18377         
18378         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18379         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18380         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18381         
18382         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18383         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18384          /*
18385         
18386         var ans = this.editorcore.getAllAncestors();
18387         if (this.formatCombo) {
18388             
18389             
18390             var store = this.formatCombo.store;
18391             this.formatCombo.setValue("");
18392             for (var i =0; i < ans.length;i++) {
18393                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18394                     // select it..
18395                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18396                     break;
18397                 }
18398             }
18399         }
18400         
18401         
18402         
18403         // hides menus... - so this cant be on a menu...
18404         Roo.bootstrap.MenuMgr.hideAll();
18405         */
18406         Roo.bootstrap.MenuMgr.hideAll();
18407         //this.editorsyncValue();
18408     },
18409     onFirstFocus: function() {
18410         this.buttons.each(function(item){
18411            item.enable();
18412         });
18413     },
18414     toggleSourceEdit : function(sourceEditMode){
18415         
18416           
18417         if(sourceEditMode){
18418             Roo.log("disabling buttons");
18419            this.buttons.each( function(item){
18420                 if(item.cmd != 'pencil'){
18421                     item.disable();
18422                 }
18423             });
18424           
18425         }else{
18426             Roo.log("enabling buttons");
18427             if(this.editorcore.initialized){
18428                 this.buttons.each( function(item){
18429                     item.enable();
18430                 });
18431             }
18432             
18433         }
18434         Roo.log("calling toggole on editor");
18435         // tell the editor that it's been pressed..
18436         this.editor.toggleSourceEdit(sourceEditMode);
18437        
18438     }
18439 });
18440
18441
18442
18443
18444
18445 /**
18446  * @class Roo.bootstrap.Table.AbstractSelectionModel
18447  * @extends Roo.util.Observable
18448  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18449  * implemented by descendant classes.  This class should not be directly instantiated.
18450  * @constructor
18451  */
18452 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18453     this.locked = false;
18454     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18455 };
18456
18457
18458 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18459     /** @ignore Called by the grid automatically. Do not call directly. */
18460     init : function(grid){
18461         this.grid = grid;
18462         this.initEvents();
18463     },
18464
18465     /**
18466      * Locks the selections.
18467      */
18468     lock : function(){
18469         this.locked = true;
18470     },
18471
18472     /**
18473      * Unlocks the selections.
18474      */
18475     unlock : function(){
18476         this.locked = false;
18477     },
18478
18479     /**
18480      * Returns true if the selections are locked.
18481      * @return {Boolean}
18482      */
18483     isLocked : function(){
18484         return this.locked;
18485     }
18486 });
18487 /**
18488  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18489  * @class Roo.bootstrap.Table.RowSelectionModel
18490  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18491  * It supports multiple selections and keyboard selection/navigation. 
18492  * @constructor
18493  * @param {Object} config
18494  */
18495
18496 Roo.bootstrap.Table.RowSelectionModel = function(config){
18497     Roo.apply(this, config);
18498     this.selections = new Roo.util.MixedCollection(false, function(o){
18499         return o.id;
18500     });
18501
18502     this.last = false;
18503     this.lastActive = false;
18504
18505     this.addEvents({
18506         /**
18507              * @event selectionchange
18508              * Fires when the selection changes
18509              * @param {SelectionModel} this
18510              */
18511             "selectionchange" : true,
18512         /**
18513              * @event afterselectionchange
18514              * Fires after the selection changes (eg. by key press or clicking)
18515              * @param {SelectionModel} this
18516              */
18517             "afterselectionchange" : true,
18518         /**
18519              * @event beforerowselect
18520              * Fires when a row is selected being selected, return false to cancel.
18521              * @param {SelectionModel} this
18522              * @param {Number} rowIndex The selected index
18523              * @param {Boolean} keepExisting False if other selections will be cleared
18524              */
18525             "beforerowselect" : true,
18526         /**
18527              * @event rowselect
18528              * Fires when a row is selected.
18529              * @param {SelectionModel} this
18530              * @param {Number} rowIndex The selected index
18531              * @param {Roo.data.Record} r The record
18532              */
18533             "rowselect" : true,
18534         /**
18535              * @event rowdeselect
18536              * Fires when a row is deselected.
18537              * @param {SelectionModel} this
18538              * @param {Number} rowIndex The selected index
18539              */
18540         "rowdeselect" : true
18541     });
18542     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18543     this.locked = false;
18544 };
18545
18546 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18547     /**
18548      * @cfg {Boolean} singleSelect
18549      * True to allow selection of only one row at a time (defaults to false)
18550      */
18551     singleSelect : false,
18552
18553     // private
18554     initEvents : function(){
18555
18556         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18557             this.grid.on("mousedown", this.handleMouseDown, this);
18558         }else{ // allow click to work like normal
18559             this.grid.on("rowclick", this.handleDragableRowClick, this);
18560         }
18561
18562         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18563             "up" : function(e){
18564                 if(!e.shiftKey){
18565                     this.selectPrevious(e.shiftKey);
18566                 }else if(this.last !== false && this.lastActive !== false){
18567                     var last = this.last;
18568                     this.selectRange(this.last,  this.lastActive-1);
18569                     this.grid.getView().focusRow(this.lastActive);
18570                     if(last !== false){
18571                         this.last = last;
18572                     }
18573                 }else{
18574                     this.selectFirstRow();
18575                 }
18576                 this.fireEvent("afterselectionchange", this);
18577             },
18578             "down" : function(e){
18579                 if(!e.shiftKey){
18580                     this.selectNext(e.shiftKey);
18581                 }else if(this.last !== false && this.lastActive !== false){
18582                     var last = this.last;
18583                     this.selectRange(this.last,  this.lastActive+1);
18584                     this.grid.getView().focusRow(this.lastActive);
18585                     if(last !== false){
18586                         this.last = last;
18587                     }
18588                 }else{
18589                     this.selectFirstRow();
18590                 }
18591                 this.fireEvent("afterselectionchange", this);
18592             },
18593             scope: this
18594         });
18595
18596         var view = this.grid.view;
18597         view.on("refresh", this.onRefresh, this);
18598         view.on("rowupdated", this.onRowUpdated, this);
18599         view.on("rowremoved", this.onRemove, this);
18600     },
18601
18602     // private
18603     onRefresh : function(){
18604         var ds = this.grid.dataSource, i, v = this.grid.view;
18605         var s = this.selections;
18606         s.each(function(r){
18607             if((i = ds.indexOfId(r.id)) != -1){
18608                 v.onRowSelect(i);
18609             }else{
18610                 s.remove(r);
18611             }
18612         });
18613     },
18614
18615     // private
18616     onRemove : function(v, index, r){
18617         this.selections.remove(r);
18618     },
18619
18620     // private
18621     onRowUpdated : function(v, index, r){
18622         if(this.isSelected(r)){
18623             v.onRowSelect(index);
18624         }
18625     },
18626
18627     /**
18628      * Select records.
18629      * @param {Array} records The records to select
18630      * @param {Boolean} keepExisting (optional) True to keep existing selections
18631      */
18632     selectRecords : function(records, keepExisting){
18633         if(!keepExisting){
18634             this.clearSelections();
18635         }
18636         var ds = this.grid.dataSource;
18637         for(var i = 0, len = records.length; i < len; i++){
18638             this.selectRow(ds.indexOf(records[i]), true);
18639         }
18640     },
18641
18642     /**
18643      * Gets the number of selected rows.
18644      * @return {Number}
18645      */
18646     getCount : function(){
18647         return this.selections.length;
18648     },
18649
18650     /**
18651      * Selects the first row in the grid.
18652      */
18653     selectFirstRow : function(){
18654         this.selectRow(0);
18655     },
18656
18657     /**
18658      * Select the last row.
18659      * @param {Boolean} keepExisting (optional) True to keep existing selections
18660      */
18661     selectLastRow : function(keepExisting){
18662         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18663     },
18664
18665     /**
18666      * Selects the row immediately following the last selected row.
18667      * @param {Boolean} keepExisting (optional) True to keep existing selections
18668      */
18669     selectNext : function(keepExisting){
18670         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18671             this.selectRow(this.last+1, keepExisting);
18672             this.grid.getView().focusRow(this.last);
18673         }
18674     },
18675
18676     /**
18677      * Selects the row that precedes the last selected row.
18678      * @param {Boolean} keepExisting (optional) True to keep existing selections
18679      */
18680     selectPrevious : function(keepExisting){
18681         if(this.last){
18682             this.selectRow(this.last-1, keepExisting);
18683             this.grid.getView().focusRow(this.last);
18684         }
18685     },
18686
18687     /**
18688      * Returns the selected records
18689      * @return {Array} Array of selected records
18690      */
18691     getSelections : function(){
18692         return [].concat(this.selections.items);
18693     },
18694
18695     /**
18696      * Returns the first selected record.
18697      * @return {Record}
18698      */
18699     getSelected : function(){
18700         return this.selections.itemAt(0);
18701     },
18702
18703
18704     /**
18705      * Clears all selections.
18706      */
18707     clearSelections : function(fast){
18708         if(this.locked) return;
18709         if(fast !== true){
18710             var ds = this.grid.dataSource;
18711             var s = this.selections;
18712             s.each(function(r){
18713                 this.deselectRow(ds.indexOfId(r.id));
18714             }, this);
18715             s.clear();
18716         }else{
18717             this.selections.clear();
18718         }
18719         this.last = false;
18720     },
18721
18722
18723     /**
18724      * Selects all rows.
18725      */
18726     selectAll : function(){
18727         if(this.locked) return;
18728         this.selections.clear();
18729         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18730             this.selectRow(i, true);
18731         }
18732     },
18733
18734     /**
18735      * Returns True if there is a selection.
18736      * @return {Boolean}
18737      */
18738     hasSelection : function(){
18739         return this.selections.length > 0;
18740     },
18741
18742     /**
18743      * Returns True if the specified row is selected.
18744      * @param {Number/Record} record The record or index of the record to check
18745      * @return {Boolean}
18746      */
18747     isSelected : function(index){
18748         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18749         return (r && this.selections.key(r.id) ? true : false);
18750     },
18751
18752     /**
18753      * Returns True if the specified record id is selected.
18754      * @param {String} id The id of record to check
18755      * @return {Boolean}
18756      */
18757     isIdSelected : function(id){
18758         return (this.selections.key(id) ? true : false);
18759     },
18760
18761     // private
18762     handleMouseDown : function(e, t){
18763         var view = this.grid.getView(), rowIndex;
18764         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18765             return;
18766         };
18767         if(e.shiftKey && this.last !== false){
18768             var last = this.last;
18769             this.selectRange(last, rowIndex, e.ctrlKey);
18770             this.last = last; // reset the last
18771             view.focusRow(rowIndex);
18772         }else{
18773             var isSelected = this.isSelected(rowIndex);
18774             if(e.button !== 0 && isSelected){
18775                 view.focusRow(rowIndex);
18776             }else if(e.ctrlKey && isSelected){
18777                 this.deselectRow(rowIndex);
18778             }else if(!isSelected){
18779                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18780                 view.focusRow(rowIndex);
18781             }
18782         }
18783         this.fireEvent("afterselectionchange", this);
18784     },
18785     // private
18786     handleDragableRowClick :  function(grid, rowIndex, e) 
18787     {
18788         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18789             this.selectRow(rowIndex, false);
18790             grid.view.focusRow(rowIndex);
18791              this.fireEvent("afterselectionchange", this);
18792         }
18793     },
18794     
18795     /**
18796      * Selects multiple rows.
18797      * @param {Array} rows Array of the indexes of the row to select
18798      * @param {Boolean} keepExisting (optional) True to keep existing selections
18799      */
18800     selectRows : function(rows, keepExisting){
18801         if(!keepExisting){
18802             this.clearSelections();
18803         }
18804         for(var i = 0, len = rows.length; i < len; i++){
18805             this.selectRow(rows[i], true);
18806         }
18807     },
18808
18809     /**
18810      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18811      * @param {Number} startRow The index of the first row in the range
18812      * @param {Number} endRow The index of the last row in the range
18813      * @param {Boolean} keepExisting (optional) True to retain existing selections
18814      */
18815     selectRange : function(startRow, endRow, keepExisting){
18816         if(this.locked) return;
18817         if(!keepExisting){
18818             this.clearSelections();
18819         }
18820         if(startRow <= endRow){
18821             for(var i = startRow; i <= endRow; i++){
18822                 this.selectRow(i, true);
18823             }
18824         }else{
18825             for(var i = startRow; i >= endRow; i--){
18826                 this.selectRow(i, true);
18827             }
18828         }
18829     },
18830
18831     /**
18832      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18833      * @param {Number} startRow The index of the first row in the range
18834      * @param {Number} endRow The index of the last row in the range
18835      */
18836     deselectRange : function(startRow, endRow, preventViewNotify){
18837         if(this.locked) return;
18838         for(var i = startRow; i <= endRow; i++){
18839             this.deselectRow(i, preventViewNotify);
18840         }
18841     },
18842
18843     /**
18844      * Selects a row.
18845      * @param {Number} row The index of the row to select
18846      * @param {Boolean} keepExisting (optional) True to keep existing selections
18847      */
18848     selectRow : function(index, keepExisting, preventViewNotify){
18849         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18850         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18851             if(!keepExisting || this.singleSelect){
18852                 this.clearSelections();
18853             }
18854             var r = this.grid.dataSource.getAt(index);
18855             this.selections.add(r);
18856             this.last = this.lastActive = index;
18857             if(!preventViewNotify){
18858                 this.grid.getView().onRowSelect(index);
18859             }
18860             this.fireEvent("rowselect", this, index, r);
18861             this.fireEvent("selectionchange", this);
18862         }
18863     },
18864
18865     /**
18866      * Deselects a row.
18867      * @param {Number} row The index of the row to deselect
18868      */
18869     deselectRow : function(index, preventViewNotify){
18870         if(this.locked) return;
18871         if(this.last == index){
18872             this.last = false;
18873         }
18874         if(this.lastActive == index){
18875             this.lastActive = false;
18876         }
18877         var r = this.grid.dataSource.getAt(index);
18878         this.selections.remove(r);
18879         if(!preventViewNotify){
18880             this.grid.getView().onRowDeselect(index);
18881         }
18882         this.fireEvent("rowdeselect", this, index);
18883         this.fireEvent("selectionchange", this);
18884     },
18885
18886     // private
18887     restoreLast : function(){
18888         if(this._last){
18889             this.last = this._last;
18890         }
18891     },
18892
18893     // private
18894     acceptsNav : function(row, col, cm){
18895         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18896     },
18897
18898     // private
18899     onEditorKey : function(field, e){
18900         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18901         if(k == e.TAB){
18902             e.stopEvent();
18903             ed.completeEdit();
18904             if(e.shiftKey){
18905                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18906             }else{
18907                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18908             }
18909         }else if(k == e.ENTER && !e.ctrlKey){
18910             e.stopEvent();
18911             ed.completeEdit();
18912             if(e.shiftKey){
18913                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18914             }else{
18915                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18916             }
18917         }else if(k == e.ESC){
18918             ed.cancelEdit();
18919         }
18920         if(newCell){
18921             g.startEditing(newCell[0], newCell[1]);
18922         }
18923     }
18924 });/*
18925  * Based on:
18926  * Ext JS Library 1.1.1
18927  * Copyright(c) 2006-2007, Ext JS, LLC.
18928  *
18929  * Originally Released Under LGPL - original licence link has changed is not relivant.
18930  *
18931  * Fork - LGPL
18932  * <script type="text/javascript">
18933  */
18934  
18935 /**
18936  * @class Roo.bootstrap.PagingToolbar
18937  * @extends Roo.Row
18938  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18939  * @constructor
18940  * Create a new PagingToolbar
18941  * @param {Object} config The config object
18942  */
18943 Roo.bootstrap.PagingToolbar = function(config)
18944 {
18945     // old args format still supported... - xtype is prefered..
18946         // created from xtype...
18947     var ds = config.dataSource;
18948     this.toolbarItems = [];
18949     if (config.items) {
18950         this.toolbarItems = config.items;
18951 //        config.items = [];
18952     }
18953     
18954     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18955     this.ds = ds;
18956     this.cursor = 0;
18957     if (ds) { 
18958         this.bind(ds);
18959     }
18960     
18961     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18962     
18963 };
18964
18965 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18966     /**
18967      * @cfg {Roo.data.Store} dataSource
18968      * The underlying data store providing the paged data
18969      */
18970     /**
18971      * @cfg {String/HTMLElement/Element} container
18972      * container The id or element that will contain the toolbar
18973      */
18974     /**
18975      * @cfg {Boolean} displayInfo
18976      * True to display the displayMsg (defaults to false)
18977      */
18978     /**
18979      * @cfg {Number} pageSize
18980      * The number of records to display per page (defaults to 20)
18981      */
18982     pageSize: 20,
18983     /**
18984      * @cfg {String} displayMsg
18985      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18986      */
18987     displayMsg : 'Displaying {0} - {1} of {2}',
18988     /**
18989      * @cfg {String} emptyMsg
18990      * The message to display when no records are found (defaults to "No data to display")
18991      */
18992     emptyMsg : 'No data to display',
18993     /**
18994      * Customizable piece of the default paging text (defaults to "Page")
18995      * @type String
18996      */
18997     beforePageText : "Page",
18998     /**
18999      * Customizable piece of the default paging text (defaults to "of %0")
19000      * @type String
19001      */
19002     afterPageText : "of {0}",
19003     /**
19004      * Customizable piece of the default paging text (defaults to "First Page")
19005      * @type String
19006      */
19007     firstText : "First Page",
19008     /**
19009      * Customizable piece of the default paging text (defaults to "Previous Page")
19010      * @type String
19011      */
19012     prevText : "Previous Page",
19013     /**
19014      * Customizable piece of the default paging text (defaults to "Next Page")
19015      * @type String
19016      */
19017     nextText : "Next Page",
19018     /**
19019      * Customizable piece of the default paging text (defaults to "Last Page")
19020      * @type String
19021      */
19022     lastText : "Last Page",
19023     /**
19024      * Customizable piece of the default paging text (defaults to "Refresh")
19025      * @type String
19026      */
19027     refreshText : "Refresh",
19028
19029     buttons : false,
19030     // private
19031     onRender : function(ct, position) 
19032     {
19033         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19034         this.navgroup.parentId = this.id;
19035         this.navgroup.onRender(this.el, null);
19036         // add the buttons to the navgroup
19037         
19038         if(this.displayInfo){
19039             Roo.log(this.el.select('ul.navbar-nav',true).first());
19040             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19041             this.displayEl = this.el.select('.x-paging-info', true).first();
19042 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19043 //            this.displayEl = navel.el.select('span',true).first();
19044         }
19045         
19046         var _this = this;
19047         
19048         if(this.buttons){
19049             Roo.each(_this.buttons, function(e){
19050                Roo.factory(e).onRender(_this.el, null);
19051             });
19052         }
19053             
19054         Roo.each(_this.toolbarItems, function(e) {
19055             _this.navgroup.addItem(e);
19056         });
19057         
19058         this.first = this.navgroup.addItem({
19059             tooltip: this.firstText,
19060             cls: "prev",
19061             icon : 'fa fa-backward',
19062             disabled: true,
19063             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19064         });
19065         
19066         this.prev =  this.navgroup.addItem({
19067             tooltip: this.prevText,
19068             cls: "prev",
19069             icon : 'fa fa-step-backward',
19070             disabled: true,
19071             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19072         });
19073     //this.addSeparator();
19074         
19075         
19076         var field = this.navgroup.addItem( {
19077             tagtype : 'span',
19078             cls : 'x-paging-position',
19079             
19080             html : this.beforePageText  +
19081                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19082                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19083          } ); //?? escaped?
19084         
19085         this.field = field.el.select('input', true).first();
19086         this.field.on("keydown", this.onPagingKeydown, this);
19087         this.field.on("focus", function(){this.dom.select();});
19088     
19089     
19090         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19091         //this.field.setHeight(18);
19092         //this.addSeparator();
19093         this.next = this.navgroup.addItem({
19094             tooltip: this.nextText,
19095             cls: "next",
19096             html : ' <i class="fa fa-step-forward">',
19097             disabled: true,
19098             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19099         });
19100         this.last = this.navgroup.addItem({
19101             tooltip: this.lastText,
19102             icon : 'fa fa-forward',
19103             cls: "next",
19104             disabled: true,
19105             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19106         });
19107     //this.addSeparator();
19108         this.loading = this.navgroup.addItem({
19109             tooltip: this.refreshText,
19110             icon: 'fa fa-refresh',
19111             
19112             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19113         });
19114
19115     },
19116
19117     // private
19118     updateInfo : function(){
19119         if(this.displayEl){
19120             var count = this.ds.getCount();
19121             var msg = count == 0 ?
19122                 this.emptyMsg :
19123                 String.format(
19124                     this.displayMsg,
19125                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19126                 );
19127             this.displayEl.update(msg);
19128         }
19129     },
19130
19131     // private
19132     onLoad : function(ds, r, o){
19133        this.cursor = o.params ? o.params.start : 0;
19134        var d = this.getPageData(),
19135             ap = d.activePage,
19136             ps = d.pages;
19137         
19138        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19139        this.field.dom.value = ap;
19140        this.first.setDisabled(ap == 1);
19141        this.prev.setDisabled(ap == 1);
19142        this.next.setDisabled(ap == ps);
19143        this.last.setDisabled(ap == ps);
19144        this.loading.enable();
19145        this.updateInfo();
19146     },
19147
19148     // private
19149     getPageData : function(){
19150         var total = this.ds.getTotalCount();
19151         return {
19152             total : total,
19153             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19154             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19155         };
19156     },
19157
19158     // private
19159     onLoadError : function(){
19160         this.loading.enable();
19161     },
19162
19163     // private
19164     onPagingKeydown : function(e){
19165         var k = e.getKey();
19166         var d = this.getPageData();
19167         if(k == e.RETURN){
19168             var v = this.field.dom.value, pageNum;
19169             if(!v || isNaN(pageNum = parseInt(v, 10))){
19170                 this.field.dom.value = d.activePage;
19171                 return;
19172             }
19173             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19174             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19175             e.stopEvent();
19176         }
19177         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))
19178         {
19179           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19180           this.field.dom.value = pageNum;
19181           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19182           e.stopEvent();
19183         }
19184         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19185         {
19186           var v = this.field.dom.value, pageNum; 
19187           var increment = (e.shiftKey) ? 10 : 1;
19188           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19189             increment *= -1;
19190           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19191             this.field.dom.value = d.activePage;
19192             return;
19193           }
19194           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19195           {
19196             this.field.dom.value = parseInt(v, 10) + increment;
19197             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19198             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19199           }
19200           e.stopEvent();
19201         }
19202     },
19203
19204     // private
19205     beforeLoad : function(){
19206         if(this.loading){
19207             this.loading.disable();
19208         }
19209     },
19210
19211     // private
19212     onClick : function(which){
19213         var ds = this.ds;
19214         if (!ds) {
19215             return;
19216         }
19217         switch(which){
19218             case "first":
19219                 ds.load({params:{start: 0, limit: this.pageSize}});
19220             break;
19221             case "prev":
19222                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19223             break;
19224             case "next":
19225                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19226             break;
19227             case "last":
19228                 var total = ds.getTotalCount();
19229                 var extra = total % this.pageSize;
19230                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19231                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19232             break;
19233             case "refresh":
19234                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19235             break;
19236         }
19237     },
19238
19239     /**
19240      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19241      * @param {Roo.data.Store} store The data store to unbind
19242      */
19243     unbind : function(ds){
19244         ds.un("beforeload", this.beforeLoad, this);
19245         ds.un("load", this.onLoad, this);
19246         ds.un("loadexception", this.onLoadError, this);
19247         ds.un("remove", this.updateInfo, this);
19248         ds.un("add", this.updateInfo, this);
19249         this.ds = undefined;
19250     },
19251
19252     /**
19253      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19254      * @param {Roo.data.Store} store The data store to bind
19255      */
19256     bind : function(ds){
19257         ds.on("beforeload", this.beforeLoad, this);
19258         ds.on("load", this.onLoad, this);
19259         ds.on("loadexception", this.onLoadError, this);
19260         ds.on("remove", this.updateInfo, this);
19261         ds.on("add", this.updateInfo, this);
19262         this.ds = ds;
19263     }
19264 });/*
19265  * - LGPL
19266  *
19267  * element
19268  * 
19269  */
19270
19271 /**
19272  * @class Roo.bootstrap.MessageBar
19273  * @extends Roo.bootstrap.Component
19274  * Bootstrap MessageBar class
19275  * @cfg {String} html contents of the MessageBar
19276  * @cfg {String} weight (info | success | warning | danger) default info
19277  * @cfg {String} beforeClass insert the bar before the given class
19278  * @cfg {Boolean} closable (true | false) default false
19279  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19280  * 
19281  * @constructor
19282  * Create a new Element
19283  * @param {Object} config The config object
19284  */
19285
19286 Roo.bootstrap.MessageBar = function(config){
19287     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19288 };
19289
19290 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19291     
19292     html: '',
19293     weight: 'info',
19294     closable: false,
19295     fixed: false,
19296     beforeClass: 'bootstrap-sticky-wrap',
19297     
19298     getAutoCreate : function(){
19299         
19300         var cfg = {
19301             tag: 'div',
19302             cls: 'alert alert-dismissable alert-' + this.weight,
19303             cn: [
19304                 {
19305                     tag: 'span',
19306                     cls: 'message',
19307                     html: this.html || ''
19308                 }
19309             ]
19310         }
19311         
19312         if(this.fixed){
19313             cfg.cls += ' alert-messages-fixed';
19314         }
19315         
19316         if(this.closable){
19317             cfg.cn.push({
19318                 tag: 'button',
19319                 cls: 'close',
19320                 html: 'x'
19321             });
19322         }
19323         
19324         return cfg;
19325     },
19326     
19327     onRender : function(ct, position)
19328     {
19329         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19330         
19331         if(!this.el){
19332             var cfg = Roo.apply({},  this.getAutoCreate());
19333             cfg.id = Roo.id();
19334             
19335             if (this.cls) {
19336                 cfg.cls += ' ' + this.cls;
19337             }
19338             if (this.style) {
19339                 cfg.style = this.style;
19340             }
19341             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19342             
19343             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19344         }
19345         
19346         this.el.select('>button.close').on('click', this.hide, this);
19347         
19348     },
19349     
19350     show : function()
19351     {
19352         if (!this.rendered) {
19353             this.render();
19354         }
19355         
19356         this.el.show();
19357         
19358         this.fireEvent('show', this);
19359         
19360     },
19361     
19362     hide : function()
19363     {
19364         if (!this.rendered) {
19365             this.render();
19366         }
19367         
19368         this.el.hide();
19369         
19370         this.fireEvent('hide', this);
19371     },
19372     
19373     update : function()
19374     {
19375 //        var e = this.el.dom.firstChild;
19376 //        
19377 //        if(this.closable){
19378 //            e = e.nextSibling;
19379 //        }
19380 //        
19381 //        e.data = this.html || '';
19382
19383         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19384     }
19385    
19386 });
19387
19388  
19389
19390      /*
19391  * - LGPL
19392  *
19393  * Graph
19394  * 
19395  */
19396
19397
19398 /**
19399  * @class Roo.bootstrap.Graph
19400  * @extends Roo.bootstrap.Component
19401  * Bootstrap Graph class
19402 > Prameters
19403  -sm {number} sm 4
19404  -md {number} md 5
19405  @cfg {String} graphtype  bar | vbar | pie
19406  @cfg {number} g_x coodinator | centre x (pie)
19407  @cfg {number} g_y coodinator | centre y (pie)
19408  @cfg {number} g_r radius (pie)
19409  @cfg {number} g_height height of the chart (respected by all elements in the set)
19410  @cfg {number} g_width width of the chart (respected by all elements in the set)
19411  @cfg {Object} title The title of the chart
19412     
19413  -{Array}  values
19414  -opts (object) options for the chart 
19415      o {
19416      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19417      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19418      o vgutter (number)
19419      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.
19420      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19421      o to
19422      o stretch (boolean)
19423      o }
19424  -opts (object) options for the pie
19425      o{
19426      o cut
19427      o startAngle (number)
19428      o endAngle (number)
19429      } 
19430  *
19431  * @constructor
19432  * Create a new Input
19433  * @param {Object} config The config object
19434  */
19435
19436 Roo.bootstrap.Graph = function(config){
19437     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19438     
19439     this.addEvents({
19440         // img events
19441         /**
19442          * @event click
19443          * The img click event for the img.
19444          * @param {Roo.EventObject} e
19445          */
19446         "click" : true
19447     });
19448 };
19449
19450 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19451     
19452     sm: 4,
19453     md: 5,
19454     graphtype: 'bar',
19455     g_height: 250,
19456     g_width: 400,
19457     g_x: 50,
19458     g_y: 50,
19459     g_r: 30,
19460     opts:{
19461         //g_colors: this.colors,
19462         g_type: 'soft',
19463         g_gutter: '20%'
19464
19465     },
19466     title : false,
19467
19468     getAutoCreate : function(){
19469         
19470         var cfg = {
19471             tag: 'div',
19472             html : null
19473         }
19474         
19475         
19476         return  cfg;
19477     },
19478
19479     onRender : function(ct,position){
19480         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19481         this.raphael = Raphael(this.el.dom);
19482         
19483                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19484                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19485                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19486                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19487                 /*
19488                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19489                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19490                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19491                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19492                 
19493                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19494                 r.barchart(330, 10, 300, 220, data1);
19495                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19496                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19497                 */
19498                 
19499                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19500                 // r.barchart(30, 30, 560, 250,  xdata, {
19501                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19502                 //     axis : "0 0 1 1",
19503                 //     axisxlabels :  xdata
19504                 //     //yvalues : cols,
19505                    
19506                 // });
19507 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19508 //        
19509 //        this.load(null,xdata,{
19510 //                axis : "0 0 1 1",
19511 //                axisxlabels :  xdata
19512 //                });
19513
19514     },
19515
19516     load : function(graphtype,xdata,opts){
19517         this.raphael.clear();
19518         if(!graphtype) {
19519             graphtype = this.graphtype;
19520         }
19521         if(!opts){
19522             opts = this.opts;
19523         }
19524         var r = this.raphael,
19525             fin = function () {
19526                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19527             },
19528             fout = function () {
19529                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19530             },
19531             pfin = function() {
19532                 this.sector.stop();
19533                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19534
19535                 if (this.label) {
19536                     this.label[0].stop();
19537                     this.label[0].attr({ r: 7.5 });
19538                     this.label[1].attr({ "font-weight": 800 });
19539                 }
19540             },
19541             pfout = function() {
19542                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19543
19544                 if (this.label) {
19545                     this.label[0].animate({ r: 5 }, 500, "bounce");
19546                     this.label[1].attr({ "font-weight": 400 });
19547                 }
19548             };
19549
19550         switch(graphtype){
19551             case 'bar':
19552                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19553                 break;
19554             case 'hbar':
19555                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19556                 break;
19557             case 'pie':
19558 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19559 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19560 //            
19561                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19562                 
19563                 break;
19564
19565         }
19566         
19567         if(this.title){
19568             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19569         }
19570         
19571     },
19572     
19573     setTitle: function(o)
19574     {
19575         this.title = o;
19576     },
19577     
19578     initEvents: function() {
19579         
19580         if(!this.href){
19581             this.el.on('click', this.onClick, this);
19582         }
19583     },
19584     
19585     onClick : function(e)
19586     {
19587         Roo.log('img onclick');
19588         this.fireEvent('click', this, e);
19589     }
19590    
19591 });
19592
19593  
19594 /*
19595  * - LGPL
19596  *
19597  * numberBox
19598  * 
19599  */
19600 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19601
19602 /**
19603  * @class Roo.bootstrap.dash.NumberBox
19604  * @extends Roo.bootstrap.Component
19605  * Bootstrap NumberBox class
19606  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19607  * @cfg {String} headline Box headline
19608  * @cfg {String} content Box content
19609  * @cfg {String} icon Box icon
19610  * @cfg {String} footer Footer text
19611  * @cfg {String} fhref Footer href
19612  * 
19613  * @constructor
19614  * Create a new NumberBox
19615  * @param {Object} config The config object
19616  */
19617
19618
19619 Roo.bootstrap.dash.NumberBox = function(config){
19620     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19621     
19622 };
19623
19624 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19625     
19626     bgcolor : 'aqua',
19627     headline : '',
19628     content : '',
19629     icon : '',
19630     footer : '',
19631     fhref : '',
19632     ficon : '',
19633     
19634     getAutoCreate : function(){
19635         
19636         var cfg = {
19637             tag : 'div',
19638             cls : 'small-box bg-' + this.bgcolor,
19639             cn : [
19640                 {
19641                     tag : 'div',
19642                     cls : 'inner',
19643                     cn :[
19644                         {
19645                             tag : 'h3',
19646                             cls : 'roo-headline',
19647                             html : this.headline
19648                         },
19649                         {
19650                             tag : 'p',
19651                             cls : 'roo-content',
19652                             html : this.content
19653                         }
19654                     ]
19655                 }
19656             ]
19657         }
19658         
19659         if(this.icon){
19660             cfg.cn.push({
19661                 tag : 'div',
19662                 cls : 'icon',
19663                 cn :[
19664                     {
19665                         tag : 'i',
19666                         cls : 'ion ' + this.icon
19667                     }
19668                 ]
19669             });
19670         }
19671         
19672         if(this.footer){
19673             var footer = {
19674                 tag : 'a',
19675                 cls : 'small-box-footer',
19676                 href : this.fhref || '#',
19677                 html : this.footer
19678             };
19679             
19680             cfg.cn.push(footer);
19681             
19682         }
19683         
19684         return  cfg;
19685     },
19686
19687     onRender : function(ct,position){
19688         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19689
19690
19691        
19692                 
19693     },
19694
19695     setHeadline: function (value)
19696     {
19697         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19698     },
19699     
19700     setFooter: function (value, href)
19701     {
19702         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19703         
19704         if(href){
19705             this.el.select('a.small-box-footer',true).first().attr('href', href);
19706         }
19707         
19708     },
19709
19710     setContent: function (value)
19711     {
19712         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19713     },
19714
19715     initEvents: function() 
19716     {   
19717         
19718     }
19719     
19720 });
19721
19722  
19723 /*
19724  * - LGPL
19725  *
19726  * TabBox
19727  * 
19728  */
19729 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19730
19731 /**
19732  * @class Roo.bootstrap.dash.TabBox
19733  * @extends Roo.bootstrap.Component
19734  * Bootstrap TabBox class
19735  * @cfg {String} title Title of the TabBox
19736  * @cfg {String} icon Icon of the TabBox
19737  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19738  * 
19739  * @constructor
19740  * Create a new TabBox
19741  * @param {Object} config The config object
19742  */
19743
19744
19745 Roo.bootstrap.dash.TabBox = function(config){
19746     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19747     this.addEvents({
19748         // raw events
19749         /**
19750          * @event addpane
19751          * When a pane is added
19752          * @param {Roo.bootstrap.dash.TabPane} pane
19753          */
19754         "addpane" : true
19755          
19756     });
19757 };
19758
19759 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19760
19761     title : '',
19762     icon : false,
19763     showtabs : true,
19764     
19765     getChildContainer : function()
19766     {
19767         return this.el.select('.tab-content', true).first();
19768     },
19769     
19770     getAutoCreate : function(){
19771         
19772         var header = {
19773             tag: 'li',
19774             cls: 'pull-left header',
19775             html: this.title,
19776             cn : []
19777         };
19778         
19779         if(this.icon){
19780             header.cn.push({
19781                 tag: 'i',
19782                 cls: 'fa ' + this.icon
19783             });
19784         }
19785         
19786         
19787         var cfg = {
19788             tag: 'div',
19789             cls: 'nav-tabs-custom',
19790             cn: [
19791                 {
19792                     tag: 'ul',
19793                     cls: 'nav nav-tabs pull-right',
19794                     cn: [
19795                         header
19796                     ]
19797                 },
19798                 {
19799                     tag: 'div',
19800                     cls: 'tab-content no-padding',
19801                     cn: []
19802                 }
19803             ]
19804         }
19805
19806         return  cfg;
19807     },
19808     initEvents : function()
19809     {
19810         //Roo.log('add add pane handler');
19811         this.on('addpane', this.onAddPane, this);
19812     },
19813      /**
19814      * Updates the box title
19815      * @param {String} html to set the title to.
19816      */
19817     setTitle : function(value)
19818     {
19819         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19820     },
19821     onAddPane : function(pane)
19822     {
19823         //Roo.log('addpane');
19824         //Roo.log(pane);
19825         // tabs are rendere left to right..
19826         if(!this.showtabs){
19827             return;
19828         }
19829         
19830         var ctr = this.el.select('.nav-tabs', true).first();
19831          
19832          
19833         var existing = ctr.select('.nav-tab',true);
19834         var qty = existing.getCount();;
19835         
19836         
19837         var tab = ctr.createChild({
19838             tag : 'li',
19839             cls : 'nav-tab' + (qty ? '' : ' active'),
19840             cn : [
19841                 {
19842                     tag : 'a',
19843                     href:'#',
19844                     html : pane.title
19845                 }
19846             ]
19847         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19848         pane.tab = tab;
19849         
19850         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19851         if (!qty) {
19852             pane.el.addClass('active');
19853         }
19854         
19855                 
19856     },
19857     onTabClick : function(ev,un,ob,pane)
19858     {
19859         //Roo.log('tab - prev default');
19860         ev.preventDefault();
19861         
19862         
19863         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19864         pane.tab.addClass('active');
19865         //Roo.log(pane.title);
19866         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19867         // technically we should have a deactivate event.. but maybe add later.
19868         // and it should not de-activate the selected tab...
19869         
19870         pane.el.addClass('active');
19871         pane.fireEvent('activate');
19872         
19873         
19874     }
19875     
19876     
19877 });
19878
19879  
19880 /*
19881  * - LGPL
19882  *
19883  * Tab pane
19884  * 
19885  */
19886 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19887 /**
19888  * @class Roo.bootstrap.TabPane
19889  * @extends Roo.bootstrap.Component
19890  * Bootstrap TabPane class
19891  * @cfg {Boolean} active (false | true) Default false
19892  * @cfg {String} title title of panel
19893
19894  * 
19895  * @constructor
19896  * Create a new TabPane
19897  * @param {Object} config The config object
19898  */
19899
19900 Roo.bootstrap.dash.TabPane = function(config){
19901     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19902     
19903     this.addEvents({
19904         // raw events
19905         /**
19906          * @event activate
19907          * When a pane is activated
19908          * @param {Roo.bootstrap.dash.TabPane} pane
19909          */
19910         "activate" : true
19911          
19912     });
19913 };
19914
19915 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19916     
19917     active : false,
19918     title : '',
19919     
19920     // the tabBox that this is attached to.
19921     tab : false,
19922      
19923     getAutoCreate : function() 
19924     {
19925         var cfg = {
19926             tag: 'div',
19927             cls: 'tab-pane'
19928         }
19929         
19930         if(this.active){
19931             cfg.cls += ' active';
19932         }
19933         
19934         return cfg;
19935     },
19936     initEvents  : function()
19937     {
19938         //Roo.log('trigger add pane handler');
19939         this.parent().fireEvent('addpane', this)
19940     },
19941     
19942      /**
19943      * Updates the tab title 
19944      * @param {String} html to set the title to.
19945      */
19946     setTitle: function(str)
19947     {
19948         if (!this.tab) {
19949             return;
19950         }
19951         this.title = str;
19952         this.tab.select('a', true).first().dom.innerHTML = str;
19953         
19954     }
19955     
19956     
19957     
19958 });
19959
19960  
19961
19962
19963  /*
19964  * - LGPL
19965  *
19966  * menu
19967  * 
19968  */
19969 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19970
19971 /**
19972  * @class Roo.bootstrap.menu.Menu
19973  * @extends Roo.bootstrap.Component
19974  * Bootstrap Menu class - container for Menu
19975  * @cfg {String} html Text of the menu
19976  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19977  * @cfg {String} icon Font awesome icon
19978  * @cfg {String} pos Menu align to (top | bottom) default bottom
19979  * 
19980  * 
19981  * @constructor
19982  * Create a new Menu
19983  * @param {Object} config The config object
19984  */
19985
19986
19987 Roo.bootstrap.menu.Menu = function(config){
19988     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19989     
19990     this.addEvents({
19991         /**
19992          * @event beforeshow
19993          * Fires before this menu is displayed
19994          * @param {Roo.bootstrap.menu.Menu} this
19995          */
19996         beforeshow : true,
19997         /**
19998          * @event beforehide
19999          * Fires before this menu is hidden
20000          * @param {Roo.bootstrap.menu.Menu} this
20001          */
20002         beforehide : true,
20003         /**
20004          * @event show
20005          * Fires after this menu is displayed
20006          * @param {Roo.bootstrap.menu.Menu} this
20007          */
20008         show : true,
20009         /**
20010          * @event hide
20011          * Fires after this menu is hidden
20012          * @param {Roo.bootstrap.menu.Menu} this
20013          */
20014         hide : true,
20015         /**
20016          * @event click
20017          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20018          * @param {Roo.bootstrap.menu.Menu} this
20019          * @param {Roo.EventObject} e
20020          */
20021         click : true
20022     });
20023     
20024 };
20025
20026 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20027     
20028     submenu : false,
20029     html : '',
20030     weight : 'default',
20031     icon : false,
20032     pos : 'bottom',
20033     
20034     
20035     getChildContainer : function() {
20036         if(this.isSubMenu){
20037             return this.el;
20038         }
20039         
20040         return this.el.select('ul.dropdown-menu', true).first();  
20041     },
20042     
20043     getAutoCreate : function()
20044     {
20045         var text = [
20046             {
20047                 tag : 'span',
20048                 cls : 'roo-menu-text',
20049                 html : this.html
20050             }
20051         ];
20052         
20053         if(this.icon){
20054             text.unshift({
20055                 tag : 'i',
20056                 cls : 'fa ' + this.icon
20057             })
20058         }
20059         
20060         
20061         var cfg = {
20062             tag : 'div',
20063             cls : 'btn-group',
20064             cn : [
20065                 {
20066                     tag : 'button',
20067                     cls : 'dropdown-button btn btn-' + this.weight,
20068                     cn : text
20069                 },
20070                 {
20071                     tag : 'button',
20072                     cls : 'dropdown-toggle btn btn-' + this.weight,
20073                     cn : [
20074                         {
20075                             tag : 'span',
20076                             cls : 'caret'
20077                         }
20078                     ]
20079                 },
20080                 {
20081                     tag : 'ul',
20082                     cls : 'dropdown-menu'
20083                 }
20084             ]
20085             
20086         };
20087         
20088         if(this.pos == 'top'){
20089             cfg.cls += ' dropup';
20090         }
20091         
20092         if(this.isSubMenu){
20093             cfg = {
20094                 tag : 'ul',
20095                 cls : 'dropdown-menu'
20096             }
20097         }
20098         
20099         return cfg;
20100     },
20101     
20102     onRender : function(ct, position)
20103     {
20104         this.isSubMenu = ct.hasClass('dropdown-submenu');
20105         
20106         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20107     },
20108     
20109     initEvents : function() 
20110     {
20111         if(this.isSubMenu){
20112             return;
20113         }
20114         
20115         this.hidden = true;
20116         
20117         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20118         this.triggerEl.on('click', this.onTriggerPress, this);
20119         
20120         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20121         this.buttonEl.on('click', this.onClick, this);
20122         
20123     },
20124     
20125     list : function()
20126     {
20127         if(this.isSubMenu){
20128             return this.el;
20129         }
20130         
20131         return this.el.select('ul.dropdown-menu', true).first();
20132     },
20133     
20134     onClick : function(e)
20135     {
20136         this.fireEvent("click", this, e);
20137     },
20138     
20139     onTriggerPress  : function(e)
20140     {   
20141         if (this.isVisible()) {
20142             this.hide();
20143         } else {
20144             this.show();
20145         }
20146     },
20147     
20148     isVisible : function(){
20149         return !this.hidden;
20150     },
20151     
20152     show : function()
20153     {
20154         this.fireEvent("beforeshow", this);
20155         
20156         this.hidden = false;
20157         this.el.addClass('open');
20158         
20159         Roo.get(document).on("mouseup", this.onMouseUp, this);
20160         
20161         this.fireEvent("show", this);
20162         
20163         
20164     },
20165     
20166     hide : function()
20167     {
20168         this.fireEvent("beforehide", this);
20169         
20170         this.hidden = true;
20171         this.el.removeClass('open');
20172         
20173         Roo.get(document).un("mouseup", this.onMouseUp);
20174         
20175         this.fireEvent("hide", this);
20176     },
20177     
20178     onMouseUp : function()
20179     {
20180         this.hide();
20181     }
20182     
20183 });
20184
20185  
20186  /*
20187  * - LGPL
20188  *
20189  * menu item
20190  * 
20191  */
20192 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20193
20194 /**
20195  * @class Roo.bootstrap.menu.Item
20196  * @extends Roo.bootstrap.Component
20197  * Bootstrap MenuItem class
20198  * @cfg {Boolean} submenu (true | false) default false
20199  * @cfg {String} html text of the item
20200  * @cfg {String} href the link
20201  * @cfg {Boolean} disable (true | false) default false
20202  * @cfg {Boolean} preventDefault (true | false) default true
20203  * @cfg {String} icon Font awesome icon
20204  * @cfg {String} pos Submenu align to (left | right) default right 
20205  * 
20206  * 
20207  * @constructor
20208  * Create a new Item
20209  * @param {Object} config The config object
20210  */
20211
20212
20213 Roo.bootstrap.menu.Item = function(config){
20214     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20215     this.addEvents({
20216         /**
20217          * @event mouseover
20218          * Fires when the mouse is hovering over this menu
20219          * @param {Roo.bootstrap.menu.Item} this
20220          * @param {Roo.EventObject} e
20221          */
20222         mouseover : true,
20223         /**
20224          * @event mouseout
20225          * Fires when the mouse exits this menu
20226          * @param {Roo.bootstrap.menu.Item} this
20227          * @param {Roo.EventObject} e
20228          */
20229         mouseout : true,
20230         // raw events
20231         /**
20232          * @event click
20233          * The raw click event for the entire grid.
20234          * @param {Roo.EventObject} e
20235          */
20236         click : true
20237     });
20238 };
20239
20240 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20241     
20242     submenu : false,
20243     href : '',
20244     html : '',
20245     preventDefault: true,
20246     disable : false,
20247     icon : false,
20248     pos : 'right',
20249     
20250     getAutoCreate : function()
20251     {
20252         var text = [
20253             {
20254                 tag : 'span',
20255                 cls : 'roo-menu-item-text',
20256                 html : this.html
20257             }
20258         ];
20259         
20260         if(this.icon){
20261             text.unshift({
20262                 tag : 'i',
20263                 cls : 'fa ' + this.icon
20264             })
20265         }
20266         
20267         var cfg = {
20268             tag : 'li',
20269             cn : [
20270                 {
20271                     tag : 'a',
20272                     href : this.href || '#',
20273                     cn : text
20274                 }
20275             ]
20276         };
20277         
20278         if(this.disable){
20279             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20280         }
20281         
20282         if(this.submenu){
20283             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20284             
20285             if(this.pos == 'left'){
20286                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20287             }
20288         }
20289         
20290         return cfg;
20291     },
20292     
20293     initEvents : function() 
20294     {
20295         this.el.on('mouseover', this.onMouseOver, this);
20296         this.el.on('mouseout', this.onMouseOut, this);
20297         
20298         this.el.select('a', true).first().on('click', this.onClick, this);
20299         
20300     },
20301     
20302     onClick : function(e)
20303     {
20304         if(this.preventDefault){
20305             e.preventDefault();
20306         }
20307         
20308         this.fireEvent("click", this, e);
20309     },
20310     
20311     onMouseOver : function(e)
20312     {
20313         if(this.submenu && this.pos == 'left'){
20314             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20315         }
20316         
20317         this.fireEvent("mouseover", this, e);
20318     },
20319     
20320     onMouseOut : function(e)
20321     {
20322         this.fireEvent("mouseout", this, e);
20323     }
20324 });
20325
20326  
20327
20328  /*
20329  * - LGPL
20330  *
20331  * menu separator
20332  * 
20333  */
20334 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20335
20336 /**
20337  * @class Roo.bootstrap.menu.Separator
20338  * @extends Roo.bootstrap.Component
20339  * Bootstrap Separator class
20340  * 
20341  * @constructor
20342  * Create a new Separator
20343  * @param {Object} config The config object
20344  */
20345
20346
20347 Roo.bootstrap.menu.Separator = function(config){
20348     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20349 };
20350
20351 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20352     
20353     getAutoCreate : function(){
20354         var cfg = {
20355             tag : 'li',
20356             cls: 'divider'
20357         };
20358         
20359         return cfg;
20360     }
20361    
20362 });
20363
20364  
20365
20366