css/grid.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192          skip_children = false;
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 
214                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
215                 // and are not displayed -this causes this to use up the wrong element when matching.
216                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
217                 
218                 
219                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
220                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
221                   
222                   
223                   
224                     cn.el = echild;
225                   //  Roo.log("GOT");
226                     //echild.dom.removeAttribute('xtype');
227                 } else {
228                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229                     Roo.log(self_cntr_el);
230                     Roo.log(echild);
231                     Roo.log(cn);
232                 }
233             }
234            
235             
236            
237             // if object has flexy:if - then it may or may not be rendered.
238             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
239                 // skip a flexy if element.
240                 Roo.log('skipping render');
241                 Roo.log(tree);
242                 if (!cn.el) {
243                     Roo.log('skipping all children');
244                     skip_children = true;
245                 }
246                 
247              } else {
248                  
249                 // actually if flexy:foreach is found, we really want to create 
250                 // multiple copies here...
251                 //Roo.log('render');
252                 //Roo.log(this[cntr]());
253                 cn.render(this[cntr](true));
254              }
255             // then add the element..
256         }
257         
258         
259         // handle the kids..
260         
261         var nitems = [];
262         /*
263         if (typeof (tree.menu) != 'undefined') {
264             tree.menu.parentType = cn.xtype;
265             tree.menu.triggerEl = cn.el;
266             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
267             
268         }
269         */
270         if (!tree.items || !tree.items.length) {
271             cn.items = nitems;
272             return cn;
273         }
274         var items = tree.items;
275         delete tree.items;
276         
277         //Roo.log(items.length);
278             // add the items..
279         if (!skip_children) {    
280             for(var i =0;i < items.length;i++) {
281                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
282             }
283         }
284         
285         cn.items = nitems;
286         
287         return cn;
288     }
289     
290     
291     
292     
293 });
294
295  /*
296  * - LGPL
297  *
298  * Body
299  * 
300  */
301
302 /**
303  * @class Roo.bootstrap.Body
304  * @extends Roo.bootstrap.Component
305  * Bootstrap Body class
306  * 
307  * @constructor
308  * Create a new body
309  * @param {Object} config The config object
310  */
311
312 Roo.bootstrap.Body = function(config){
313     Roo.bootstrap.Body.superclass.constructor.call(this, config);
314     this.el = Roo.get(document.body);
315     if (this.cls && this.cls.length) {
316         Roo.get(document.body).addClass(this.cls);
317     }
318 };
319
320 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
321       
322         autoCreate : {
323         cls: 'container'
324     },
325     onRender : function(ct, position)
326     {
327        /* Roo.log("Roo.bootstrap.Body - onRender");
328         if (this.cls && this.cls.length) {
329             Roo.get(document.body).addClass(this.cls);
330         }
331         // style??? xttr???
332         */
333     }
334     
335     
336  
337    
338 });
339
340  /*
341  * - LGPL
342  *
343  * button group
344  * 
345  */
346
347
348 /**
349  * @class Roo.bootstrap.ButtonGroup
350  * @extends Roo.bootstrap.Component
351  * Bootstrap ButtonGroup class
352  * @cfg {String} size lg | sm | xs (default empty normal)
353  * @cfg {String} align vertical | justified  (default none)
354  * @cfg {String} direction up | down (default down)
355  * @cfg {Boolean} toolbar false | true
356  * @cfg {Boolean} btn true | false
357  * 
358  * 
359  * @constructor
360  * Create a new Input
361  * @param {Object} config The config object
362  */
363
364 Roo.bootstrap.ButtonGroup = function(config){
365     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
366 };
367
368 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
369     
370     size: '',
371     align: '',
372     direction: '',
373     toolbar: false,
374     btn: true,
375
376     getAutoCreate : function(){
377         var cfg = {
378             cls: 'btn-group',
379             html : null
380         }
381         
382         cfg.html = this.html || cfg.html;
383         
384         if (this.toolbar) {
385             cfg = {
386                 cls: 'btn-toolbar',
387                 html: null
388             }
389             
390             return cfg;
391         }
392         
393         if (['vertical','justified'].indexOf(this.align)!==-1) {
394             cfg.cls = 'btn-group-' + this.align;
395             
396             if (this.align == 'justified') {
397                 console.log(this.items);
398             }
399         }
400         
401         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
402             cfg.cls += ' btn-group-' + this.size;
403         }
404         
405         if (this.direction == 'up') {
406             cfg.cls += ' dropup' ;
407         }
408         
409         return cfg;
410     }
411    
412 });
413
414  /*
415  * - LGPL
416  *
417  * button
418  * 
419  */
420
421 /**
422  * @class Roo.bootstrap.Button
423  * @extends Roo.bootstrap.Component
424  * Bootstrap Button class
425  * @cfg {String} html The button content
426  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
427  * @cfg {String} size empty | lg | sm | xs
428  * @cfg {String} tag empty | a | input | submit
429  * @cfg {String} href empty or href
430  * @cfg {Boolean} disabled false | true
431  * @cfg {Boolean} isClose false | true
432  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
433  * @cfg {String} badge text for badge
434  * @cfg {String} theme default (or empty) | glow
435  * @cfg {Boolean} inverse false | true
436  * @cfg {Boolean} toggle false | true
437  * @cfg {String} ontext text for on toggle state
438  * @cfg {String} offtext text for off toggle state
439  * @cfg {Boolean} defaulton true | false
440  * @cfg {Boolean} preventDefault (true | false) default true
441  * @cfg {Boolean} removeClass true | false remove the standard class..
442  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
443  * 
444  * @constructor
445  * Create a new button
446  * @param {Object} config The config object
447  */
448
449
450 Roo.bootstrap.Button = function(config){
451     Roo.bootstrap.Button.superclass.constructor.call(this, config);
452     this.addEvents({
453         // raw events
454         /**
455          * @event click
456          * When a butotn is pressed
457          * @param {Roo.EventObject} e
458          */
459         "click" : true,
460          /**
461          * @event toggle
462          * After the button has been toggles
463          * @param {Roo.EventObject} e
464          * @param {boolean} pressed (also available as button.pressed)
465          */
466         "toggle" : true
467     });
468 };
469
470 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
471     html: false,
472     active: false,
473     weight: '',
474     size: '',
475     tag: 'button',
476     href: '',
477     disabled: false,
478     isClose: false,
479     glyphicon: '',
480     badge: '',
481     theme: 'default',
482     inverse: false,
483     
484     toggle: false,
485     ontext: 'ON',
486     offtext: 'OFF',
487     defaulton: true,
488     preventDefault: true,
489     removeClass: false,
490     name: false,
491     target: false,
492     
493     
494     pressed : null,
495      
496     
497     getAutoCreate : function(){
498         
499         var cfg = {
500             tag : 'button',
501             cls : 'roo-button',
502             html: ''
503         };
504         
505         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
506             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
507             this.tag = 'button';
508         } else {
509             cfg.tag = this.tag;
510         }
511         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
512         
513         if (this.toggle == true) {
514             cfg={
515                 tag: 'div',
516                 cls: 'slider-frame roo-button',
517                 cn: [
518                     {
519                         tag: 'span',
520                         'data-on-text':'ON',
521                         'data-off-text':'OFF',
522                         cls: 'slider-button',
523                         html: this.offtext
524                     }
525                 ]
526             };
527             
528             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
529                 cfg.cls += ' '+this.weight;
530             }
531             
532             return cfg;
533         }
534         
535         if (this.isClose) {
536             cfg.cls += ' close';
537             
538             cfg["aria-hidden"] = true;
539             
540             cfg.html = "&times;";
541             
542             return cfg;
543         }
544         
545          
546         if (this.theme==='default') {
547             cfg.cls = 'btn roo-button';
548             
549             //if (this.parentType != 'Navbar') {
550             this.weight = this.weight.length ?  this.weight : 'default';
551             //}
552             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
553                 
554                 cfg.cls += ' btn-' + this.weight;
555             }
556         } else if (this.theme==='glow') {
557             
558             cfg.tag = 'a';
559             cfg.cls = 'btn-glow roo-button';
560             
561             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
562                 
563                 cfg.cls += ' ' + this.weight;
564             }
565         }
566    
567         
568         if (this.inverse) {
569             this.cls += ' inverse';
570         }
571         
572         
573         if (this.active) {
574             cfg.cls += ' active';
575         }
576         
577         if (this.disabled) {
578             cfg.disabled = 'disabled';
579         }
580         
581         if (this.items) {
582             Roo.log('changing to ul' );
583             cfg.tag = 'ul';
584             this.glyphicon = 'caret';
585         }
586         
587         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
588          
589         //gsRoo.log(this.parentType);
590         if (this.parentType === 'Navbar' && !this.parent().bar) {
591             Roo.log('changing to li?');
592             
593             cfg.tag = 'li';
594             
595             cfg.cls = '';
596             cfg.cn =  [{
597                 tag : 'a',
598                 cls : 'roo-button',
599                 html : this.html,
600                 href : this.href || '#'
601             }];
602             if (this.menu) {
603                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
604                 cfg.cls += ' dropdown';
605             }   
606             
607             delete cfg.html;
608             
609         }
610         
611        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
612         
613         if (this.glyphicon) {
614             cfg.html = ' ' + cfg.html;
615             
616             cfg.cn = [
617                 {
618                     tag: 'span',
619                     cls: 'glyphicon glyphicon-' + this.glyphicon
620                 }
621             ];
622         }
623         
624         if (this.badge) {
625             cfg.html += ' ';
626             
627             cfg.tag = 'a';
628             
629 //            cfg.cls='btn roo-button';
630             
631             cfg.href=this.href;
632             
633             var value = cfg.html;
634             
635             if(this.glyphicon){
636                 value = {
637                             tag: 'span',
638                             cls: 'glyphicon glyphicon-' + this.glyphicon,
639                             html: this.html
640                         };
641                 
642             }
643             
644             cfg.cn = [
645                 value,
646                 {
647                     tag: 'span',
648                     cls: 'badge',
649                     html: this.badge
650                 }
651             ];
652             
653             cfg.html='';
654         }
655         
656         if (this.menu) {
657             cfg.cls += ' dropdown';
658             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
659         }
660         
661         if (cfg.tag !== 'a' && this.href !== '') {
662             throw "Tag must be a to set href.";
663         } else if (this.href.length > 0) {
664             cfg.href = this.href;
665         }
666         
667         if(this.removeClass){
668             cfg.cls = '';
669         }
670         
671         if(this.target){
672             cfg.target = this.target;
673         }
674         
675         return cfg;
676     },
677     initEvents: function() {
678        // Roo.log('init events?');
679 //        Roo.log(this.el.dom);
680         // add the menu...
681         
682         if (typeof (this.menu) != 'undefined') {
683             this.menu.parentType = this.xtype;
684             this.menu.triggerEl = this.el;
685             this.addxtype(Roo.apply({}, this.menu));
686         }
687
688
689        if (this.el.hasClass('roo-button')) {
690             this.el.on('click', this.onClick, this);
691        } else {
692             this.el.select('.roo-button').on('click', this.onClick, this);
693        }
694        
695        if(this.removeClass){
696            this.el.on('click', this.onClick, this);
697        }
698        
699        this.el.enableDisplayMode();
700         
701     },
702     onClick : function(e)
703     {
704         if (this.disabled) {
705             return;
706         }
707         
708         Roo.log('button on click ');
709         if(this.preventDefault){
710             e.preventDefault();
711         }
712         if (this.pressed === true || this.pressed === false) {
713             this.pressed = !this.pressed;
714             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
715             this.fireEvent('toggle', this, e, this.pressed);
716         }
717         
718         
719         this.fireEvent('click', this, e);
720     },
721     
722     /**
723      * Enables this button
724      */
725     enable : function()
726     {
727         this.disabled = false;
728         this.el.removeClass('disabled');
729     },
730     
731     /**
732      * Disable this button
733      */
734     disable : function()
735     {
736         this.disabled = true;
737         this.el.addClass('disabled');
738     },
739      /**
740      * sets the active state on/off, 
741      * @param {Boolean} state (optional) Force a particular state
742      */
743     setActive : function(v) {
744         
745         this.el[v ? 'addClass' : 'removeClass']('active');
746     },
747      /**
748      * toggles the current active state 
749      */
750     toggleActive : function()
751     {
752        var active = this.el.hasClass('active');
753        this.setActive(!active);
754        
755         
756     },
757     setText : function(str)
758     {
759         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
760     },
761     getText : function()
762     {
763         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
764     },
765     hide: function() {
766        
767      
768         this.el.hide();   
769     },
770     show: function() {
771        
772         this.el.show();   
773     }
774     
775     
776 });
777
778  /*
779  * - LGPL
780  *
781  * column
782  * 
783  */
784
785 /**
786  * @class Roo.bootstrap.Column
787  * @extends Roo.bootstrap.Component
788  * Bootstrap Column class
789  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
790  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
791  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
792  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
793  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
794  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
795  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
796  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
797  *
798  * 
799  * @cfg {Boolean} hidden (true|false) hide the element
800  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
801  * @cfg {String} fa (ban|check|...) font awesome icon
802  * @cfg {Number} fasize (1|2|....) font awsome size
803
804  * @cfg {String} icon (info-sign|check|...) glyphicon name
805
806  * @cfg {String} html content of column.
807  * 
808  * @constructor
809  * Create a new Column
810  * @param {Object} config The config object
811  */
812
813 Roo.bootstrap.Column = function(config){
814     Roo.bootstrap.Column.superclass.constructor.call(this, config);
815 };
816
817 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
818     
819     xs: false,
820     sm: false,
821     md: false,
822     lg: false,
823     xsoff: false,
824     smoff: false,
825     mdoff: false,
826     lgoff: false,
827     html: '',
828     offset: 0,
829     alert: false,
830     fa: false,
831     icon : false,
832     hidden : false,
833     fasize : 1,
834     
835     getAutoCreate : function(){
836         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
837         
838         cfg = {
839             tag: 'div',
840             cls: 'column'
841         };
842         
843         var settings=this;
844         ['xs','sm','md','lg'].map(function(size){
845             //Roo.log( size + ':' + settings[size]);
846             
847             if (settings[size+'off'] !== false) {
848                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
849             }
850             
851             if (settings[size] === false) {
852                 return;
853             }
854             Roo.log(settings[size]);
855             if (!settings[size]) { // 0 = hidden
856                 cfg.cls += ' hidden-' + size;
857                 return;
858             }
859             cfg.cls += ' col-' + size + '-' + settings[size];
860             
861         });
862         
863         if (this.hidden) {
864             cfg.cls += ' hidden';
865         }
866         
867         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
868             cfg.cls +=' alert alert-' + this.alert;
869         }
870         
871         
872         if (this.html.length) {
873             cfg.html = this.html;
874         }
875         if (this.fa) {
876             var fasize = '';
877             if (this.fasize > 1) {
878                 fasize = ' fa-' + this.fasize + 'x';
879             }
880             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
881             
882             
883         }
884         if (this.icon) {
885             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
886         }
887         
888         return cfg;
889     }
890    
891 });
892
893  
894
895  /*
896  * - LGPL
897  *
898  * page container.
899  * 
900  */
901
902
903 /**
904  * @class Roo.bootstrap.Container
905  * @extends Roo.bootstrap.Component
906  * Bootstrap Container class
907  * @cfg {Boolean} jumbotron is it a jumbotron element
908  * @cfg {String} html content of element
909  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
910  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
911  * @cfg {String} header content of header (for panel)
912  * @cfg {String} footer content of footer (for panel)
913  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
914  * @cfg {String} tag (header|aside|section) type of HTML tag.
915  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
916  * @cfg {String} fa (ban|check|...) font awesome icon
917  * @cfg {String} icon (info-sign|check|...) glyphicon name
918  * @cfg {Boolean} hidden (true|false) hide the element
919
920  *     
921  * @constructor
922  * Create a new Container
923  * @param {Object} config The config object
924  */
925
926 Roo.bootstrap.Container = function(config){
927     Roo.bootstrap.Container.superclass.constructor.call(this, config);
928 };
929
930 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
931     
932     jumbotron : false,
933     well: '',
934     panel : '',
935     header: '',
936     footer : '',
937     sticky: '',
938     tag : false,
939     alert : false,
940     fa: false,
941     icon : false,
942   
943      
944     getChildContainer : function() {
945         
946         if(!this.el){
947             return false;
948         }
949         
950         if (this.panel.length) {
951             return this.el.select('.panel-body',true).first();
952         }
953         
954         return this.el;
955     },
956     
957     
958     getAutoCreate : function(){
959         
960         var cfg = {
961             tag : this.tag || 'div',
962             html : '',
963             cls : ''
964         };
965         if (this.jumbotron) {
966             cfg.cls = 'jumbotron';
967         }
968         
969         
970         
971         // - this is applied by the parent..
972         //if (this.cls) {
973         //    cfg.cls = this.cls + '';
974         //}
975         
976         if (this.sticky.length) {
977             
978             var bd = Roo.get(document.body);
979             if (!bd.hasClass('bootstrap-sticky')) {
980                 bd.addClass('bootstrap-sticky');
981                 Roo.select('html',true).setStyle('height', '100%');
982             }
983              
984             cfg.cls += 'bootstrap-sticky-' + this.sticky;
985         }
986         
987         
988         if (this.well.length) {
989             switch (this.well) {
990                 case 'lg':
991                 case 'sm':
992                     cfg.cls +=' well well-' +this.well;
993                     break;
994                 default:
995                     cfg.cls +=' well';
996                     break;
997             }
998         }
999         
1000         if (this.hidden) {
1001             cfg.cls += ' hidden';
1002         }
1003         
1004         
1005         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1006             cfg.cls +=' alert alert-' + this.alert;
1007         }
1008         
1009         var body = cfg;
1010         
1011         if (this.panel.length) {
1012             cfg.cls += ' panel panel-' + this.panel;
1013             cfg.cn = [];
1014             if (this.header.length) {
1015                 cfg.cn.push({
1016                     
1017                     cls : 'panel-heading',
1018                     cn : [{
1019                         tag: 'h3',
1020                         cls : 'panel-title',
1021                         html : this.header
1022                     }]
1023                     
1024                 });
1025             }
1026             body = false;
1027             cfg.cn.push({
1028                 cls : 'panel-body',
1029                 html : this.html
1030             });
1031             
1032             
1033             if (this.footer.length) {
1034                 cfg.cn.push({
1035                     cls : 'panel-footer',
1036                     html : this.footer
1037                     
1038                 });
1039             }
1040             
1041         }
1042         
1043         if (body) {
1044             body.html = this.html || cfg.html;
1045             // prefix with the icons..
1046             if (this.fa) {
1047                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1048             }
1049             if (this.icon) {
1050                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1051             }
1052             
1053             
1054         }
1055         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1056             cfg.cls =  'container';
1057         }
1058         
1059         return cfg;
1060     },
1061     
1062     titleEl : function()
1063     {
1064         if(!this.el || !this.panel.length || !this.header.length){
1065             return;
1066         }
1067         
1068         return this.el.select('.panel-title',true).first();
1069     },
1070     
1071     setTitle : function(v)
1072     {
1073         var titleEl = this.titleEl();
1074         
1075         if(!titleEl){
1076             return;
1077         }
1078         
1079         titleEl.dom.innerHTML = v;
1080     },
1081     
1082     getTitle : function()
1083     {
1084         
1085         var titleEl = this.titleEl();
1086         
1087         if(!titleEl){
1088             return '';
1089         }
1090         
1091         return titleEl.dom.innerHTML;
1092     }
1093    
1094 });
1095
1096  /*
1097  * - LGPL
1098  *
1099  * image
1100  * 
1101  */
1102
1103
1104 /**
1105  * @class Roo.bootstrap.Img
1106  * @extends Roo.bootstrap.Component
1107  * Bootstrap Img class
1108  * @cfg {Boolean} imgResponsive false | true
1109  * @cfg {String} border rounded | circle | thumbnail
1110  * @cfg {String} src image source
1111  * @cfg {String} alt image alternative text
1112  * @cfg {String} href a tag href
1113  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1114  * 
1115  * @constructor
1116  * Create a new Input
1117  * @param {Object} config The config object
1118  */
1119
1120 Roo.bootstrap.Img = function(config){
1121     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1122     
1123     this.addEvents({
1124         // img events
1125         /**
1126          * @event click
1127          * The img click event for the img.
1128          * @param {Roo.EventObject} e
1129          */
1130         "click" : true
1131     });
1132 };
1133
1134 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1135     
1136     imgResponsive: true,
1137     border: '',
1138     src: '',
1139     href: false,
1140     target: false,
1141
1142     getAutoCreate : function(){
1143         
1144         var cfg = {
1145             tag: 'img',
1146             cls: (this.imgResponsive) ? 'img-responsive' : '',
1147             html : null
1148         }
1149         
1150         cfg.html = this.html || cfg.html;
1151         
1152         cfg.src = this.src || cfg.src;
1153         
1154         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1155             cfg.cls += ' img-' + this.border;
1156         }
1157         
1158         if(this.alt){
1159             cfg.alt = this.alt;
1160         }
1161         
1162         if(this.href){
1163             var a = {
1164                 tag: 'a',
1165                 href: this.href,
1166                 cn: [
1167                     cfg
1168                 ]
1169             }
1170             
1171             if(this.target){
1172                 a.target = this.target;
1173             }
1174             
1175         }
1176         
1177         
1178         return (this.href) ? a : cfg;
1179     },
1180     
1181     initEvents: function() {
1182         
1183         if(!this.href){
1184             this.el.on('click', this.onClick, this);
1185         }
1186     },
1187     
1188     onClick : function(e)
1189     {
1190         Roo.log('img onclick');
1191         this.fireEvent('click', this, e);
1192     }
1193    
1194 });
1195
1196  /*
1197  * - LGPL
1198  *
1199  * image
1200  * 
1201  */
1202
1203
1204 /**
1205  * @class Roo.bootstrap.Link
1206  * @extends Roo.bootstrap.Component
1207  * Bootstrap Link Class
1208  * @cfg {String} alt image alternative text
1209  * @cfg {String} href a tag href
1210  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1211  * @cfg {String} html the content of the link.
1212  * @cfg {String} anchor name for the anchor link
1213
1214  * @cfg {Boolean} preventDefault (true | false) default false
1215
1216  * 
1217  * @constructor
1218  * Create a new Input
1219  * @param {Object} config The config object
1220  */
1221
1222 Roo.bootstrap.Link = function(config){
1223     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1224     
1225     this.addEvents({
1226         // img events
1227         /**
1228          * @event click
1229          * The img click event for the img.
1230          * @param {Roo.EventObject} e
1231          */
1232         "click" : true
1233     });
1234 };
1235
1236 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1237     
1238     href: false,
1239     target: false,
1240     preventDefault: false,
1241     anchor : false,
1242     alt : false,
1243
1244     getAutoCreate : function()
1245     {
1246         
1247         var cfg = {
1248             tag: 'a'
1249         };
1250         // anchor's do not require html/href...
1251         if (this.anchor === false) {
1252             cfg.html = this.html || 'html-missing';
1253             cfg.href = this.href || '#';
1254         } else {
1255             cfg.name = this.anchor;
1256             if (this.html !== false) {
1257                 cfg.html = this.html;
1258             }
1259             if (this.href !== false) {
1260                 cfg.href = this.href;
1261             }
1262         }
1263         
1264         if(this.alt !== false){
1265             cfg.alt = this.alt;
1266         }
1267         
1268         
1269         if(this.target !== false) {
1270             cfg.target = this.target;
1271         }
1272         
1273         return cfg;
1274     },
1275     
1276     initEvents: function() {
1277         
1278         if(!this.href || this.preventDefault){
1279             this.el.on('click', this.onClick, this);
1280         }
1281     },
1282     
1283     onClick : function(e)
1284     {
1285         if(this.preventDefault){
1286             e.preventDefault();
1287         }
1288         //Roo.log('img onclick');
1289         this.fireEvent('click', this, e);
1290     }
1291    
1292 });
1293
1294  /*
1295  * - LGPL
1296  *
1297  * header
1298  * 
1299  */
1300
1301 /**
1302  * @class Roo.bootstrap.Header
1303  * @extends Roo.bootstrap.Component
1304  * Bootstrap Header class
1305  * @cfg {String} html content of header
1306  * @cfg {Number} level (1|2|3|4|5|6) default 1
1307  * 
1308  * @constructor
1309  * Create a new Header
1310  * @param {Object} config The config object
1311  */
1312
1313
1314 Roo.bootstrap.Header  = function(config){
1315     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1316 };
1317
1318 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1319     
1320     //href : false,
1321     html : false,
1322     level : 1,
1323     
1324     
1325     
1326     getAutoCreate : function(){
1327         
1328         var cfg = {
1329             tag: 'h' + (1 *this.level),
1330             html: this.html || 'fill in html'
1331         } ;
1332         
1333         return cfg;
1334     }
1335    
1336 });
1337
1338  
1339
1340  /*
1341  * Based on:
1342  * Ext JS Library 1.1.1
1343  * Copyright(c) 2006-2007, Ext JS, LLC.
1344  *
1345  * Originally Released Under LGPL - original licence link has changed is not relivant.
1346  *
1347  * Fork - LGPL
1348  * <script type="text/javascript">
1349  */
1350  
1351 /**
1352  * @class Roo.bootstrap.MenuMgr
1353  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1354  * @singleton
1355  */
1356 Roo.bootstrap.MenuMgr = function(){
1357    var menus, active, groups = {}, attached = false, lastShow = new Date();
1358
1359    // private - called when first menu is created
1360    function init(){
1361        menus = {};
1362        active = new Roo.util.MixedCollection();
1363        Roo.get(document).addKeyListener(27, function(){
1364            if(active.length > 0){
1365                hideAll();
1366            }
1367        });
1368    }
1369
1370    // private
1371    function hideAll(){
1372        if(active && active.length > 0){
1373            var c = active.clone();
1374            c.each(function(m){
1375                m.hide();
1376            });
1377        }
1378    }
1379
1380    // private
1381    function onHide(m){
1382        active.remove(m);
1383        if(active.length < 1){
1384            Roo.get(document).un("mouseup", onMouseDown);
1385             
1386            attached = false;
1387        }
1388    }
1389
1390    // private
1391    function onShow(m){
1392        var last = active.last();
1393        lastShow = new Date();
1394        active.add(m);
1395        if(!attached){
1396           Roo.get(document).on("mouseup", onMouseDown);
1397            
1398            attached = true;
1399        }
1400        if(m.parentMenu){
1401           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1402           m.parentMenu.activeChild = m;
1403        }else if(last && last.isVisible()){
1404           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1405        }
1406    }
1407
1408    // private
1409    function onBeforeHide(m){
1410        if(m.activeChild){
1411            m.activeChild.hide();
1412        }
1413        if(m.autoHideTimer){
1414            clearTimeout(m.autoHideTimer);
1415            delete m.autoHideTimer;
1416        }
1417    }
1418
1419    // private
1420    function onBeforeShow(m){
1421        var pm = m.parentMenu;
1422        if(!pm && !m.allowOtherMenus){
1423            hideAll();
1424        }else if(pm && pm.activeChild && active != m){
1425            pm.activeChild.hide();
1426        }
1427    }
1428
1429    // private
1430    function onMouseDown(e){
1431         Roo.log("on MouseDown");
1432         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1433            hideAll();
1434         }
1435         
1436         
1437    }
1438
1439    // private
1440    function onBeforeCheck(mi, state){
1441        if(state){
1442            var g = groups[mi.group];
1443            for(var i = 0, l = g.length; i < l; i++){
1444                if(g[i] != mi){
1445                    g[i].setChecked(false);
1446                }
1447            }
1448        }
1449    }
1450
1451    return {
1452
1453        /**
1454         * Hides all menus that are currently visible
1455         */
1456        hideAll : function(){
1457             hideAll();  
1458        },
1459
1460        // private
1461        register : function(menu){
1462            if(!menus){
1463                init();
1464            }
1465            menus[menu.id] = menu;
1466            menu.on("beforehide", onBeforeHide);
1467            menu.on("hide", onHide);
1468            menu.on("beforeshow", onBeforeShow);
1469            menu.on("show", onShow);
1470            var g = menu.group;
1471            if(g && menu.events["checkchange"]){
1472                if(!groups[g]){
1473                    groups[g] = [];
1474                }
1475                groups[g].push(menu);
1476                menu.on("checkchange", onCheck);
1477            }
1478        },
1479
1480         /**
1481          * Returns a {@link Roo.menu.Menu} object
1482          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1483          * be used to generate and return a new Menu instance.
1484          */
1485        get : function(menu){
1486            if(typeof menu == "string"){ // menu id
1487                return menus[menu];
1488            }else if(menu.events){  // menu instance
1489                return menu;
1490            }
1491            /*else if(typeof menu.length == 'number'){ // array of menu items?
1492                return new Roo.bootstrap.Menu({items:menu});
1493            }else{ // otherwise, must be a config
1494                return new Roo.bootstrap.Menu(menu);
1495            }
1496            */
1497            return false;
1498        },
1499
1500        // private
1501        unregister : function(menu){
1502            delete menus[menu.id];
1503            menu.un("beforehide", onBeforeHide);
1504            menu.un("hide", onHide);
1505            menu.un("beforeshow", onBeforeShow);
1506            menu.un("show", onShow);
1507            var g = menu.group;
1508            if(g && menu.events["checkchange"]){
1509                groups[g].remove(menu);
1510                menu.un("checkchange", onCheck);
1511            }
1512        },
1513
1514        // private
1515        registerCheckable : function(menuItem){
1516            var g = menuItem.group;
1517            if(g){
1518                if(!groups[g]){
1519                    groups[g] = [];
1520                }
1521                groups[g].push(menuItem);
1522                menuItem.on("beforecheckchange", onBeforeCheck);
1523            }
1524        },
1525
1526        // private
1527        unregisterCheckable : function(menuItem){
1528            var g = menuItem.group;
1529            if(g){
1530                groups[g].remove(menuItem);
1531                menuItem.un("beforecheckchange", onBeforeCheck);
1532            }
1533        }
1534    };
1535 }();/*
1536  * - LGPL
1537  *
1538  * menu
1539  * 
1540  */
1541
1542 /**
1543  * @class Roo.bootstrap.Menu
1544  * @extends Roo.bootstrap.Component
1545  * Bootstrap Menu class - container for MenuItems
1546  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1547  * 
1548  * @constructor
1549  * Create a new Menu
1550  * @param {Object} config The config object
1551  */
1552
1553
1554 Roo.bootstrap.Menu = function(config){
1555     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1556     if (this.registerMenu) {
1557         Roo.bootstrap.MenuMgr.register(this);
1558     }
1559     this.addEvents({
1560         /**
1561          * @event beforeshow
1562          * Fires before this menu is displayed
1563          * @param {Roo.menu.Menu} this
1564          */
1565         beforeshow : true,
1566         /**
1567          * @event beforehide
1568          * Fires before this menu is hidden
1569          * @param {Roo.menu.Menu} this
1570          */
1571         beforehide : true,
1572         /**
1573          * @event show
1574          * Fires after this menu is displayed
1575          * @param {Roo.menu.Menu} this
1576          */
1577         show : true,
1578         /**
1579          * @event hide
1580          * Fires after this menu is hidden
1581          * @param {Roo.menu.Menu} this
1582          */
1583         hide : true,
1584         /**
1585          * @event click
1586          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1587          * @param {Roo.menu.Menu} this
1588          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1589          * @param {Roo.EventObject} e
1590          */
1591         click : true,
1592         /**
1593          * @event mouseover
1594          * Fires when the mouse is hovering over this menu
1595          * @param {Roo.menu.Menu} this
1596          * @param {Roo.EventObject} e
1597          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1598          */
1599         mouseover : true,
1600         /**
1601          * @event mouseout
1602          * Fires when the mouse exits this menu
1603          * @param {Roo.menu.Menu} this
1604          * @param {Roo.EventObject} e
1605          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1606          */
1607         mouseout : true,
1608         /**
1609          * @event itemclick
1610          * Fires when a menu item contained in this menu is clicked
1611          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1612          * @param {Roo.EventObject} e
1613          */
1614         itemclick: true
1615     });
1616     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1617 };
1618
1619 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1620     
1621    /// html : false,
1622     //align : '',
1623     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1624     type: false,
1625     /**
1626      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1627      */
1628     registerMenu : true,
1629     
1630     menuItems :false, // stores the menu items..
1631     
1632     hidden:true,
1633     
1634     parentMenu : false,
1635     
1636     getChildContainer : function() {
1637         return this.el;  
1638     },
1639     
1640     getAutoCreate : function(){
1641          
1642         //if (['right'].indexOf(this.align)!==-1) {
1643         //    cfg.cn[1].cls += ' pull-right'
1644         //}
1645         
1646         
1647         var cfg = {
1648             tag : 'ul',
1649             cls : 'dropdown-menu' ,
1650             style : 'z-index:1000'
1651             
1652         }
1653         
1654         if (this.type === 'submenu') {
1655             cfg.cls = 'submenu active';
1656         }
1657         if (this.type === 'treeview') {
1658             cfg.cls = 'treeview-menu';
1659         }
1660         
1661         return cfg;
1662     },
1663     initEvents : function() {
1664         
1665        // Roo.log("ADD event");
1666        // Roo.log(this.triggerEl.dom);
1667         this.triggerEl.on('click', this.onTriggerPress, this);
1668         this.triggerEl.addClass('dropdown-toggle');
1669         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1670
1671         this.el.on("mouseover", this.onMouseOver, this);
1672         this.el.on("mouseout", this.onMouseOut, this);
1673         
1674         
1675     },
1676     findTargetItem : function(e){
1677         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1678         if(!t){
1679             return false;
1680         }
1681         //Roo.log(t);         Roo.log(t.id);
1682         if(t && t.id){
1683             //Roo.log(this.menuitems);
1684             return this.menuitems.get(t.id);
1685             
1686             //return this.items.get(t.menuItemId);
1687         }
1688         
1689         return false;
1690     },
1691     onClick : function(e){
1692         Roo.log("menu.onClick");
1693         var t = this.findTargetItem(e);
1694         if(!t){
1695             return;
1696         }
1697         Roo.log(e);
1698         /*
1699         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1700             if(t == this.activeItem && t.shouldDeactivate(e)){
1701                 this.activeItem.deactivate();
1702                 delete this.activeItem;
1703                 return;
1704             }
1705             if(t.canActivate){
1706                 this.setActiveItem(t, true);
1707             }
1708             return;
1709             
1710             
1711         }
1712         */
1713         Roo.log('pass click event');
1714         
1715         t.onClick(e);
1716         
1717         this.fireEvent("click", this, t, e);
1718         
1719         this.hide();
1720     },
1721      onMouseOver : function(e){
1722         var t  = this.findTargetItem(e);
1723         //Roo.log(t);
1724         //if(t){
1725         //    if(t.canActivate && !t.disabled){
1726         //        this.setActiveItem(t, true);
1727         //    }
1728         //}
1729         
1730         this.fireEvent("mouseover", this, e, t);
1731     },
1732     isVisible : function(){
1733         return !this.hidden;
1734     },
1735      onMouseOut : function(e){
1736         var t  = this.findTargetItem(e);
1737         
1738         //if(t ){
1739         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1740         //        this.activeItem.deactivate();
1741         //        delete this.activeItem;
1742         //    }
1743         //}
1744         this.fireEvent("mouseout", this, e, t);
1745     },
1746     
1747     
1748     /**
1749      * Displays this menu relative to another element
1750      * @param {String/HTMLElement/Roo.Element} element The element to align to
1751      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1752      * the element (defaults to this.defaultAlign)
1753      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1754      */
1755     show : function(el, pos, parentMenu){
1756         this.parentMenu = parentMenu;
1757         if(!this.el){
1758             this.render();
1759         }
1760         this.fireEvent("beforeshow", this);
1761         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1762     },
1763      /**
1764      * Displays this menu at a specific xy position
1765      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1766      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1767      */
1768     showAt : function(xy, parentMenu, /* private: */_e){
1769         this.parentMenu = parentMenu;
1770         if(!this.el){
1771             this.render();
1772         }
1773         if(_e !== false){
1774             this.fireEvent("beforeshow", this);
1775             
1776             //xy = this.el.adjustForConstraints(xy);
1777         }
1778         //this.el.setXY(xy);
1779         //this.el.show();
1780         this.hideMenuItems();
1781         this.hidden = false;
1782         this.triggerEl.addClass('open');
1783         this.focus();
1784         this.fireEvent("show", this);
1785     },
1786     
1787     focus : function(){
1788         return;
1789         if(!this.hidden){
1790             this.doFocus.defer(50, this);
1791         }
1792     },
1793
1794     doFocus : function(){
1795         if(!this.hidden){
1796             this.focusEl.focus();
1797         }
1798     },
1799
1800     /**
1801      * Hides this menu and optionally all parent menus
1802      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1803      */
1804     hide : function(deep){
1805         
1806         this.hideMenuItems();
1807         if(this.el && this.isVisible()){
1808             this.fireEvent("beforehide", this);
1809             if(this.activeItem){
1810                 this.activeItem.deactivate();
1811                 this.activeItem = null;
1812             }
1813             this.triggerEl.removeClass('open');;
1814             this.hidden = true;
1815             this.fireEvent("hide", this);
1816         }
1817         if(deep === true && this.parentMenu){
1818             this.parentMenu.hide(true);
1819         }
1820     },
1821     
1822     onTriggerPress  : function(e)
1823     {
1824         
1825         Roo.log('trigger press');
1826         //Roo.log(e.getTarget());
1827        // Roo.log(this.triggerEl.dom);
1828         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1829             return;
1830         }
1831         if (this.isVisible()) {
1832             Roo.log('hide');
1833             this.hide();
1834         } else {
1835             this.show(this.triggerEl, false, false);
1836         }
1837         
1838         
1839     },
1840     
1841          
1842        
1843     
1844     hideMenuItems : function()
1845     {
1846         //$(backdrop).remove()
1847         Roo.select('.open',true).each(function(aa) {
1848             
1849             aa.removeClass('open');
1850           //var parent = getParent($(this))
1851           //var relatedTarget = { relatedTarget: this }
1852           
1853            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1854           //if (e.isDefaultPrevented()) return
1855            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1856         })
1857     },
1858     addxtypeChild : function (tree, cntr) {
1859         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1860           
1861         this.menuitems.add(comp);
1862         return comp;
1863
1864     },
1865     getEl : function()
1866     {
1867         Roo.log(this.el);
1868         return this.el;
1869     }
1870 });
1871
1872  
1873  /*
1874  * - LGPL
1875  *
1876  * menu item
1877  * 
1878  */
1879
1880
1881 /**
1882  * @class Roo.bootstrap.MenuItem
1883  * @extends Roo.bootstrap.Component
1884  * Bootstrap MenuItem class
1885  * @cfg {String} html the menu label
1886  * @cfg {String} href the link
1887  * @cfg {Boolean} preventDefault (true | false) default true
1888  * 
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         this.list.show();
11649         
11650         this.restrictHeight();
11651         
11652         if(this.tickable){
11653             
11654             this.tickItems = Roo.apply([], this.item);
11655             
11656             this.okBtn.show();
11657             this.cancelBtn.show();
11658             this.trigger.hide();
11659             
11660         }
11661         
11662         Roo.get(document).on('mousedown', this.collapseIf, this);
11663         Roo.get(document).on('mousewheel', this.collapseIf, this);
11664         if (!this.editable) {
11665             Roo.get(document).on('keydown', this.listKeyPress, this);
11666         }
11667         
11668         this.fireEvent('expand', this);
11669     },
11670
11671     // private
11672     // Implements the default empty TriggerField.onTriggerClick function
11673     onTriggerClick : function(e)
11674     {
11675         Roo.log('trigger click');
11676         
11677         if(this.disabled || !this.triggerList){
11678             return;
11679         }
11680         
11681         this.page = 0;
11682         this.loadNext = false;
11683         
11684         if(this.isExpanded()){
11685             this.collapse();
11686             if (!this.blockFocus) {
11687                 this.inputEl().focus();
11688             }
11689             
11690         }else {
11691             this.hasFocus = true;
11692             if(this.triggerAction == 'all') {
11693                 this.doQuery(this.allQuery, true);
11694             } else {
11695                 this.doQuery(this.getRawValue());
11696             }
11697             if (!this.blockFocus) {
11698                 this.inputEl().focus();
11699             }
11700         }
11701     },
11702     
11703     onTickableTriggerClick : function(e)
11704     {
11705         if(this.disabled){
11706             return;
11707         }
11708         
11709         this.page = 0;
11710         this.loadNext = false;
11711         this.hasFocus = true;
11712         
11713         if(this.triggerAction == 'all') {
11714             this.doQuery(this.allQuery, true);
11715         } else {
11716             this.doQuery(this.getRawValue());
11717         }
11718     },
11719     
11720     onSearchFieldClick : function(e)
11721     {
11722         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11723             return;
11724         }
11725         
11726         this.page = 0;
11727         this.loadNext = false;
11728         this.hasFocus = true;
11729         
11730         if(this.triggerAction == 'all') {
11731             this.doQuery(this.allQuery, true);
11732         } else {
11733             this.doQuery(this.getRawValue());
11734         }
11735     },
11736     
11737     listKeyPress : function(e)
11738     {
11739         //Roo.log('listkeypress');
11740         // scroll to first matching element based on key pres..
11741         if (e.isSpecialKey()) {
11742             return false;
11743         }
11744         var k = String.fromCharCode(e.getKey()).toUpperCase();
11745         //Roo.log(k);
11746         var match  = false;
11747         var csel = this.view.getSelectedNodes();
11748         var cselitem = false;
11749         if (csel.length) {
11750             var ix = this.view.indexOf(csel[0]);
11751             cselitem  = this.store.getAt(ix);
11752             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11753                 cselitem = false;
11754             }
11755             
11756         }
11757         
11758         this.store.each(function(v) { 
11759             if (cselitem) {
11760                 // start at existing selection.
11761                 if (cselitem.id == v.id) {
11762                     cselitem = false;
11763                 }
11764                 return true;
11765             }
11766                 
11767             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11768                 match = this.store.indexOf(v);
11769                 return false;
11770             }
11771             return true;
11772         }, this);
11773         
11774         if (match === false) {
11775             return true; // no more action?
11776         }
11777         // scroll to?
11778         this.view.select(match);
11779         var sn = Roo.get(this.view.getSelectedNodes()[0])
11780         //sn.scrollIntoView(sn.dom.parentNode, false);
11781     },
11782     
11783     onViewScroll : function(e, t){
11784         
11785         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){
11786             return;
11787         }
11788         
11789         this.hasQuery = true;
11790         
11791         this.loading = this.list.select('.loading', true).first();
11792         
11793         if(this.loading === null){
11794             this.list.createChild({
11795                 tag: 'div',
11796                 cls: 'loading select2-more-results select2-active',
11797                 html: 'Loading more results...'
11798             })
11799             
11800             this.loading = this.list.select('.loading', true).first();
11801             
11802             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11803             
11804             this.loading.hide();
11805         }
11806         
11807         this.loading.show();
11808         
11809         var _combo = this;
11810         
11811         this.page++;
11812         this.loadNext = true;
11813         
11814         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11815         
11816         return;
11817     },
11818     
11819     addItem : function(o)
11820     {   
11821         var dv = ''; // display value
11822         
11823         if (this.displayField) {
11824             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11825         } else {
11826             // this is an error condition!!!
11827             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11828         }
11829         
11830         if(!dv.length){
11831             return;
11832         }
11833         
11834         var choice = this.choices.createChild({
11835             tag: 'li',
11836             cls: 'select2-search-choice',
11837             cn: [
11838                 {
11839                     tag: 'div',
11840                     html: dv
11841                 },
11842                 {
11843                     tag: 'a',
11844                     href: '#',
11845                     cls: 'select2-search-choice-close',
11846                     tabindex: '-1'
11847                 }
11848             ]
11849             
11850         }, this.searchField);
11851         
11852         var close = choice.select('a.select2-search-choice-close', true).first()
11853         
11854         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11855         
11856         this.item.push(o);
11857         
11858         this.lastData = o;
11859         
11860         this.syncValue();
11861         
11862         this.inputEl().dom.value = '';
11863         
11864     },
11865     
11866     onRemoveItem : function(e, _self, o)
11867     {
11868         e.preventDefault();
11869         var index = this.item.indexOf(o.data) * 1;
11870         
11871         if( index < 0){
11872             Roo.log('not this item?!');
11873             return;
11874         }
11875         
11876         this.item.splice(index, 1);
11877         o.item.remove();
11878         
11879         this.syncValue();
11880         
11881         this.fireEvent('remove', this, e);
11882         
11883     },
11884     
11885     syncValue : function()
11886     {
11887         if(!this.item.length){
11888             this.clearValue();
11889             return;
11890         }
11891             
11892         var value = [];
11893         var _this = this;
11894         Roo.each(this.item, function(i){
11895             if(_this.valueField){
11896                 value.push(i[_this.valueField]);
11897                 return;
11898             }
11899
11900             value.push(i);
11901         });
11902
11903         this.value = value.join(',');
11904
11905         if(this.hiddenField){
11906             this.hiddenField.dom.value = this.value;
11907         }
11908     },
11909     
11910     clearItem : function()
11911     {
11912         if(!this.multiple){
11913             return;
11914         }
11915         
11916         this.item = [];
11917         
11918         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11919            c.remove();
11920         });
11921         
11922         this.syncValue();
11923     },
11924     
11925     inputEl: function ()
11926     {
11927         if(this.tickable){
11928             return this.searchField;
11929         }
11930         return this.el.select('input.form-control',true).first();
11931     },
11932     
11933     
11934     onTickableFooterButtonClick : function(e, btn, el)
11935     {
11936         e.preventDefault();
11937         
11938         if(btn && btn.name == 'cancel'){
11939             this.tickItems = Roo.apply([], this.item);
11940             this.collapse();
11941             return;
11942         }
11943         
11944         this.clearItem();
11945         
11946         var _this = this;
11947         
11948         Roo.each(this.tickItems, function(o){
11949             _this.addItem(o);
11950         });
11951         
11952         this.collapse();
11953         
11954     }
11955     
11956     
11957
11958     /** 
11959     * @cfg {Boolean} grow 
11960     * @hide 
11961     */
11962     /** 
11963     * @cfg {Number} growMin 
11964     * @hide 
11965     */
11966     /** 
11967     * @cfg {Number} growMax 
11968     * @hide 
11969     */
11970     /**
11971      * @hide
11972      * @method autoSize
11973      */
11974 });
11975 /*
11976  * Based on:
11977  * Ext JS Library 1.1.1
11978  * Copyright(c) 2006-2007, Ext JS, LLC.
11979  *
11980  * Originally Released Under LGPL - original licence link has changed is not relivant.
11981  *
11982  * Fork - LGPL
11983  * <script type="text/javascript">
11984  */
11985
11986 /**
11987  * @class Roo.View
11988  * @extends Roo.util.Observable
11989  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11990  * This class also supports single and multi selection modes. <br>
11991  * Create a data model bound view:
11992  <pre><code>
11993  var store = new Roo.data.Store(...);
11994
11995  var view = new Roo.View({
11996     el : "my-element",
11997     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11998  
11999     singleSelect: true,
12000     selectedClass: "ydataview-selected",
12001     store: store
12002  });
12003
12004  // listen for node click?
12005  view.on("click", function(vw, index, node, e){
12006  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12007  });
12008
12009  // load XML data
12010  dataModel.load("foobar.xml");
12011  </code></pre>
12012  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12013  * <br><br>
12014  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12015  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12016  * 
12017  * Note: old style constructor is still suported (container, template, config)
12018  * 
12019  * @constructor
12020  * Create a new View
12021  * @param {Object} config The config object
12022  * 
12023  */
12024 Roo.View = function(config, depreciated_tpl, depreciated_config){
12025     
12026     this.parent = false;
12027     
12028     if (typeof(depreciated_tpl) == 'undefined') {
12029         // new way.. - universal constructor.
12030         Roo.apply(this, config);
12031         this.el  = Roo.get(this.el);
12032     } else {
12033         // old format..
12034         this.el  = Roo.get(config);
12035         this.tpl = depreciated_tpl;
12036         Roo.apply(this, depreciated_config);
12037     }
12038     this.wrapEl  = this.el.wrap().wrap();
12039     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12040     
12041     
12042     if(typeof(this.tpl) == "string"){
12043         this.tpl = new Roo.Template(this.tpl);
12044     } else {
12045         // support xtype ctors..
12046         this.tpl = new Roo.factory(this.tpl, Roo);
12047     }
12048     
12049     
12050     this.tpl.compile();
12051     
12052     /** @private */
12053     this.addEvents({
12054         /**
12055          * @event beforeclick
12056          * Fires before a click is processed. Returns false to cancel the default action.
12057          * @param {Roo.View} this
12058          * @param {Number} index The index of the target node
12059          * @param {HTMLElement} node The target node
12060          * @param {Roo.EventObject} e The raw event object
12061          */
12062             "beforeclick" : true,
12063         /**
12064          * @event click
12065          * Fires when a template node is clicked.
12066          * @param {Roo.View} this
12067          * @param {Number} index The index of the target node
12068          * @param {HTMLElement} node The target node
12069          * @param {Roo.EventObject} e The raw event object
12070          */
12071             "click" : true,
12072         /**
12073          * @event dblclick
12074          * Fires when a template node is double clicked.
12075          * @param {Roo.View} this
12076          * @param {Number} index The index of the target node
12077          * @param {HTMLElement} node The target node
12078          * @param {Roo.EventObject} e The raw event object
12079          */
12080             "dblclick" : true,
12081         /**
12082          * @event contextmenu
12083          * Fires when a template node is right clicked.
12084          * @param {Roo.View} this
12085          * @param {Number} index The index of the target node
12086          * @param {HTMLElement} node The target node
12087          * @param {Roo.EventObject} e The raw event object
12088          */
12089             "contextmenu" : true,
12090         /**
12091          * @event selectionchange
12092          * Fires when the selected nodes change.
12093          * @param {Roo.View} this
12094          * @param {Array} selections Array of the selected nodes
12095          */
12096             "selectionchange" : true,
12097     
12098         /**
12099          * @event beforeselect
12100          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12101          * @param {Roo.View} this
12102          * @param {HTMLElement} node The node to be selected
12103          * @param {Array} selections Array of currently selected nodes
12104          */
12105             "beforeselect" : true,
12106         /**
12107          * @event preparedata
12108          * Fires on every row to render, to allow you to change the data.
12109          * @param {Roo.View} this
12110          * @param {Object} data to be rendered (change this)
12111          */
12112           "preparedata" : true
12113           
12114           
12115         });
12116
12117
12118
12119     this.el.on({
12120         "click": this.onClick,
12121         "dblclick": this.onDblClick,
12122         "contextmenu": this.onContextMenu,
12123         scope:this
12124     });
12125
12126     this.selections = [];
12127     this.nodes = [];
12128     this.cmp = new Roo.CompositeElementLite([]);
12129     if(this.store){
12130         this.store = Roo.factory(this.store, Roo.data);
12131         this.setStore(this.store, true);
12132     }
12133     
12134     if ( this.footer && this.footer.xtype) {
12135            
12136          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12137         
12138         this.footer.dataSource = this.store
12139         this.footer.container = fctr;
12140         this.footer = Roo.factory(this.footer, Roo);
12141         fctr.insertFirst(this.el);
12142         
12143         // this is a bit insane - as the paging toolbar seems to detach the el..
12144 //        dom.parentNode.parentNode.parentNode
12145          // they get detached?
12146     }
12147     
12148     
12149     Roo.View.superclass.constructor.call(this);
12150     
12151     
12152 };
12153
12154 Roo.extend(Roo.View, Roo.util.Observable, {
12155     
12156      /**
12157      * @cfg {Roo.data.Store} store Data store to load data from.
12158      */
12159     store : false,
12160     
12161     /**
12162      * @cfg {String|Roo.Element} el The container element.
12163      */
12164     el : '',
12165     
12166     /**
12167      * @cfg {String|Roo.Template} tpl The template used by this View 
12168      */
12169     tpl : false,
12170     /**
12171      * @cfg {String} dataName the named area of the template to use as the data area
12172      *                          Works with domtemplates roo-name="name"
12173      */
12174     dataName: false,
12175     /**
12176      * @cfg {String} selectedClass The css class to add to selected nodes
12177      */
12178     selectedClass : "x-view-selected",
12179      /**
12180      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12181      */
12182     emptyText : "",
12183     
12184     /**
12185      * @cfg {String} text to display on mask (default Loading)
12186      */
12187     mask : false,
12188     /**
12189      * @cfg {Boolean} multiSelect Allow multiple selection
12190      */
12191     multiSelect : false,
12192     /**
12193      * @cfg {Boolean} singleSelect Allow single selection
12194      */
12195     singleSelect:  false,
12196     
12197     /**
12198      * @cfg {Boolean} toggleSelect - selecting 
12199      */
12200     toggleSelect : false,
12201     
12202     /**
12203      * @cfg {Boolean} tickable - selecting 
12204      */
12205     tickable : false,
12206     
12207     /**
12208      * Returns the element this view is bound to.
12209      * @return {Roo.Element}
12210      */
12211     getEl : function(){
12212         return this.wrapEl;
12213     },
12214     
12215     
12216
12217     /**
12218      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12219      */
12220     refresh : function(){
12221         Roo.log('refresh');
12222         var t = this.tpl;
12223         
12224         // if we are using something like 'domtemplate', then
12225         // the what gets used is:
12226         // t.applySubtemplate(NAME, data, wrapping data..)
12227         // the outer template then get' applied with
12228         //     the store 'extra data'
12229         // and the body get's added to the
12230         //      roo-name="data" node?
12231         //      <span class='roo-tpl-{name}'></span> ?????
12232         
12233         
12234         
12235         this.clearSelections();
12236         this.el.update("");
12237         var html = [];
12238         var records = this.store.getRange();
12239         if(records.length < 1) {
12240             
12241             // is this valid??  = should it render a template??
12242             
12243             this.el.update(this.emptyText);
12244             return;
12245         }
12246         var el = this.el;
12247         if (this.dataName) {
12248             this.el.update(t.apply(this.store.meta)); //????
12249             el = this.el.child('.roo-tpl-' + this.dataName);
12250         }
12251         
12252         for(var i = 0, len = records.length; i < len; i++){
12253             var data = this.prepareData(records[i].data, i, records[i]);
12254             this.fireEvent("preparedata", this, data, i, records[i]);
12255             
12256             var d = Roo.apply({}, data);
12257             
12258             if(this.tickable){
12259                 Roo.apply(d, {'roo-id' : Roo.id()});
12260                 
12261                 var _this = this;
12262             
12263                 Roo.each(this.parent.item, function(item){
12264                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12265                         return;
12266                     }
12267                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12268                 });
12269             }
12270             
12271             html[html.length] = Roo.util.Format.trim(
12272                 this.dataName ?
12273                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12274                     t.apply(d)
12275             );
12276         }
12277         
12278         
12279         
12280         el.update(html.join(""));
12281         this.nodes = el.dom.childNodes;
12282         this.updateIndexes(0);
12283     },
12284     
12285
12286     /**
12287      * Function to override to reformat the data that is sent to
12288      * the template for each node.
12289      * DEPRICATED - use the preparedata event handler.
12290      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12291      * a JSON object for an UpdateManager bound view).
12292      */
12293     prepareData : function(data, index, record)
12294     {
12295         this.fireEvent("preparedata", this, data, index, record);
12296         return data;
12297     },
12298
12299     onUpdate : function(ds, record){
12300          Roo.log('on update');   
12301         this.clearSelections();
12302         var index = this.store.indexOf(record);
12303         var n = this.nodes[index];
12304         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12305         n.parentNode.removeChild(n);
12306         this.updateIndexes(index, index);
12307     },
12308
12309     
12310     
12311 // --------- FIXME     
12312     onAdd : function(ds, records, index)
12313     {
12314         Roo.log(['on Add', ds, records, index] );        
12315         this.clearSelections();
12316         if(this.nodes.length == 0){
12317             this.refresh();
12318             return;
12319         }
12320         var n = this.nodes[index];
12321         for(var i = 0, len = records.length; i < len; i++){
12322             var d = this.prepareData(records[i].data, i, records[i]);
12323             if(n){
12324                 this.tpl.insertBefore(n, d);
12325             }else{
12326                 
12327                 this.tpl.append(this.el, d);
12328             }
12329         }
12330         this.updateIndexes(index);
12331     },
12332
12333     onRemove : function(ds, record, index){
12334         Roo.log('onRemove');
12335         this.clearSelections();
12336         var el = this.dataName  ?
12337             this.el.child('.roo-tpl-' + this.dataName) :
12338             this.el; 
12339         
12340         el.dom.removeChild(this.nodes[index]);
12341         this.updateIndexes(index);
12342     },
12343
12344     /**
12345      * Refresh an individual node.
12346      * @param {Number} index
12347      */
12348     refreshNode : function(index){
12349         this.onUpdate(this.store, this.store.getAt(index));
12350     },
12351
12352     updateIndexes : function(startIndex, endIndex){
12353         var ns = this.nodes;
12354         startIndex = startIndex || 0;
12355         endIndex = endIndex || ns.length - 1;
12356         for(var i = startIndex; i <= endIndex; i++){
12357             ns[i].nodeIndex = i;
12358         }
12359     },
12360
12361     /**
12362      * Changes the data store this view uses and refresh the view.
12363      * @param {Store} store
12364      */
12365     setStore : function(store, initial){
12366         if(!initial && this.store){
12367             this.store.un("datachanged", this.refresh);
12368             this.store.un("add", this.onAdd);
12369             this.store.un("remove", this.onRemove);
12370             this.store.un("update", this.onUpdate);
12371             this.store.un("clear", this.refresh);
12372             this.store.un("beforeload", this.onBeforeLoad);
12373             this.store.un("load", this.onLoad);
12374             this.store.un("loadexception", this.onLoad);
12375         }
12376         if(store){
12377           
12378             store.on("datachanged", this.refresh, this);
12379             store.on("add", this.onAdd, this);
12380             store.on("remove", this.onRemove, this);
12381             store.on("update", this.onUpdate, this);
12382             store.on("clear", this.refresh, this);
12383             store.on("beforeload", this.onBeforeLoad, this);
12384             store.on("load", this.onLoad, this);
12385             store.on("loadexception", this.onLoad, this);
12386         }
12387         
12388         if(store){
12389             this.refresh();
12390         }
12391     },
12392     /**
12393      * onbeforeLoad - masks the loading area.
12394      *
12395      */
12396     onBeforeLoad : function(store,opts)
12397     {
12398          Roo.log('onBeforeLoad');   
12399         if (!opts.add) {
12400             this.el.update("");
12401         }
12402         this.el.mask(this.mask ? this.mask : "Loading" ); 
12403     },
12404     onLoad : function ()
12405     {
12406         this.el.unmask();
12407     },
12408     
12409
12410     /**
12411      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12412      * @param {HTMLElement} node
12413      * @return {HTMLElement} The template node
12414      */
12415     findItemFromChild : function(node){
12416         var el = this.dataName  ?
12417             this.el.child('.roo-tpl-' + this.dataName,true) :
12418             this.el.dom; 
12419         
12420         if(!node || node.parentNode == el){
12421                     return node;
12422             }
12423             var p = node.parentNode;
12424             while(p && p != el){
12425             if(p.parentNode == el){
12426                 return p;
12427             }
12428             p = p.parentNode;
12429         }
12430             return null;
12431     },
12432
12433     /** @ignore */
12434     onClick : function(e){
12435         var item = this.findItemFromChild(e.getTarget());
12436         if(item){
12437             var index = this.indexOf(item);
12438             if(this.onItemClick(item, index, e) !== false){
12439                 this.fireEvent("click", this, index, item, e);
12440             }
12441         }else{
12442             this.clearSelections();
12443         }
12444     },
12445
12446     /** @ignore */
12447     onContextMenu : function(e){
12448         var item = this.findItemFromChild(e.getTarget());
12449         if(item){
12450             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12451         }
12452     },
12453
12454     /** @ignore */
12455     onDblClick : function(e){
12456         var item = this.findItemFromChild(e.getTarget());
12457         if(item){
12458             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12459         }
12460     },
12461
12462     onItemClick : function(item, index, e)
12463     {
12464         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12465             return false;
12466         }
12467         if (this.toggleSelect) {
12468             var m = this.isSelected(item) ? 'unselect' : 'select';
12469             Roo.log(m);
12470             var _t = this;
12471             _t[m](item, true, false);
12472             return true;
12473         }
12474         if(this.multiSelect || this.singleSelect){
12475             if(this.multiSelect && e.shiftKey && this.lastSelection){
12476                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12477             }else{
12478                 this.select(item, this.multiSelect && e.ctrlKey);
12479                 this.lastSelection = item;
12480             }
12481             
12482             if(!this.tickable){
12483                 e.preventDefault();
12484             }
12485             
12486         }
12487         return true;
12488     },
12489
12490     /**
12491      * Get the number of selected nodes.
12492      * @return {Number}
12493      */
12494     getSelectionCount : function(){
12495         return this.selections.length;
12496     },
12497
12498     /**
12499      * Get the currently selected nodes.
12500      * @return {Array} An array of HTMLElements
12501      */
12502     getSelectedNodes : function(){
12503         return this.selections;
12504     },
12505
12506     /**
12507      * Get the indexes of the selected nodes.
12508      * @return {Array}
12509      */
12510     getSelectedIndexes : function(){
12511         var indexes = [], s = this.selections;
12512         for(var i = 0, len = s.length; i < len; i++){
12513             indexes.push(s[i].nodeIndex);
12514         }
12515         return indexes;
12516     },
12517
12518     /**
12519      * Clear all selections
12520      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12521      */
12522     clearSelections : function(suppressEvent){
12523         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12524             this.cmp.elements = this.selections;
12525             this.cmp.removeClass(this.selectedClass);
12526             this.selections = [];
12527             if(!suppressEvent){
12528                 this.fireEvent("selectionchange", this, this.selections);
12529             }
12530         }
12531     },
12532
12533     /**
12534      * Returns true if the passed node is selected
12535      * @param {HTMLElement/Number} node The node or node index
12536      * @return {Boolean}
12537      */
12538     isSelected : function(node){
12539         var s = this.selections;
12540         if(s.length < 1){
12541             return false;
12542         }
12543         node = this.getNode(node);
12544         return s.indexOf(node) !== -1;
12545     },
12546
12547     /**
12548      * Selects nodes.
12549      * @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
12550      * @param {Boolean} keepExisting (optional) true to keep existing selections
12551      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12552      */
12553     select : function(nodeInfo, keepExisting, suppressEvent){
12554         if(nodeInfo instanceof Array){
12555             if(!keepExisting){
12556                 this.clearSelections(true);
12557             }
12558             for(var i = 0, len = nodeInfo.length; i < len; i++){
12559                 this.select(nodeInfo[i], true, true);
12560             }
12561             return;
12562         } 
12563         var node = this.getNode(nodeInfo);
12564         if(!node || this.isSelected(node)){
12565             return; // already selected.
12566         }
12567         if(!keepExisting){
12568             this.clearSelections(true);
12569         }
12570         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12571             Roo.fly(node).addClass(this.selectedClass);
12572             this.selections.push(node);
12573             if(!suppressEvent){
12574                 this.fireEvent("selectionchange", this, this.selections);
12575             }
12576         }
12577         
12578         
12579     },
12580       /**
12581      * Unselects nodes.
12582      * @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
12583      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12584      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12585      */
12586     unselect : function(nodeInfo, keepExisting, suppressEvent)
12587     {
12588         if(nodeInfo instanceof Array){
12589             Roo.each(this.selections, function(s) {
12590                 this.unselect(s, nodeInfo);
12591             }, this);
12592             return;
12593         }
12594         var node = this.getNode(nodeInfo);
12595         if(!node || !this.isSelected(node)){
12596             Roo.log("not selected");
12597             return; // not selected.
12598         }
12599         // fireevent???
12600         var ns = [];
12601         Roo.each(this.selections, function(s) {
12602             if (s == node ) {
12603                 Roo.fly(node).removeClass(this.selectedClass);
12604
12605                 return;
12606             }
12607             ns.push(s);
12608         },this);
12609         
12610         this.selections= ns;
12611         this.fireEvent("selectionchange", this, this.selections);
12612     },
12613
12614     /**
12615      * Gets a template node.
12616      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12617      * @return {HTMLElement} The node or null if it wasn't found
12618      */
12619     getNode : function(nodeInfo){
12620         if(typeof nodeInfo == "string"){
12621             return document.getElementById(nodeInfo);
12622         }else if(typeof nodeInfo == "number"){
12623             return this.nodes[nodeInfo];
12624         }
12625         return nodeInfo;
12626     },
12627
12628     /**
12629      * Gets a range template nodes.
12630      * @param {Number} startIndex
12631      * @param {Number} endIndex
12632      * @return {Array} An array of nodes
12633      */
12634     getNodes : function(start, end){
12635         var ns = this.nodes;
12636         start = start || 0;
12637         end = typeof end == "undefined" ? ns.length - 1 : end;
12638         var nodes = [];
12639         if(start <= end){
12640             for(var i = start; i <= end; i++){
12641                 nodes.push(ns[i]);
12642             }
12643         } else{
12644             for(var i = start; i >= end; i--){
12645                 nodes.push(ns[i]);
12646             }
12647         }
12648         return nodes;
12649     },
12650
12651     /**
12652      * Finds the index of the passed node
12653      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12654      * @return {Number} The index of the node or -1
12655      */
12656     indexOf : function(node){
12657         node = this.getNode(node);
12658         if(typeof node.nodeIndex == "number"){
12659             return node.nodeIndex;
12660         }
12661         var ns = this.nodes;
12662         for(var i = 0, len = ns.length; i < len; i++){
12663             if(ns[i] == node){
12664                 return i;
12665             }
12666         }
12667         return -1;
12668     }
12669 });
12670 /*
12671  * - LGPL
12672  *
12673  * based on jquery fullcalendar
12674  * 
12675  */
12676
12677 Roo.bootstrap = Roo.bootstrap || {};
12678 /**
12679  * @class Roo.bootstrap.Calendar
12680  * @extends Roo.bootstrap.Component
12681  * Bootstrap Calendar class
12682  * @cfg {Boolean} loadMask (true|false) default false
12683  * @cfg {Object} header generate the user specific header of the calendar, default false
12684
12685  * @constructor
12686  * Create a new Container
12687  * @param {Object} config The config object
12688  */
12689
12690
12691
12692 Roo.bootstrap.Calendar = function(config){
12693     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12694      this.addEvents({
12695         /**
12696              * @event select
12697              * Fires when a date is selected
12698              * @param {DatePicker} this
12699              * @param {Date} date The selected date
12700              */
12701         'select': true,
12702         /**
12703              * @event monthchange
12704              * Fires when the displayed month changes 
12705              * @param {DatePicker} this
12706              * @param {Date} date The selected month
12707              */
12708         'monthchange': true,
12709         /**
12710              * @event evententer
12711              * Fires when mouse over an event
12712              * @param {Calendar} this
12713              * @param {event} Event
12714              */
12715         'evententer': true,
12716         /**
12717              * @event eventleave
12718              * Fires when the mouse leaves an
12719              * @param {Calendar} this
12720              * @param {event}
12721              */
12722         'eventleave': true,
12723         /**
12724              * @event eventclick
12725              * Fires when the mouse click an
12726              * @param {Calendar} this
12727              * @param {event}
12728              */
12729         'eventclick': true
12730         
12731     });
12732
12733 };
12734
12735 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12736     
12737      /**
12738      * @cfg {Number} startDay
12739      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12740      */
12741     startDay : 0,
12742     
12743     loadMask : false,
12744     
12745     header : false,
12746       
12747     getAutoCreate : function(){
12748         
12749         
12750         var fc_button = function(name, corner, style, content ) {
12751             return Roo.apply({},{
12752                 tag : 'span',
12753                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12754                          (corner.length ?
12755                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12756                             ''
12757                         ),
12758                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12759                 unselectable: 'on'
12760             });
12761         };
12762         
12763         var header = {};
12764         
12765         if(!this.header){
12766             header = {
12767                 tag : 'table',
12768                 cls : 'fc-header',
12769                 style : 'width:100%',
12770                 cn : [
12771                     {
12772                         tag: 'tr',
12773                         cn : [
12774                             {
12775                                 tag : 'td',
12776                                 cls : 'fc-header-left',
12777                                 cn : [
12778                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12779                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12780                                     { tag: 'span', cls: 'fc-header-space' },
12781                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12782
12783
12784                                 ]
12785                             },
12786
12787                             {
12788                                 tag : 'td',
12789                                 cls : 'fc-header-center',
12790                                 cn : [
12791                                     {
12792                                         tag: 'span',
12793                                         cls: 'fc-header-title',
12794                                         cn : {
12795                                             tag: 'H2',
12796                                             html : 'month / year'
12797                                         }
12798                                     }
12799
12800                                 ]
12801                             },
12802                             {
12803                                 tag : 'td',
12804                                 cls : 'fc-header-right',
12805                                 cn : [
12806                               /*      fc_button('month', 'left', '', 'month' ),
12807                                     fc_button('week', '', '', 'week' ),
12808                                     fc_button('day', 'right', '', 'day' )
12809                                 */    
12810
12811                                 ]
12812                             }
12813
12814                         ]
12815                     }
12816                 ]
12817             };
12818         }
12819         
12820         header = this.header;
12821         
12822        
12823         var cal_heads = function() {
12824             var ret = [];
12825             // fixme - handle this.
12826             
12827             for (var i =0; i < Date.dayNames.length; i++) {
12828                 var d = Date.dayNames[i];
12829                 ret.push({
12830                     tag: 'th',
12831                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12832                     html : d.substring(0,3)
12833                 });
12834                 
12835             }
12836             ret[0].cls += ' fc-first';
12837             ret[6].cls += ' fc-last';
12838             return ret;
12839         };
12840         var cal_cell = function(n) {
12841             return  {
12842                 tag: 'td',
12843                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12844                 cn : [
12845                     {
12846                         cn : [
12847                             {
12848                                 cls: 'fc-day-number',
12849                                 html: 'D'
12850                             },
12851                             {
12852                                 cls: 'fc-day-content',
12853                              
12854                                 cn : [
12855                                      {
12856                                         style: 'position: relative;' // height: 17px;
12857                                     }
12858                                 ]
12859                             }
12860                             
12861                             
12862                         ]
12863                     }
12864                 ]
12865                 
12866             }
12867         };
12868         var cal_rows = function() {
12869             
12870             var ret = []
12871             for (var r = 0; r < 6; r++) {
12872                 var row= {
12873                     tag : 'tr',
12874                     cls : 'fc-week',
12875                     cn : []
12876                 };
12877                 
12878                 for (var i =0; i < Date.dayNames.length; i++) {
12879                     var d = Date.dayNames[i];
12880                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12881
12882                 }
12883                 row.cn[0].cls+=' fc-first';
12884                 row.cn[0].cn[0].style = 'min-height:90px';
12885                 row.cn[6].cls+=' fc-last';
12886                 ret.push(row);
12887                 
12888             }
12889             ret[0].cls += ' fc-first';
12890             ret[4].cls += ' fc-prev-last';
12891             ret[5].cls += ' fc-last';
12892             return ret;
12893             
12894         };
12895         
12896         var cal_table = {
12897             tag: 'table',
12898             cls: 'fc-border-separate',
12899             style : 'width:100%',
12900             cellspacing  : 0,
12901             cn : [
12902                 { 
12903                     tag: 'thead',
12904                     cn : [
12905                         { 
12906                             tag: 'tr',
12907                             cls : 'fc-first fc-last',
12908                             cn : cal_heads()
12909                         }
12910                     ]
12911                 },
12912                 { 
12913                     tag: 'tbody',
12914                     cn : cal_rows()
12915                 }
12916                   
12917             ]
12918         };
12919          
12920          var cfg = {
12921             cls : 'fc fc-ltr',
12922             cn : [
12923                 header,
12924                 {
12925                     cls : 'fc-content',
12926                     style : "position: relative;",
12927                     cn : [
12928                         {
12929                             cls : 'fc-view fc-view-month fc-grid',
12930                             style : 'position: relative',
12931                             unselectable : 'on',
12932                             cn : [
12933                                 {
12934                                     cls : 'fc-event-container',
12935                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12936                                 },
12937                                 cal_table
12938                             ]
12939                         }
12940                     ]
12941     
12942                 }
12943            ] 
12944             
12945         };
12946         
12947          
12948         
12949         return cfg;
12950     },
12951     
12952     
12953     initEvents : function()
12954     {
12955         if(!this.store){
12956             throw "can not find store for calendar";
12957         }
12958         
12959         var mark = {
12960             tag: "div",
12961             cls:"x-dlg-mask",
12962             style: "text-align:center",
12963             cn: [
12964                 {
12965                     tag: "div",
12966                     style: "background-color:white;width:50%;margin:250 auto",
12967                     cn: [
12968                         {
12969                             tag: "img",
12970                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12971                         },
12972                         {
12973                             tag: "span",
12974                             html: "Loading"
12975                         }
12976                         
12977                     ]
12978                 }
12979             ]
12980         }
12981         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12982         
12983         var size = this.el.select('.fc-content', true).first().getSize();
12984         this.maskEl.setSize(size.width, size.height);
12985         this.maskEl.enableDisplayMode("block");
12986         if(!this.loadMask){
12987             this.maskEl.hide();
12988         }
12989         
12990         this.store = Roo.factory(this.store, Roo.data);
12991         this.store.on('load', this.onLoad, this);
12992         this.store.on('beforeload', this.onBeforeLoad, this);
12993         
12994         this.resize();
12995         
12996         this.cells = this.el.select('.fc-day',true);
12997         //Roo.log(this.cells);
12998         this.textNodes = this.el.query('.fc-day-number');
12999         this.cells.addClassOnOver('fc-state-hover');
13000         
13001         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13002         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13003         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13004         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13005         
13006         this.on('monthchange', this.onMonthChange, this);
13007         
13008         this.update(new Date().clearTime());
13009     },
13010     
13011     resize : function() {
13012         var sz  = this.el.getSize();
13013         
13014         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13015         this.el.select('.fc-day-content div',true).setHeight(34);
13016     },
13017     
13018     
13019     // private
13020     showPrevMonth : function(e){
13021         this.update(this.activeDate.add("mo", -1));
13022     },
13023     showToday : function(e){
13024         this.update(new Date().clearTime());
13025     },
13026     // private
13027     showNextMonth : function(e){
13028         this.update(this.activeDate.add("mo", 1));
13029     },
13030
13031     // private
13032     showPrevYear : function(){
13033         this.update(this.activeDate.add("y", -1));
13034     },
13035
13036     // private
13037     showNextYear : function(){
13038         this.update(this.activeDate.add("y", 1));
13039     },
13040
13041     
13042    // private
13043     update : function(date)
13044     {
13045         var vd = this.activeDate;
13046         this.activeDate = date;
13047 //        if(vd && this.el){
13048 //            var t = date.getTime();
13049 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13050 //                Roo.log('using add remove');
13051 //                
13052 //                this.fireEvent('monthchange', this, date);
13053 //                
13054 //                this.cells.removeClass("fc-state-highlight");
13055 //                this.cells.each(function(c){
13056 //                   if(c.dateValue == t){
13057 //                       c.addClass("fc-state-highlight");
13058 //                       setTimeout(function(){
13059 //                            try{c.dom.firstChild.focus();}catch(e){}
13060 //                       }, 50);
13061 //                       return false;
13062 //                   }
13063 //                   return true;
13064 //                });
13065 //                return;
13066 //            }
13067 //        }
13068         
13069         var days = date.getDaysInMonth();
13070         
13071         var firstOfMonth = date.getFirstDateOfMonth();
13072         var startingPos = firstOfMonth.getDay()-this.startDay;
13073         
13074         if(startingPos < this.startDay){
13075             startingPos += 7;
13076         }
13077         
13078         var pm = date.add(Date.MONTH, -1);
13079         var prevStart = pm.getDaysInMonth()-startingPos;
13080 //        
13081         this.cells = this.el.select('.fc-day',true);
13082         this.textNodes = this.el.query('.fc-day-number');
13083         this.cells.addClassOnOver('fc-state-hover');
13084         
13085         var cells = this.cells.elements;
13086         var textEls = this.textNodes;
13087         
13088         Roo.each(cells, function(cell){
13089             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13090         });
13091         
13092         days += startingPos;
13093
13094         // convert everything to numbers so it's fast
13095         var day = 86400000;
13096         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13097         //Roo.log(d);
13098         //Roo.log(pm);
13099         //Roo.log(prevStart);
13100         
13101         var today = new Date().clearTime().getTime();
13102         var sel = date.clearTime().getTime();
13103         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13104         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13105         var ddMatch = this.disabledDatesRE;
13106         var ddText = this.disabledDatesText;
13107         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13108         var ddaysText = this.disabledDaysText;
13109         var format = this.format;
13110         
13111         var setCellClass = function(cal, cell){
13112             cell.row = 0;
13113             cell.events = [];
13114             cell.more = [];
13115             //Roo.log('set Cell Class');
13116             cell.title = "";
13117             var t = d.getTime();
13118             
13119             //Roo.log(d);
13120             
13121             cell.dateValue = t;
13122             if(t == today){
13123                 cell.className += " fc-today";
13124                 cell.className += " fc-state-highlight";
13125                 cell.title = cal.todayText;
13126             }
13127             if(t == sel){
13128                 // disable highlight in other month..
13129                 //cell.className += " fc-state-highlight";
13130                 
13131             }
13132             // disabling
13133             if(t < min) {
13134                 cell.className = " fc-state-disabled";
13135                 cell.title = cal.minText;
13136                 return;
13137             }
13138             if(t > max) {
13139                 cell.className = " fc-state-disabled";
13140                 cell.title = cal.maxText;
13141                 return;
13142             }
13143             if(ddays){
13144                 if(ddays.indexOf(d.getDay()) != -1){
13145                     cell.title = ddaysText;
13146                     cell.className = " fc-state-disabled";
13147                 }
13148             }
13149             if(ddMatch && format){
13150                 var fvalue = d.dateFormat(format);
13151                 if(ddMatch.test(fvalue)){
13152                     cell.title = ddText.replace("%0", fvalue);
13153                     cell.className = " fc-state-disabled";
13154                 }
13155             }
13156             
13157             if (!cell.initialClassName) {
13158                 cell.initialClassName = cell.dom.className;
13159             }
13160             
13161             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13162         };
13163
13164         var i = 0;
13165         
13166         for(; i < startingPos; i++) {
13167             textEls[i].innerHTML = (++prevStart);
13168             d.setDate(d.getDate()+1);
13169             
13170             cells[i].className = "fc-past fc-other-month";
13171             setCellClass(this, cells[i]);
13172         }
13173         
13174         var intDay = 0;
13175         
13176         for(; i < days; i++){
13177             intDay = i - startingPos + 1;
13178             textEls[i].innerHTML = (intDay);
13179             d.setDate(d.getDate()+1);
13180             
13181             cells[i].className = ''; // "x-date-active";
13182             setCellClass(this, cells[i]);
13183         }
13184         var extraDays = 0;
13185         
13186         for(; i < 42; i++) {
13187             textEls[i].innerHTML = (++extraDays);
13188             d.setDate(d.getDate()+1);
13189             
13190             cells[i].className = "fc-future fc-other-month";
13191             setCellClass(this, cells[i]);
13192         }
13193         
13194         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13195         
13196         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13197         
13198         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13199         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13200         
13201         if(totalRows != 6){
13202             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13203             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13204         }
13205         
13206         this.fireEvent('monthchange', this, date);
13207         
13208         
13209         /*
13210         if(!this.internalRender){
13211             var main = this.el.dom.firstChild;
13212             var w = main.offsetWidth;
13213             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13214             Roo.fly(main).setWidth(w);
13215             this.internalRender = true;
13216             // opera does not respect the auto grow header center column
13217             // then, after it gets a width opera refuses to recalculate
13218             // without a second pass
13219             if(Roo.isOpera && !this.secondPass){
13220                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13221                 this.secondPass = true;
13222                 this.update.defer(10, this, [date]);
13223             }
13224         }
13225         */
13226         
13227     },
13228     
13229     findCell : function(dt) {
13230         dt = dt.clearTime().getTime();
13231         var ret = false;
13232         this.cells.each(function(c){
13233             //Roo.log("check " +c.dateValue + '?=' + dt);
13234             if(c.dateValue == dt){
13235                 ret = c;
13236                 return false;
13237             }
13238             return true;
13239         });
13240         
13241         return ret;
13242     },
13243     
13244     findCells : function(ev) {
13245         var s = ev.start.clone().clearTime().getTime();
13246        // Roo.log(s);
13247         var e= ev.end.clone().clearTime().getTime();
13248        // Roo.log(e);
13249         var ret = [];
13250         this.cells.each(function(c){
13251              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13252             
13253             if(c.dateValue > e){
13254                 return ;
13255             }
13256             if(c.dateValue < s){
13257                 return ;
13258             }
13259             ret.push(c);
13260         });
13261         
13262         return ret;    
13263     },
13264     
13265 //    findBestRow: function(cells)
13266 //    {
13267 //        var ret = 0;
13268 //        
13269 //        for (var i =0 ; i < cells.length;i++) {
13270 //            ret  = Math.max(cells[i].rows || 0,ret);
13271 //        }
13272 //        return ret;
13273 //        
13274 //    },
13275     
13276     
13277     addItem : function(ev)
13278     {
13279         // look for vertical location slot in
13280         var cells = this.findCells(ev);
13281         
13282 //        ev.row = this.findBestRow(cells);
13283         
13284         // work out the location.
13285         
13286         var crow = false;
13287         var rows = [];
13288         for(var i =0; i < cells.length; i++) {
13289             
13290             cells[i].row = cells[0].row;
13291             
13292             if(i == 0){
13293                 cells[i].row = cells[i].row + 1;
13294             }
13295             
13296             if (!crow) {
13297                 crow = {
13298                     start : cells[i],
13299                     end :  cells[i]
13300                 };
13301                 continue;
13302             }
13303             if (crow.start.getY() == cells[i].getY()) {
13304                 // on same row.
13305                 crow.end = cells[i];
13306                 continue;
13307             }
13308             // different row.
13309             rows.push(crow);
13310             crow = {
13311                 start: cells[i],
13312                 end : cells[i]
13313             };
13314             
13315         }
13316         
13317         rows.push(crow);
13318         ev.els = [];
13319         ev.rows = rows;
13320         ev.cells = cells;
13321         
13322         cells[0].events.push(ev);
13323         
13324         this.calevents.push(ev);
13325     },
13326     
13327     clearEvents: function() {
13328         
13329         if(!this.calevents){
13330             return;
13331         }
13332         
13333         Roo.each(this.cells.elements, function(c){
13334             c.row = 0;
13335             c.events = [];
13336             c.more = [];
13337         });
13338         
13339         Roo.each(this.calevents, function(e) {
13340             Roo.each(e.els, function(el) {
13341                 el.un('mouseenter' ,this.onEventEnter, this);
13342                 el.un('mouseleave' ,this.onEventLeave, this);
13343                 el.remove();
13344             },this);
13345         },this);
13346         
13347         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13348             e.remove();
13349         });
13350         
13351     },
13352     
13353     renderEvents: function()
13354     {   
13355         var _this = this;
13356         
13357         this.cells.each(function(c) {
13358             
13359             if(c.row < 5){
13360                 return;
13361             }
13362             
13363             var ev = c.events;
13364             
13365             var r = 4;
13366             if(c.row != c.events.length){
13367                 r = 4 - (4 - (c.row - c.events.length));
13368             }
13369             
13370             c.events = ev.slice(0, r);
13371             c.more = ev.slice(r);
13372             
13373             if(c.more.length && c.more.length == 1){
13374                 c.events.push(c.more.pop());
13375             }
13376             
13377             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13378             
13379         });
13380             
13381         this.cells.each(function(c) {
13382             
13383             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13384             
13385             
13386             for (var e = 0; e < c.events.length; e++){
13387                 var ev = c.events[e];
13388                 var rows = ev.rows;
13389                 
13390                 for(var i = 0; i < rows.length; i++) {
13391                 
13392                     // how many rows should it span..
13393
13394                     var  cfg = {
13395                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13396                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13397
13398                         unselectable : "on",
13399                         cn : [
13400                             {
13401                                 cls: 'fc-event-inner',
13402                                 cn : [
13403     //                                {
13404     //                                  tag:'span',
13405     //                                  cls: 'fc-event-time',
13406     //                                  html : cells.length > 1 ? '' : ev.time
13407     //                                },
13408                                     {
13409                                       tag:'span',
13410                                       cls: 'fc-event-title',
13411                                       html : String.format('{0}', ev.title)
13412                                     }
13413
13414
13415                                 ]
13416                             },
13417                             {
13418                                 cls: 'ui-resizable-handle ui-resizable-e',
13419                                 html : '&nbsp;&nbsp;&nbsp'
13420                             }
13421
13422                         ]
13423                     };
13424
13425                     if (i == 0) {
13426                         cfg.cls += ' fc-event-start';
13427                     }
13428                     if ((i+1) == rows.length) {
13429                         cfg.cls += ' fc-event-end';
13430                     }
13431
13432                     var ctr = _this.el.select('.fc-event-container',true).first();
13433                     var cg = ctr.createChild(cfg);
13434
13435                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13436                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13437
13438                     var r = (c.more.length) ? 1 : 0;
13439                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13440                     cg.setWidth(ebox.right - sbox.x -2);
13441
13442                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13443                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13444                     cg.on('click', _this.onEventClick, _this, ev);
13445
13446                     ev.els.push(cg);
13447                     
13448                 }
13449                 
13450             }
13451             
13452             
13453             if(c.more.length){
13454                 var  cfg = {
13455                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13456                     style : 'position: absolute',
13457                     unselectable : "on",
13458                     cn : [
13459                         {
13460                             cls: 'fc-event-inner',
13461                             cn : [
13462                                 {
13463                                   tag:'span',
13464                                   cls: 'fc-event-title',
13465                                   html : 'More'
13466                                 }
13467
13468
13469                             ]
13470                         },
13471                         {
13472                             cls: 'ui-resizable-handle ui-resizable-e',
13473                             html : '&nbsp;&nbsp;&nbsp'
13474                         }
13475
13476                     ]
13477                 };
13478
13479                 var ctr = _this.el.select('.fc-event-container',true).first();
13480                 var cg = ctr.createChild(cfg);
13481
13482                 var sbox = c.select('.fc-day-content',true).first().getBox();
13483                 var ebox = c.select('.fc-day-content',true).first().getBox();
13484                 //Roo.log(cg);
13485                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13486                 cg.setWidth(ebox.right - sbox.x -2);
13487
13488                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13489                 
13490             }
13491             
13492         });
13493         
13494         
13495         
13496     },
13497     
13498     onEventEnter: function (e, el,event,d) {
13499         this.fireEvent('evententer', this, el, event);
13500     },
13501     
13502     onEventLeave: function (e, el,event,d) {
13503         this.fireEvent('eventleave', this, el, event);
13504     },
13505     
13506     onEventClick: function (e, el,event,d) {
13507         this.fireEvent('eventclick', this, el, event);
13508     },
13509     
13510     onMonthChange: function () {
13511         this.store.load();
13512     },
13513     
13514     onMoreEventClick: function(e, el, more)
13515     {
13516         var _this = this;
13517         
13518         this.calpopover.placement = 'right';
13519         this.calpopover.setTitle('More');
13520         
13521         this.calpopover.setContent('');
13522         
13523         var ctr = this.calpopover.el.select('.popover-content', true).first();
13524         
13525         Roo.each(more, function(m){
13526             var cfg = {
13527                 cls : 'fc-event-hori fc-event-draggable',
13528                 html : m.title
13529             }
13530             var cg = ctr.createChild(cfg);
13531             
13532             cg.on('click', _this.onEventClick, _this, m);
13533         });
13534         
13535         this.calpopover.show(el);
13536         
13537         
13538     },
13539     
13540     onLoad: function () 
13541     {   
13542         this.calevents = [];
13543         var cal = this;
13544         
13545         if(this.store.getCount() > 0){
13546             this.store.data.each(function(d){
13547                cal.addItem({
13548                     id : d.data.id,
13549                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13550                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13551                     time : d.data.start_time,
13552                     title : d.data.title,
13553                     description : d.data.description,
13554                     venue : d.data.venue
13555                 });
13556             });
13557         }
13558         
13559         this.renderEvents();
13560         
13561         if(this.calevents.length && this.loadMask){
13562             this.maskEl.hide();
13563         }
13564     },
13565     
13566     onBeforeLoad: function()
13567     {
13568         this.clearEvents();
13569         if(this.loadMask){
13570             this.maskEl.show();
13571         }
13572     }
13573 });
13574
13575  
13576  /*
13577  * - LGPL
13578  *
13579  * element
13580  * 
13581  */
13582
13583 /**
13584  * @class Roo.bootstrap.Popover
13585  * @extends Roo.bootstrap.Component
13586  * Bootstrap Popover class
13587  * @cfg {String} html contents of the popover   (or false to use children..)
13588  * @cfg {String} title of popover (or false to hide)
13589  * @cfg {String} placement how it is placed
13590  * @cfg {String} trigger click || hover (or false to trigger manually)
13591  * @cfg {String} over what (parent or false to trigger manually.)
13592  * 
13593  * @constructor
13594  * Create a new Popover
13595  * @param {Object} config The config object
13596  */
13597
13598 Roo.bootstrap.Popover = function(config){
13599     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13600 };
13601
13602 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13603     
13604     title: 'Fill in a title',
13605     html: false,
13606     
13607     placement : 'right',
13608     trigger : 'hover', // hover
13609     
13610     over: 'parent',
13611     
13612     can_build_overlaid : false,
13613     
13614     getChildContainer : function()
13615     {
13616         return this.el.select('.popover-content',true).first();
13617     },
13618     
13619     getAutoCreate : function(){
13620          Roo.log('make popover?');
13621         var cfg = {
13622            cls : 'popover roo-dynamic',
13623            style: 'display:block',
13624            cn : [
13625                 {
13626                     cls : 'arrow'
13627                 },
13628                 {
13629                     cls : 'popover-inner',
13630                     cn : [
13631                         {
13632                             tag: 'h3',
13633                             cls: 'popover-title',
13634                             html : this.title
13635                         },
13636                         {
13637                             cls : 'popover-content',
13638                             html : this.html
13639                         }
13640                     ]
13641                     
13642                 }
13643            ]
13644         };
13645         
13646         return cfg;
13647     },
13648     setTitle: function(str)
13649     {
13650         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13651     },
13652     setContent: function(str)
13653     {
13654         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13655     },
13656     // as it get's added to the bottom of the page.
13657     onRender : function(ct, position)
13658     {
13659         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13660         if(!this.el){
13661             var cfg = Roo.apply({},  this.getAutoCreate());
13662             cfg.id = Roo.id();
13663             
13664             if (this.cls) {
13665                 cfg.cls += ' ' + this.cls;
13666             }
13667             if (this.style) {
13668                 cfg.style = this.style;
13669             }
13670             Roo.log("adding to ")
13671             this.el = Roo.get(document.body).createChild(cfg, position);
13672             Roo.log(this.el);
13673         }
13674         this.initEvents();
13675     },
13676     
13677     initEvents : function()
13678     {
13679         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13680         this.el.enableDisplayMode('block');
13681         this.el.hide();
13682         if (this.over === false) {
13683             return; 
13684         }
13685         if (this.triggers === false) {
13686             return;
13687         }
13688         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13689         var triggers = this.trigger ? this.trigger.split(' ') : [];
13690         Roo.each(triggers, function(trigger) {
13691         
13692             if (trigger == 'click') {
13693                 on_el.on('click', this.toggle, this);
13694             } else if (trigger != 'manual') {
13695                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13696                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13697       
13698                 on_el.on(eventIn  ,this.enter, this);
13699                 on_el.on(eventOut, this.leave, this);
13700             }
13701         }, this);
13702         
13703     },
13704     
13705     
13706     // private
13707     timeout : null,
13708     hoverState : null,
13709     
13710     toggle : function () {
13711         this.hoverState == 'in' ? this.leave() : this.enter();
13712     },
13713     
13714     enter : function () {
13715        
13716     
13717         clearTimeout(this.timeout);
13718     
13719         this.hoverState = 'in'
13720     
13721         if (!this.delay || !this.delay.show) {
13722             this.show();
13723             return 
13724         }
13725         var _t = this;
13726         this.timeout = setTimeout(function () {
13727             if (_t.hoverState == 'in') {
13728                 _t.show();
13729             }
13730         }, this.delay.show)
13731     },
13732     leave : function() {
13733         clearTimeout(this.timeout);
13734     
13735         this.hoverState = 'out'
13736     
13737         if (!this.delay || !this.delay.hide) {
13738             this.hide();
13739             return 
13740         }
13741         var _t = this;
13742         this.timeout = setTimeout(function () {
13743             if (_t.hoverState == 'out') {
13744                 _t.hide();
13745             }
13746         }, this.delay.hide)
13747     },
13748     
13749     show : function (on_el)
13750     {
13751         if (!on_el) {
13752             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13753         }
13754         // set content.
13755         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13756         if (this.html !== false) {
13757             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13758         }
13759         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13760         if (!this.title.length) {
13761             this.el.select('.popover-title',true).hide();
13762         }
13763         
13764         var placement = typeof this.placement == 'function' ?
13765             this.placement.call(this, this.el, on_el) :
13766             this.placement;
13767             
13768         var autoToken = /\s?auto?\s?/i;
13769         var autoPlace = autoToken.test(placement);
13770         if (autoPlace) {
13771             placement = placement.replace(autoToken, '') || 'top';
13772         }
13773         
13774         //this.el.detach()
13775         //this.el.setXY([0,0]);
13776         this.el.show();
13777         this.el.dom.style.display='block';
13778         this.el.addClass(placement);
13779         
13780         //this.el.appendTo(on_el);
13781         
13782         var p = this.getPosition();
13783         var box = this.el.getBox();
13784         
13785         if (autoPlace) {
13786             // fixme..
13787         }
13788         var align = Roo.bootstrap.Popover.alignment[placement]
13789         this.el.alignTo(on_el, align[0],align[1]);
13790         //var arrow = this.el.select('.arrow',true).first();
13791         //arrow.set(align[2], 
13792         
13793         this.el.addClass('in');
13794         this.hoverState = null;
13795         
13796         if (this.el.hasClass('fade')) {
13797             // fade it?
13798         }
13799         
13800     },
13801     hide : function()
13802     {
13803         this.el.setXY([0,0]);
13804         this.el.removeClass('in');
13805         this.el.hide();
13806         
13807     }
13808     
13809 });
13810
13811 Roo.bootstrap.Popover.alignment = {
13812     'left' : ['r-l', [-10,0], 'right'],
13813     'right' : ['l-r', [10,0], 'left'],
13814     'bottom' : ['t-b', [0,10], 'top'],
13815     'top' : [ 'b-t', [0,-10], 'bottom']
13816 };
13817
13818  /*
13819  * - LGPL
13820  *
13821  * Progress
13822  * 
13823  */
13824
13825 /**
13826  * @class Roo.bootstrap.Progress
13827  * @extends Roo.bootstrap.Component
13828  * Bootstrap Progress class
13829  * @cfg {Boolean} striped striped of the progress bar
13830  * @cfg {Boolean} active animated of the progress bar
13831  * 
13832  * 
13833  * @constructor
13834  * Create a new Progress
13835  * @param {Object} config The config object
13836  */
13837
13838 Roo.bootstrap.Progress = function(config){
13839     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13840 };
13841
13842 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13843     
13844     striped : false,
13845     active: false,
13846     
13847     getAutoCreate : function(){
13848         var cfg = {
13849             tag: 'div',
13850             cls: 'progress'
13851         };
13852         
13853         
13854         if(this.striped){
13855             cfg.cls += ' progress-striped';
13856         }
13857       
13858         if(this.active){
13859             cfg.cls += ' active';
13860         }
13861         
13862         
13863         return cfg;
13864     }
13865    
13866 });
13867
13868  
13869
13870  /*
13871  * - LGPL
13872  *
13873  * ProgressBar
13874  * 
13875  */
13876
13877 /**
13878  * @class Roo.bootstrap.ProgressBar
13879  * @extends Roo.bootstrap.Component
13880  * Bootstrap ProgressBar class
13881  * @cfg {Number} aria_valuenow aria-value now
13882  * @cfg {Number} aria_valuemin aria-value min
13883  * @cfg {Number} aria_valuemax aria-value max
13884  * @cfg {String} label label for the progress bar
13885  * @cfg {String} panel (success | info | warning | danger )
13886  * @cfg {String} role role of the progress bar
13887  * @cfg {String} sr_only text
13888  * 
13889  * 
13890  * @constructor
13891  * Create a new ProgressBar
13892  * @param {Object} config The config object
13893  */
13894
13895 Roo.bootstrap.ProgressBar = function(config){
13896     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13897 };
13898
13899 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13900     
13901     aria_valuenow : 0,
13902     aria_valuemin : 0,
13903     aria_valuemax : 100,
13904     label : false,
13905     panel : false,
13906     role : false,
13907     sr_only: false,
13908     
13909     getAutoCreate : function()
13910     {
13911         
13912         var cfg = {
13913             tag: 'div',
13914             cls: 'progress-bar',
13915             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13916         };
13917         
13918         if(this.sr_only){
13919             cfg.cn = {
13920                 tag: 'span',
13921                 cls: 'sr-only',
13922                 html: this.sr_only
13923             }
13924         }
13925         
13926         if(this.role){
13927             cfg.role = this.role;
13928         }
13929         
13930         if(this.aria_valuenow){
13931             cfg['aria-valuenow'] = this.aria_valuenow;
13932         }
13933         
13934         if(this.aria_valuemin){
13935             cfg['aria-valuemin'] = this.aria_valuemin;
13936         }
13937         
13938         if(this.aria_valuemax){
13939             cfg['aria-valuemax'] = this.aria_valuemax;
13940         }
13941         
13942         if(this.label && !this.sr_only){
13943             cfg.html = this.label;
13944         }
13945         
13946         if(this.panel){
13947             cfg.cls += ' progress-bar-' + this.panel;
13948         }
13949         
13950         return cfg;
13951     },
13952     
13953     update : function(aria_valuenow)
13954     {
13955         this.aria_valuenow = aria_valuenow;
13956         
13957         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13958     }
13959    
13960 });
13961
13962  
13963
13964  /*
13965  * - LGPL
13966  *
13967  * column
13968  * 
13969  */
13970
13971 /**
13972  * @class Roo.bootstrap.TabGroup
13973  * @extends Roo.bootstrap.Column
13974  * Bootstrap Column class
13975  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13976  * @cfg {Boolean} carousel true to make the group behave like a carousel
13977  * 
13978  * @constructor
13979  * Create a new TabGroup
13980  * @param {Object} config The config object
13981  */
13982
13983 Roo.bootstrap.TabGroup = function(config){
13984     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13985     if (!this.navId) {
13986         this.navId = Roo.id();
13987     }
13988     this.tabs = [];
13989     Roo.bootstrap.TabGroup.register(this);
13990     
13991 };
13992
13993 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13994     
13995     carousel : false,
13996     transition : false,
13997      
13998     getAutoCreate : function()
13999     {
14000         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14001         
14002         cfg.cls += ' tab-content';
14003         
14004         if (this.carousel) {
14005             cfg.cls += ' carousel slide';
14006             cfg.cn = [{
14007                cls : 'carousel-inner'
14008             }]
14009         }
14010         
14011         
14012         return cfg;
14013     },
14014     getChildContainer : function()
14015     {
14016         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14017     },
14018     
14019     /**
14020     * register a Navigation item
14021     * @param {Roo.bootstrap.NavItem} the navitem to add
14022     */
14023     register : function(item)
14024     {
14025         this.tabs.push( item);
14026         item.navId = this.navId; // not really needed..
14027     
14028     },
14029     
14030     getActivePanel : function()
14031     {
14032         var r = false;
14033         Roo.each(this.tabs, function(t) {
14034             if (t.active) {
14035                 r = t;
14036                 return false;
14037             }
14038             return null;
14039         });
14040         return r;
14041         
14042     },
14043     getPanelByName : function(n)
14044     {
14045         var r = false;
14046         Roo.each(this.tabs, function(t) {
14047             if (t.tabId == n) {
14048                 r = t;
14049                 return false;
14050             }
14051             return null;
14052         });
14053         return r;
14054     },
14055     indexOfPanel : function(p)
14056     {
14057         var r = false;
14058         Roo.each(this.tabs, function(t,i) {
14059             if (t.tabId == p.tabId) {
14060                 r = i;
14061                 return false;
14062             }
14063             return null;
14064         });
14065         return r;
14066     },
14067     /**
14068      * show a specific panel
14069      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14070      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14071      */
14072     showPanel : function (pan)
14073     {
14074         
14075         if (typeof(pan) == 'number') {
14076             pan = this.tabs[pan];
14077         }
14078         if (typeof(pan) == 'string') {
14079             pan = this.getPanelByName(pan);
14080         }
14081         if (pan.tabId == this.getActivePanel().tabId) {
14082             return true;
14083         }
14084         var cur = this.getActivePanel();
14085         
14086         if (false === cur.fireEvent('beforedeactivate')) {
14087             return false;
14088         }
14089         
14090         if (this.carousel) {
14091             this.transition = true;
14092             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14093             var lr = dir == 'next' ? 'left' : 'right';
14094             pan.el.addClass(dir); // or prev
14095             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14096             cur.el.addClass(lr); // or right
14097             pan.el.addClass(lr);
14098             
14099             var _this = this;
14100             cur.el.on('transitionend', function() {
14101                 Roo.log("trans end?");
14102                 
14103                 pan.el.removeClass([lr,dir]);
14104                 pan.setActive(true);
14105                 
14106                 cur.el.removeClass([lr]);
14107                 cur.setActive(false);
14108                 
14109                 _this.transition = false;
14110                 
14111             }, this, { single:  true } );
14112             return true;
14113         }
14114         
14115         cur.setActive(false);
14116         pan.setActive(true);
14117         return true;
14118         
14119     },
14120     showPanelNext : function()
14121     {
14122         var i = this.indexOfPanel(this.getActivePanel());
14123         if (i > this.tabs.length) {
14124             return;
14125         }
14126         this.showPanel(this.tabs[i+1]);
14127     },
14128     showPanelPrev : function()
14129     {
14130         var i = this.indexOfPanel(this.getActivePanel());
14131         if (i  < 1) {
14132             return;
14133         }
14134         this.showPanel(this.tabs[i-1]);
14135     }
14136     
14137     
14138   
14139 });
14140
14141  
14142
14143  
14144  
14145 Roo.apply(Roo.bootstrap.TabGroup, {
14146     
14147     groups: {},
14148      /**
14149     * register a Navigation Group
14150     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14151     */
14152     register : function(navgrp)
14153     {
14154         this.groups[navgrp.navId] = navgrp;
14155         
14156     },
14157     /**
14158     * fetch a Navigation Group based on the navigation ID
14159     * if one does not exist , it will get created.
14160     * @param {string} the navgroup to add
14161     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14162     */
14163     get: function(navId) {
14164         if (typeof(this.groups[navId]) == 'undefined') {
14165             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14166         }
14167         return this.groups[navId] ;
14168     }
14169     
14170     
14171     
14172 });
14173
14174  /*
14175  * - LGPL
14176  *
14177  * TabPanel
14178  * 
14179  */
14180
14181 /**
14182  * @class Roo.bootstrap.TabPanel
14183  * @extends Roo.bootstrap.Component
14184  * Bootstrap TabPanel class
14185  * @cfg {Boolean} active panel active
14186  * @cfg {String} html panel content
14187  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14188  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14189  * 
14190  * 
14191  * @constructor
14192  * Create a new TabPanel
14193  * @param {Object} config The config object
14194  */
14195
14196 Roo.bootstrap.TabPanel = function(config){
14197     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14198     this.addEvents({
14199         /**
14200              * @event changed
14201              * Fires when the active status changes
14202              * @param {Roo.bootstrap.TabPanel} this
14203              * @param {Boolean} state the new state
14204             
14205          */
14206         'changed': true,
14207         /**
14208              * @event beforedeactivate
14209              * Fires before a tab is de-activated - can be used to do validation on a form.
14210              * @param {Roo.bootstrap.TabPanel} this
14211              * @return {Boolean} false if there is an error
14212             
14213          */
14214         'beforedeactivate': true
14215      });
14216     
14217     this.tabId = this.tabId || Roo.id();
14218   
14219 };
14220
14221 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14222     
14223     active: false,
14224     html: false,
14225     tabId: false,
14226     navId : false,
14227     
14228     getAutoCreate : function(){
14229         var cfg = {
14230             tag: 'div',
14231             // item is needed for carousel - not sure if it has any effect otherwise
14232             cls: 'tab-pane item',
14233             html: this.html || ''
14234         };
14235         
14236         if(this.active){
14237             cfg.cls += ' active';
14238         }
14239         
14240         if(this.tabId){
14241             cfg.tabId = this.tabId;
14242         }
14243         
14244         
14245         return cfg;
14246     },
14247     
14248     initEvents:  function()
14249     {
14250         Roo.log('-------- init events on tab panel ---------');
14251         
14252         var p = this.parent();
14253         this.navId = this.navId || p.navId;
14254         
14255         if (typeof(this.navId) != 'undefined') {
14256             // not really needed.. but just in case.. parent should be a NavGroup.
14257             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14258             Roo.log(['register', tg, this]);
14259             tg.register(this);
14260         }
14261     },
14262     
14263     
14264     onRender : function(ct, position)
14265     {
14266        // Roo.log("Call onRender: " + this.xtype);
14267         
14268         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14269         
14270         
14271         
14272         
14273         
14274     },
14275     
14276     setActive: function(state)
14277     {
14278         Roo.log("panel - set active " + this.tabId + "=" + state);
14279         
14280         this.active = state;
14281         if (!state) {
14282             this.el.removeClass('active');
14283             
14284         } else  if (!this.el.hasClass('active')) {
14285             this.el.addClass('active');
14286         }
14287         this.fireEvent('changed', this, state);
14288     }
14289     
14290     
14291 });
14292  
14293
14294  
14295
14296  /*
14297  * - LGPL
14298  *
14299  * DateField
14300  * 
14301  */
14302
14303 /**
14304  * @class Roo.bootstrap.DateField
14305  * @extends Roo.bootstrap.Input
14306  * Bootstrap DateField class
14307  * @cfg {Number} weekStart default 0
14308  * @cfg {Number} weekStart default 0
14309  * @cfg {Number} viewMode default empty, (months|years)
14310  * @cfg {Number} minViewMode default empty, (months|years)
14311  * @cfg {Number} startDate default -Infinity
14312  * @cfg {Number} endDate default Infinity
14313  * @cfg {Boolean} todayHighlight default false
14314  * @cfg {Boolean} todayBtn default false
14315  * @cfg {Boolean} calendarWeeks default false
14316  * @cfg {Object} daysOfWeekDisabled default empty
14317  * 
14318  * @cfg {Boolean} keyboardNavigation default true
14319  * @cfg {String} language default en
14320  * 
14321  * @constructor
14322  * Create a new DateField
14323  * @param {Object} config The config object
14324  */
14325
14326 Roo.bootstrap.DateField = function(config){
14327     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14328      this.addEvents({
14329             /**
14330              * @event show
14331              * Fires when this field show.
14332              * @param {Roo.bootstrap.DateField} this
14333              * @param {Mixed} date The date value
14334              */
14335             show : true,
14336             /**
14337              * @event show
14338              * Fires when this field hide.
14339              * @param {Roo.bootstrap.DateField} this
14340              * @param {Mixed} date The date value
14341              */
14342             hide : true,
14343             /**
14344              * @event select
14345              * Fires when select a date.
14346              * @param {Roo.bootstrap.DateField} this
14347              * @param {Mixed} date The date value
14348              */
14349             select : true
14350         });
14351 };
14352
14353 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14354     
14355     /**
14356      * @cfg {String} format
14357      * The default date format string which can be overriden for localization support.  The format must be
14358      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14359      */
14360     format : "m/d/y",
14361     /**
14362      * @cfg {String} altFormats
14363      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14364      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14365      */
14366     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14367     
14368     weekStart : 0,
14369     
14370     viewMode : '',
14371     
14372     minViewMode : '',
14373     
14374     todayHighlight : false,
14375     
14376     todayBtn: false,
14377     
14378     language: 'en',
14379     
14380     keyboardNavigation: true,
14381     
14382     calendarWeeks: false,
14383     
14384     startDate: -Infinity,
14385     
14386     endDate: Infinity,
14387     
14388     daysOfWeekDisabled: [],
14389     
14390     _events: [],
14391     
14392     UTCDate: function()
14393     {
14394         return new Date(Date.UTC.apply(Date, arguments));
14395     },
14396     
14397     UTCToday: function()
14398     {
14399         var today = new Date();
14400         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14401     },
14402     
14403     getDate: function() {
14404             var d = this.getUTCDate();
14405             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14406     },
14407     
14408     getUTCDate: function() {
14409             return this.date;
14410     },
14411     
14412     setDate: function(d) {
14413             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14414     },
14415     
14416     setUTCDate: function(d) {
14417             this.date = d;
14418             this.setValue(this.formatDate(this.date));
14419     },
14420         
14421     onRender: function(ct, position)
14422     {
14423         
14424         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14425         
14426         this.language = this.language || 'en';
14427         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14428         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14429         
14430         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14431         this.format = this.format || 'm/d/y';
14432         this.isInline = false;
14433         this.isInput = true;
14434         this.component = this.el.select('.add-on', true).first() || false;
14435         this.component = (this.component && this.component.length === 0) ? false : this.component;
14436         this.hasInput = this.component && this.inputEL().length;
14437         
14438         if (typeof(this.minViewMode === 'string')) {
14439             switch (this.minViewMode) {
14440                 case 'months':
14441                     this.minViewMode = 1;
14442                     break;
14443                 case 'years':
14444                     this.minViewMode = 2;
14445                     break;
14446                 default:
14447                     this.minViewMode = 0;
14448                     break;
14449             }
14450         }
14451         
14452         if (typeof(this.viewMode === 'string')) {
14453             switch (this.viewMode) {
14454                 case 'months':
14455                     this.viewMode = 1;
14456                     break;
14457                 case 'years':
14458                     this.viewMode = 2;
14459                     break;
14460                 default:
14461                     this.viewMode = 0;
14462                     break;
14463             }
14464         }
14465                 
14466         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14467         
14468 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14469         
14470         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14471         
14472         this.picker().on('mousedown', this.onMousedown, this);
14473         this.picker().on('click', this.onClick, this);
14474         
14475         this.picker().addClass('datepicker-dropdown');
14476         
14477         this.startViewMode = this.viewMode;
14478         
14479         
14480         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14481             if(!this.calendarWeeks){
14482                 v.remove();
14483                 return;
14484             };
14485             
14486             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14487             v.attr('colspan', function(i, val){
14488                 return parseInt(val) + 1;
14489             });
14490         })
14491                         
14492         
14493         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14494         
14495         this.setStartDate(this.startDate);
14496         this.setEndDate(this.endDate);
14497         
14498         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14499         
14500         this.fillDow();
14501         this.fillMonths();
14502         this.update();
14503         this.showMode();
14504         
14505         if(this.isInline) {
14506             this.show();
14507         }
14508     },
14509     
14510     picker : function()
14511     {
14512         return this.pickerEl;
14513 //        return this.el.select('.datepicker', true).first();
14514     },
14515     
14516     fillDow: function()
14517     {
14518         var dowCnt = this.weekStart;
14519         
14520         var dow = {
14521             tag: 'tr',
14522             cn: [
14523                 
14524             ]
14525         };
14526         
14527         if(this.calendarWeeks){
14528             dow.cn.push({
14529                 tag: 'th',
14530                 cls: 'cw',
14531                 html: '&nbsp;'
14532             })
14533         }
14534         
14535         while (dowCnt < this.weekStart + 7) {
14536             dow.cn.push({
14537                 tag: 'th',
14538                 cls: 'dow',
14539                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14540             });
14541         }
14542         
14543         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14544     },
14545     
14546     fillMonths: function()
14547     {    
14548         var i = 0
14549         var months = this.picker().select('>.datepicker-months td', true).first();
14550         
14551         months.dom.innerHTML = '';
14552         
14553         while (i < 12) {
14554             var month = {
14555                 tag: 'span',
14556                 cls: 'month',
14557                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14558             }
14559             
14560             months.createChild(month);
14561         }
14562         
14563     },
14564     
14565     update: function()
14566     {
14567         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;
14568         
14569         if (this.date < this.startDate) {
14570             this.viewDate = new Date(this.startDate);
14571         } else if (this.date > this.endDate) {
14572             this.viewDate = new Date(this.endDate);
14573         } else {
14574             this.viewDate = new Date(this.date);
14575         }
14576         
14577         this.fill();
14578     },
14579     
14580     fill: function() 
14581     {
14582         var d = new Date(this.viewDate),
14583                 year = d.getUTCFullYear(),
14584                 month = d.getUTCMonth(),
14585                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14586                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14587                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14588                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14589                 currentDate = this.date && this.date.valueOf(),
14590                 today = this.UTCToday();
14591         
14592         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14593         
14594 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14595         
14596 //        this.picker.select('>tfoot th.today').
14597 //                                              .text(dates[this.language].today)
14598 //                                              .toggle(this.todayBtn !== false);
14599     
14600         this.updateNavArrows();
14601         this.fillMonths();
14602                                                 
14603         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14604         
14605         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14606          
14607         prevMonth.setUTCDate(day);
14608         
14609         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14610         
14611         var nextMonth = new Date(prevMonth);
14612         
14613         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14614         
14615         nextMonth = nextMonth.valueOf();
14616         
14617         var fillMonths = false;
14618         
14619         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14620         
14621         while(prevMonth.valueOf() < nextMonth) {
14622             var clsName = '';
14623             
14624             if (prevMonth.getUTCDay() === this.weekStart) {
14625                 if(fillMonths){
14626                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14627                 }
14628                     
14629                 fillMonths = {
14630                     tag: 'tr',
14631                     cn: []
14632                 };
14633                 
14634                 if(this.calendarWeeks){
14635                     // ISO 8601: First week contains first thursday.
14636                     // ISO also states week starts on Monday, but we can be more abstract here.
14637                     var
14638                     // Start of current week: based on weekstart/current date
14639                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14640                     // Thursday of this week
14641                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14642                     // First Thursday of year, year from thursday
14643                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14644                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14645                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14646                     
14647                     fillMonths.cn.push({
14648                         tag: 'td',
14649                         cls: 'cw',
14650                         html: calWeek
14651                     });
14652                 }
14653             }
14654             
14655             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14656                 clsName += ' old';
14657             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14658                 clsName += ' new';
14659             }
14660             if (this.todayHighlight &&
14661                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14662                 prevMonth.getUTCMonth() == today.getMonth() &&
14663                 prevMonth.getUTCDate() == today.getDate()) {
14664                 clsName += ' today';
14665             }
14666             
14667             if (currentDate && prevMonth.valueOf() === currentDate) {
14668                 clsName += ' active';
14669             }
14670             
14671             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14672                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14673                     clsName += ' disabled';
14674             }
14675             
14676             fillMonths.cn.push({
14677                 tag: 'td',
14678                 cls: 'day ' + clsName,
14679                 html: prevMonth.getDate()
14680             })
14681             
14682             prevMonth.setDate(prevMonth.getDate()+1);
14683         }
14684           
14685         var currentYear = this.date && this.date.getUTCFullYear();
14686         var currentMonth = this.date && this.date.getUTCMonth();
14687         
14688         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14689         
14690         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14691             v.removeClass('active');
14692             
14693             if(currentYear === year && k === currentMonth){
14694                 v.addClass('active');
14695             }
14696             
14697             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14698                 v.addClass('disabled');
14699             }
14700             
14701         });
14702         
14703         
14704         year = parseInt(year/10, 10) * 10;
14705         
14706         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14707         
14708         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14709         
14710         year -= 1;
14711         for (var i = -1; i < 11; i++) {
14712             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14713                 tag: 'span',
14714                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14715                 html: year
14716             })
14717             
14718             year += 1;
14719         }
14720     },
14721     
14722     showMode: function(dir) 
14723     {
14724         if (dir) {
14725             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14726         }
14727         Roo.each(this.picker().select('>div',true).elements, function(v){
14728             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14729             v.hide();
14730         });
14731         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14732     },
14733     
14734     place: function()
14735     {
14736         if(this.isInline) return;
14737         
14738         this.picker().removeClass(['bottom', 'top']);
14739         
14740         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14741             /*
14742              * place to the top of element!
14743              *
14744              */
14745             
14746             this.picker().addClass('top');
14747             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14748             
14749             return;
14750         }
14751         
14752         this.picker().addClass('bottom');
14753         
14754         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14755     },
14756     
14757     parseDate : function(value)
14758     {
14759         if(!value || value instanceof Date){
14760             return value;
14761         }
14762         var v = Date.parseDate(value, this.format);
14763         if (!v && this.useIso) {
14764             v = Date.parseDate(value, 'Y-m-d');
14765         }
14766         if(!v && this.altFormats){
14767             if(!this.altFormatsArray){
14768                 this.altFormatsArray = this.altFormats.split("|");
14769             }
14770             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14771                 v = Date.parseDate(value, this.altFormatsArray[i]);
14772             }
14773         }
14774         return v;
14775     },
14776     
14777     formatDate : function(date, fmt)
14778     {
14779         return (!date || !(date instanceof Date)) ?
14780         date : date.dateFormat(fmt || this.format);
14781     },
14782     
14783     onFocus : function()
14784     {
14785         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14786         this.show();
14787     },
14788     
14789     onBlur : function()
14790     {
14791         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14792         
14793         var d = this.inputEl().getValue();
14794         
14795         this.setValue(d);
14796                 
14797         this.hide();
14798     },
14799     
14800     show : function()
14801     {
14802         this.picker().show();
14803         this.update();
14804         this.place();
14805         
14806         this.fireEvent('show', this, this.date);
14807     },
14808     
14809     hide : function()
14810     {
14811         if(this.isInline) return;
14812         this.picker().hide();
14813         this.viewMode = this.startViewMode;
14814         this.showMode();
14815         
14816         this.fireEvent('hide', this, this.date);
14817         
14818     },
14819     
14820     onMousedown: function(e)
14821     {
14822         e.stopPropagation();
14823         e.preventDefault();
14824     },
14825     
14826     keyup: function(e)
14827     {
14828         Roo.bootstrap.DateField.superclass.keyup.call(this);
14829         this.update();
14830     },
14831
14832     setValue: function(v)
14833     {
14834         var d = new Date(v).clearTime();
14835         
14836         if(isNaN(d.getTime())){
14837             this.date = this.viewDate = '';
14838             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14839             return;
14840         }
14841         
14842         v = this.formatDate(d);
14843         
14844         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14845         
14846         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14847      
14848         this.update();
14849
14850         this.fireEvent('select', this, this.date);
14851         
14852     },
14853     
14854     getValue: function()
14855     {
14856         return this.formatDate(this.date);
14857     },
14858     
14859     fireKey: function(e)
14860     {
14861         if (!this.picker().isVisible()){
14862             if (e.keyCode == 27) // allow escape to hide and re-show picker
14863                 this.show();
14864             return;
14865         }
14866         
14867         var dateChanged = false,
14868         dir, day, month,
14869         newDate, newViewDate;
14870         
14871         switch(e.keyCode){
14872             case 27: // escape
14873                 this.hide();
14874                 e.preventDefault();
14875                 break;
14876             case 37: // left
14877             case 39: // right
14878                 if (!this.keyboardNavigation) break;
14879                 dir = e.keyCode == 37 ? -1 : 1;
14880                 
14881                 if (e.ctrlKey){
14882                     newDate = this.moveYear(this.date, dir);
14883                     newViewDate = this.moveYear(this.viewDate, dir);
14884                 } else if (e.shiftKey){
14885                     newDate = this.moveMonth(this.date, dir);
14886                     newViewDate = this.moveMonth(this.viewDate, dir);
14887                 } else {
14888                     newDate = new Date(this.date);
14889                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14890                     newViewDate = new Date(this.viewDate);
14891                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14892                 }
14893                 if (this.dateWithinRange(newDate)){
14894                     this.date = newDate;
14895                     this.viewDate = newViewDate;
14896                     this.setValue(this.formatDate(this.date));
14897 //                    this.update();
14898                     e.preventDefault();
14899                     dateChanged = true;
14900                 }
14901                 break;
14902             case 38: // up
14903             case 40: // down
14904                 if (!this.keyboardNavigation) break;
14905                 dir = e.keyCode == 38 ? -1 : 1;
14906                 if (e.ctrlKey){
14907                     newDate = this.moveYear(this.date, dir);
14908                     newViewDate = this.moveYear(this.viewDate, dir);
14909                 } else if (e.shiftKey){
14910                     newDate = this.moveMonth(this.date, dir);
14911                     newViewDate = this.moveMonth(this.viewDate, dir);
14912                 } else {
14913                     newDate = new Date(this.date);
14914                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14915                     newViewDate = new Date(this.viewDate);
14916                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14917                 }
14918                 if (this.dateWithinRange(newDate)){
14919                     this.date = newDate;
14920                     this.viewDate = newViewDate;
14921                     this.setValue(this.formatDate(this.date));
14922 //                    this.update();
14923                     e.preventDefault();
14924                     dateChanged = true;
14925                 }
14926                 break;
14927             case 13: // enter
14928                 this.setValue(this.formatDate(this.date));
14929                 this.hide();
14930                 e.preventDefault();
14931                 break;
14932             case 9: // tab
14933                 this.setValue(this.formatDate(this.date));
14934                 this.hide();
14935                 break;
14936             case 16: // shift
14937             case 17: // ctrl
14938             case 18: // alt
14939                 break;
14940             default :
14941                 this.hide();
14942                 
14943         }
14944     },
14945     
14946     
14947     onClick: function(e) 
14948     {
14949         e.stopPropagation();
14950         e.preventDefault();
14951         
14952         var target = e.getTarget();
14953         
14954         if(target.nodeName.toLowerCase() === 'i'){
14955             target = Roo.get(target).dom.parentNode;
14956         }
14957         
14958         var nodeName = target.nodeName;
14959         var className = target.className;
14960         var html = target.innerHTML;
14961         
14962         switch(nodeName.toLowerCase()) {
14963             case 'th':
14964                 switch(className) {
14965                     case 'switch':
14966                         this.showMode(1);
14967                         break;
14968                     case 'prev':
14969                     case 'next':
14970                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14971                         switch(this.viewMode){
14972                                 case 0:
14973                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14974                                         break;
14975                                 case 1:
14976                                 case 2:
14977                                         this.viewDate = this.moveYear(this.viewDate, dir);
14978                                         break;
14979                         }
14980                         this.fill();
14981                         break;
14982                     case 'today':
14983                         var date = new Date();
14984                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14985 //                        this.fill()
14986                         this.setValue(this.formatDate(this.date));
14987                         
14988                         this.hide();
14989                         break;
14990                 }
14991                 break;
14992             case 'span':
14993                 if (className.indexOf('disabled') === -1) {
14994                     this.viewDate.setUTCDate(1);
14995                     if (className.indexOf('month') !== -1) {
14996                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14997                     } else {
14998                         var year = parseInt(html, 10) || 0;
14999                         this.viewDate.setUTCFullYear(year);
15000                         
15001                     }
15002                     this.showMode(-1);
15003                     this.fill();
15004                 }
15005                 break;
15006                 
15007             case 'td':
15008                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15009                     var day = parseInt(html, 10) || 1;
15010                     var year = this.viewDate.getUTCFullYear(),
15011                         month = this.viewDate.getUTCMonth();
15012
15013                     if (className.indexOf('old') !== -1) {
15014                         if(month === 0 ){
15015                             month = 11;
15016                             year -= 1;
15017                         }else{
15018                             month -= 1;
15019                         }
15020                     } else if (className.indexOf('new') !== -1) {
15021                         if (month == 11) {
15022                             month = 0;
15023                             year += 1;
15024                         } else {
15025                             month += 1;
15026                         }
15027                     }
15028                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15029                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15030 //                    this.fill();
15031                     this.setValue(this.formatDate(this.date));
15032                     this.hide();
15033                 }
15034                 break;
15035         }
15036     },
15037     
15038     setStartDate: function(startDate)
15039     {
15040         this.startDate = startDate || -Infinity;
15041         if (this.startDate !== -Infinity) {
15042             this.startDate = this.parseDate(this.startDate);
15043         }
15044         this.update();
15045         this.updateNavArrows();
15046     },
15047
15048     setEndDate: function(endDate)
15049     {
15050         this.endDate = endDate || Infinity;
15051         if (this.endDate !== Infinity) {
15052             this.endDate = this.parseDate(this.endDate);
15053         }
15054         this.update();
15055         this.updateNavArrows();
15056     },
15057     
15058     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15059     {
15060         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15061         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15062             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15063         }
15064         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15065             return parseInt(d, 10);
15066         });
15067         this.update();
15068         this.updateNavArrows();
15069     },
15070     
15071     updateNavArrows: function() 
15072     {
15073         var d = new Date(this.viewDate),
15074         year = d.getUTCFullYear(),
15075         month = d.getUTCMonth();
15076         
15077         Roo.each(this.picker().select('.prev', true).elements, function(v){
15078             v.show();
15079             switch (this.viewMode) {
15080                 case 0:
15081
15082                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15083                         v.hide();
15084                     }
15085                     break;
15086                 case 1:
15087                 case 2:
15088                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15089                         v.hide();
15090                     }
15091                     break;
15092             }
15093         });
15094         
15095         Roo.each(this.picker().select('.next', true).elements, function(v){
15096             v.show();
15097             switch (this.viewMode) {
15098                 case 0:
15099
15100                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15101                         v.hide();
15102                     }
15103                     break;
15104                 case 1:
15105                 case 2:
15106                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15107                         v.hide();
15108                     }
15109                     break;
15110             }
15111         })
15112     },
15113     
15114     moveMonth: function(date, dir)
15115     {
15116         if (!dir) return date;
15117         var new_date = new Date(date.valueOf()),
15118         day = new_date.getUTCDate(),
15119         month = new_date.getUTCMonth(),
15120         mag = Math.abs(dir),
15121         new_month, test;
15122         dir = dir > 0 ? 1 : -1;
15123         if (mag == 1){
15124             test = dir == -1
15125             // If going back one month, make sure month is not current month
15126             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15127             ? function(){
15128                 return new_date.getUTCMonth() == month;
15129             }
15130             // If going forward one month, make sure month is as expected
15131             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15132             : function(){
15133                 return new_date.getUTCMonth() != new_month;
15134             };
15135             new_month = month + dir;
15136             new_date.setUTCMonth(new_month);
15137             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15138             if (new_month < 0 || new_month > 11)
15139                 new_month = (new_month + 12) % 12;
15140         } else {
15141             // For magnitudes >1, move one month at a time...
15142             for (var i=0; i<mag; i++)
15143                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15144                 new_date = this.moveMonth(new_date, dir);
15145             // ...then reset the day, keeping it in the new month
15146             new_month = new_date.getUTCMonth();
15147             new_date.setUTCDate(day);
15148             test = function(){
15149                 return new_month != new_date.getUTCMonth();
15150             };
15151         }
15152         // Common date-resetting loop -- if date is beyond end of month, make it
15153         // end of month
15154         while (test()){
15155             new_date.setUTCDate(--day);
15156             new_date.setUTCMonth(new_month);
15157         }
15158         return new_date;
15159     },
15160
15161     moveYear: function(date, dir)
15162     {
15163         return this.moveMonth(date, dir*12);
15164     },
15165
15166     dateWithinRange: function(date)
15167     {
15168         return date >= this.startDate && date <= this.endDate;
15169     },
15170
15171     
15172     remove: function() 
15173     {
15174         this.picker().remove();
15175     }
15176    
15177 });
15178
15179 Roo.apply(Roo.bootstrap.DateField,  {
15180     
15181     head : {
15182         tag: 'thead',
15183         cn: [
15184         {
15185             tag: 'tr',
15186             cn: [
15187             {
15188                 tag: 'th',
15189                 cls: 'prev',
15190                 html: '<i class="fa fa-arrow-left"/>'
15191             },
15192             {
15193                 tag: 'th',
15194                 cls: 'switch',
15195                 colspan: '5'
15196             },
15197             {
15198                 tag: 'th',
15199                 cls: 'next',
15200                 html: '<i class="fa fa-arrow-right"/>'
15201             }
15202
15203             ]
15204         }
15205         ]
15206     },
15207     
15208     content : {
15209         tag: 'tbody',
15210         cn: [
15211         {
15212             tag: 'tr',
15213             cn: [
15214             {
15215                 tag: 'td',
15216                 colspan: '7'
15217             }
15218             ]
15219         }
15220         ]
15221     },
15222     
15223     footer : {
15224         tag: 'tfoot',
15225         cn: [
15226         {
15227             tag: 'tr',
15228             cn: [
15229             {
15230                 tag: 'th',
15231                 colspan: '7',
15232                 cls: 'today'
15233             }
15234                     
15235             ]
15236         }
15237         ]
15238     },
15239     
15240     dates:{
15241         en: {
15242             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15243             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15244             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15245             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15246             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15247             today: "Today"
15248         }
15249     },
15250     
15251     modes: [
15252     {
15253         clsName: 'days',
15254         navFnc: 'Month',
15255         navStep: 1
15256     },
15257     {
15258         clsName: 'months',
15259         navFnc: 'FullYear',
15260         navStep: 1
15261     },
15262     {
15263         clsName: 'years',
15264         navFnc: 'FullYear',
15265         navStep: 10
15266     }]
15267 });
15268
15269 Roo.apply(Roo.bootstrap.DateField,  {
15270   
15271     template : {
15272         tag: 'div',
15273         cls: 'datepicker dropdown-menu',
15274         cn: [
15275         {
15276             tag: 'div',
15277             cls: 'datepicker-days',
15278             cn: [
15279             {
15280                 tag: 'table',
15281                 cls: 'table-condensed',
15282                 cn:[
15283                 Roo.bootstrap.DateField.head,
15284                 {
15285                     tag: 'tbody'
15286                 },
15287                 Roo.bootstrap.DateField.footer
15288                 ]
15289             }
15290             ]
15291         },
15292         {
15293             tag: 'div',
15294             cls: 'datepicker-months',
15295             cn: [
15296             {
15297                 tag: 'table',
15298                 cls: 'table-condensed',
15299                 cn:[
15300                 Roo.bootstrap.DateField.head,
15301                 Roo.bootstrap.DateField.content,
15302                 Roo.bootstrap.DateField.footer
15303                 ]
15304             }
15305             ]
15306         },
15307         {
15308             tag: 'div',
15309             cls: 'datepicker-years',
15310             cn: [
15311             {
15312                 tag: 'table',
15313                 cls: 'table-condensed',
15314                 cn:[
15315                 Roo.bootstrap.DateField.head,
15316                 Roo.bootstrap.DateField.content,
15317                 Roo.bootstrap.DateField.footer
15318                 ]
15319             }
15320             ]
15321         }
15322         ]
15323     }
15324 });
15325
15326  
15327
15328  /*
15329  * - LGPL
15330  *
15331  * TimeField
15332  * 
15333  */
15334
15335 /**
15336  * @class Roo.bootstrap.TimeField
15337  * @extends Roo.bootstrap.Input
15338  * Bootstrap DateField class
15339  * 
15340  * 
15341  * @constructor
15342  * Create a new TimeField
15343  * @param {Object} config The config object
15344  */
15345
15346 Roo.bootstrap.TimeField = function(config){
15347     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15348     this.addEvents({
15349             /**
15350              * @event show
15351              * Fires when this field show.
15352              * @param {Roo.bootstrap.DateField} this
15353              * @param {Mixed} date The date value
15354              */
15355             show : true,
15356             /**
15357              * @event show
15358              * Fires when this field hide.
15359              * @param {Roo.bootstrap.DateField} this
15360              * @param {Mixed} date The date value
15361              */
15362             hide : true,
15363             /**
15364              * @event select
15365              * Fires when select a date.
15366              * @param {Roo.bootstrap.DateField} this
15367              * @param {Mixed} date The date value
15368              */
15369             select : true
15370         });
15371 };
15372
15373 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15374     
15375     /**
15376      * @cfg {String} format
15377      * The default time format string which can be overriden for localization support.  The format must be
15378      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15379      */
15380     format : "H:i",
15381        
15382     onRender: function(ct, position)
15383     {
15384         
15385         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15386                 
15387         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15388         
15389         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15390         
15391         this.pop = this.picker().select('>.datepicker-time',true).first();
15392         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15393         
15394         this.picker().on('mousedown', this.onMousedown, this);
15395         this.picker().on('click', this.onClick, this);
15396         
15397         this.picker().addClass('datepicker-dropdown');
15398     
15399         this.fillTime();
15400         this.update();
15401             
15402         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15403         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15404         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15405         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15406         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15407         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15408
15409     },
15410     
15411     fireKey: function(e){
15412         if (!this.picker().isVisible()){
15413             if (e.keyCode == 27) // allow escape to hide and re-show picker
15414                 this.show();
15415             return;
15416         }
15417
15418         e.preventDefault();
15419         
15420         switch(e.keyCode){
15421             case 27: // escape
15422                 this.hide();
15423                 break;
15424             case 37: // left
15425             case 39: // right
15426                 this.onTogglePeriod();
15427                 break;
15428             case 38: // up
15429                 this.onIncrementMinutes();
15430                 break;
15431             case 40: // down
15432                 this.onDecrementMinutes();
15433                 break;
15434             case 13: // enter
15435             case 9: // tab
15436                 this.setTime();
15437                 break;
15438         }
15439     },
15440     
15441     onClick: function(e) {
15442         e.stopPropagation();
15443         e.preventDefault();
15444     },
15445     
15446     picker : function()
15447     {
15448         return this.el.select('.datepicker', true).first();
15449     },
15450     
15451     fillTime: function()
15452     {    
15453         var time = this.pop.select('tbody', true).first();
15454         
15455         time.dom.innerHTML = '';
15456         
15457         time.createChild({
15458             tag: 'tr',
15459             cn: [
15460                 {
15461                     tag: 'td',
15462                     cn: [
15463                         {
15464                             tag: 'a',
15465                             href: '#',
15466                             cls: 'btn',
15467                             cn: [
15468                                 {
15469                                     tag: 'span',
15470                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15471                                 }
15472                             ]
15473                         } 
15474                     ]
15475                 },
15476                 {
15477                     tag: 'td',
15478                     cls: 'separator'
15479                 },
15480                 {
15481                     tag: 'td',
15482                     cn: [
15483                         {
15484                             tag: 'a',
15485                             href: '#',
15486                             cls: 'btn',
15487                             cn: [
15488                                 {
15489                                     tag: 'span',
15490                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15491                                 }
15492                             ]
15493                         }
15494                     ]
15495                 },
15496                 {
15497                     tag: 'td',
15498                     cls: 'separator'
15499                 }
15500             ]
15501         });
15502         
15503         time.createChild({
15504             tag: 'tr',
15505             cn: [
15506                 {
15507                     tag: 'td',
15508                     cn: [
15509                         {
15510                             tag: 'span',
15511                             cls: 'timepicker-hour',
15512                             html: '00'
15513                         }  
15514                     ]
15515                 },
15516                 {
15517                     tag: 'td',
15518                     cls: 'separator',
15519                     html: ':'
15520                 },
15521                 {
15522                     tag: 'td',
15523                     cn: [
15524                         {
15525                             tag: 'span',
15526                             cls: 'timepicker-minute',
15527                             html: '00'
15528                         }  
15529                     ]
15530                 },
15531                 {
15532                     tag: 'td',
15533                     cls: 'separator'
15534                 },
15535                 {
15536                     tag: 'td',
15537                     cn: [
15538                         {
15539                             tag: 'button',
15540                             type: 'button',
15541                             cls: 'btn btn-primary period',
15542                             html: 'AM'
15543                             
15544                         }
15545                     ]
15546                 }
15547             ]
15548         });
15549         
15550         time.createChild({
15551             tag: 'tr',
15552             cn: [
15553                 {
15554                     tag: 'td',
15555                     cn: [
15556                         {
15557                             tag: 'a',
15558                             href: '#',
15559                             cls: 'btn',
15560                             cn: [
15561                                 {
15562                                     tag: 'span',
15563                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15564                                 }
15565                             ]
15566                         }
15567                     ]
15568                 },
15569                 {
15570                     tag: 'td',
15571                     cls: 'separator'
15572                 },
15573                 {
15574                     tag: 'td',
15575                     cn: [
15576                         {
15577                             tag: 'a',
15578                             href: '#',
15579                             cls: 'btn',
15580                             cn: [
15581                                 {
15582                                     tag: 'span',
15583                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15584                                 }
15585                             ]
15586                         }
15587                     ]
15588                 },
15589                 {
15590                     tag: 'td',
15591                     cls: 'separator'
15592                 }
15593             ]
15594         });
15595         
15596     },
15597     
15598     update: function()
15599     {
15600         
15601         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15602         
15603         this.fill();
15604     },
15605     
15606     fill: function() 
15607     {
15608         var hours = this.time.getHours();
15609         var minutes = this.time.getMinutes();
15610         var period = 'AM';
15611         
15612         if(hours > 11){
15613             period = 'PM';
15614         }
15615         
15616         if(hours == 0){
15617             hours = 12;
15618         }
15619         
15620         
15621         if(hours > 12){
15622             hours = hours - 12;
15623         }
15624         
15625         if(hours < 10){
15626             hours = '0' + hours;
15627         }
15628         
15629         if(minutes < 10){
15630             minutes = '0' + minutes;
15631         }
15632         
15633         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15634         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15635         this.pop.select('button', true).first().dom.innerHTML = period;
15636         
15637     },
15638     
15639     place: function()
15640     {   
15641         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15642         
15643         var cls = ['bottom'];
15644         
15645         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15646             cls.pop();
15647             cls.push('top');
15648         }
15649         
15650         cls.push('right');
15651         
15652         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15653             cls.pop();
15654             cls.push('left');
15655         }
15656         
15657         this.picker().addClass(cls.join('-'));
15658         
15659         var _this = this;
15660         
15661         Roo.each(cls, function(c){
15662             if(c == 'bottom'){
15663                 _this.picker().setTop(_this.inputEl().getHeight());
15664                 return;
15665             }
15666             if(c == 'top'){
15667                 _this.picker().setTop(0 - _this.picker().getHeight());
15668                 return;
15669             }
15670             
15671             if(c == 'left'){
15672                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15673                 return;
15674             }
15675             if(c == 'right'){
15676                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15677                 return;
15678             }
15679         });
15680         
15681     },
15682   
15683     onFocus : function()
15684     {
15685         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15686         this.show();
15687     },
15688     
15689     onBlur : function()
15690     {
15691         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15692         this.hide();
15693     },
15694     
15695     show : function()
15696     {
15697         this.picker().show();
15698         this.pop.show();
15699         this.update();
15700         this.place();
15701         
15702         this.fireEvent('show', this, this.date);
15703     },
15704     
15705     hide : function()
15706     {
15707         this.picker().hide();
15708         this.pop.hide();
15709         
15710         this.fireEvent('hide', this, this.date);
15711     },
15712     
15713     setTime : function()
15714     {
15715         this.hide();
15716         this.setValue(this.time.format(this.format));
15717         
15718         this.fireEvent('select', this, this.date);
15719         
15720         
15721     },
15722     
15723     onMousedown: function(e){
15724         e.stopPropagation();
15725         e.preventDefault();
15726     },
15727     
15728     onIncrementHours: function()
15729     {
15730         Roo.log('onIncrementHours');
15731         this.time = this.time.add(Date.HOUR, 1);
15732         this.update();
15733         
15734     },
15735     
15736     onDecrementHours: function()
15737     {
15738         Roo.log('onDecrementHours');
15739         this.time = this.time.add(Date.HOUR, -1);
15740         this.update();
15741     },
15742     
15743     onIncrementMinutes: function()
15744     {
15745         Roo.log('onIncrementMinutes');
15746         this.time = this.time.add(Date.MINUTE, 1);
15747         this.update();
15748     },
15749     
15750     onDecrementMinutes: function()
15751     {
15752         Roo.log('onDecrementMinutes');
15753         this.time = this.time.add(Date.MINUTE, -1);
15754         this.update();
15755     },
15756     
15757     onTogglePeriod: function()
15758     {
15759         Roo.log('onTogglePeriod');
15760         this.time = this.time.add(Date.HOUR, 12);
15761         this.update();
15762     }
15763     
15764    
15765 });
15766
15767 Roo.apply(Roo.bootstrap.TimeField,  {
15768     
15769     content : {
15770         tag: 'tbody',
15771         cn: [
15772             {
15773                 tag: 'tr',
15774                 cn: [
15775                 {
15776                     tag: 'td',
15777                     colspan: '7'
15778                 }
15779                 ]
15780             }
15781         ]
15782     },
15783     
15784     footer : {
15785         tag: 'tfoot',
15786         cn: [
15787             {
15788                 tag: 'tr',
15789                 cn: [
15790                 {
15791                     tag: 'th',
15792                     colspan: '7',
15793                     cls: '',
15794                     cn: [
15795                         {
15796                             tag: 'button',
15797                             cls: 'btn btn-info ok',
15798                             html: 'OK'
15799                         }
15800                     ]
15801                 }
15802
15803                 ]
15804             }
15805         ]
15806     }
15807 });
15808
15809 Roo.apply(Roo.bootstrap.TimeField,  {
15810   
15811     template : {
15812         tag: 'div',
15813         cls: 'datepicker dropdown-menu',
15814         cn: [
15815             {
15816                 tag: 'div',
15817                 cls: 'datepicker-time',
15818                 cn: [
15819                 {
15820                     tag: 'table',
15821                     cls: 'table-condensed',
15822                     cn:[
15823                     Roo.bootstrap.TimeField.content,
15824                     Roo.bootstrap.TimeField.footer
15825                     ]
15826                 }
15827                 ]
15828             }
15829         ]
15830     }
15831 });
15832
15833  
15834
15835  /*
15836  * - LGPL
15837  *
15838  * CheckBox
15839  * 
15840  */
15841
15842 /**
15843  * @class Roo.bootstrap.CheckBox
15844  * @extends Roo.bootstrap.Input
15845  * Bootstrap CheckBox class
15846  * 
15847  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15848  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15849  * @cfg {String} boxLabel The text that appears beside the checkbox
15850  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15851  * @cfg {Boolean} checked initnal the element
15852  * 
15853  * 
15854  * @constructor
15855  * Create a new CheckBox
15856  * @param {Object} config The config object
15857  */
15858
15859 Roo.bootstrap.CheckBox = function(config){
15860     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15861    
15862         this.addEvents({
15863             /**
15864             * @event check
15865             * Fires when the element is checked or unchecked.
15866             * @param {Roo.bootstrap.CheckBox} this This input
15867             * @param {Boolean} checked The new checked value
15868             */
15869            check : true
15870         });
15871 };
15872
15873 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15874     
15875     inputType: 'checkbox',
15876     inputValue: 1,
15877     valueOff: 0,
15878     boxLabel: false,
15879     checked: false,
15880     weight : false,
15881     
15882     getAutoCreate : function()
15883     {
15884         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15885         
15886         var id = Roo.id();
15887         
15888         var cfg = {};
15889         
15890         cfg.cls = 'form-group checkbox' //input-group
15891         
15892         
15893         
15894         
15895         var input =  {
15896             tag: 'input',
15897             id : id,
15898             type : this.inputType,
15899             value : (!this.checked) ? this.valueOff : this.inputValue,
15900             cls : 'roo-checkbox', //'form-box',
15901             placeholder : this.placeholder || ''
15902             
15903         };
15904         
15905         if (this.weight) { // Validity check?
15906             cfg.cls += " checkbox-" + this.weight;
15907         }
15908         
15909         if (this.disabled) {
15910             input.disabled=true;
15911         }
15912         
15913         if(this.checked){
15914             input.checked = this.checked;
15915         }
15916         
15917         if (this.name) {
15918             input.name = this.name;
15919         }
15920         
15921         if (this.size) {
15922             input.cls += ' input-' + this.size;
15923         }
15924         
15925         var settings=this;
15926         ['xs','sm','md','lg'].map(function(size){
15927             if (settings[size]) {
15928                 cfg.cls += ' col-' + size + '-' + settings[size];
15929             }
15930         });
15931         
15932        
15933         
15934         var inputblock = input;
15935         
15936         
15937         
15938         
15939         if (this.before || this.after) {
15940             
15941             inputblock = {
15942                 cls : 'input-group',
15943                 cn :  [] 
15944             };
15945             if (this.before) {
15946                 inputblock.cn.push({
15947                     tag :'span',
15948                     cls : 'input-group-addon',
15949                     html : this.before
15950                 });
15951             }
15952             inputblock.cn.push(input);
15953             if (this.after) {
15954                 inputblock.cn.push({
15955                     tag :'span',
15956                     cls : 'input-group-addon',
15957                     html : this.after
15958                 });
15959             }
15960             
15961         };
15962         
15963         if (align ==='left' && this.fieldLabel.length) {
15964                 Roo.log("left and has label");
15965                 cfg.cn = [
15966                     
15967                     {
15968                         tag: 'label',
15969                         'for' :  id,
15970                         cls : 'control-label col-md-' + this.labelWidth,
15971                         html : this.fieldLabel
15972                         
15973                     },
15974                     {
15975                         cls : "col-md-" + (12 - this.labelWidth), 
15976                         cn: [
15977                             inputblock
15978                         ]
15979                     }
15980                     
15981                 ];
15982         } else if ( this.fieldLabel.length) {
15983                 Roo.log(" label");
15984                 cfg.cn = [
15985                    
15986                     {
15987                         tag: this.boxLabel ? 'span' : 'label',
15988                         'for': id,
15989                         cls: 'control-label box-input-label',
15990                         //cls : 'input-group-addon',
15991                         html : this.fieldLabel
15992                         
15993                     },
15994                     
15995                     inputblock
15996                     
15997                 ];
15998
15999         } else {
16000             
16001                 Roo.log(" no label && no align");
16002                 cfg.cn = [  inputblock ] ;
16003                 
16004                 
16005         };
16006          if(this.boxLabel){
16007             cfg.cn.push( {
16008                 tag: 'label',
16009                 'for': id,
16010                 cls: 'box-label',
16011                 html: this.boxLabel
16012                 
16013             });
16014         }
16015         
16016         
16017        
16018         return cfg;
16019         
16020     },
16021     
16022     /**
16023      * return the real input element.
16024      */
16025     inputEl: function ()
16026     {
16027         return this.el.select('input.roo-checkbox',true).first();
16028     },
16029     
16030     label: function()
16031     {
16032         return this.el.select('label.control-label',true).first();
16033     },
16034     
16035     initEvents : function()
16036     {
16037 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16038         
16039         this.inputEl().on('click', this.onClick,  this);
16040         
16041     },
16042     
16043     onClick : function()
16044     {   
16045         this.setChecked(!this.checked);
16046     },
16047     
16048     setChecked : function(state,suppressEvent)
16049     {
16050         this.checked = state;
16051         
16052         this.inputEl().dom.checked = state;
16053         
16054         if(suppressEvent !== true){
16055             this.fireEvent('check', this, state);
16056         }
16057         
16058         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16059         
16060     },
16061     
16062     setValue : function(v,suppressEvent)
16063     {
16064         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16065     }
16066     
16067 });
16068
16069  
16070 /*
16071  * - LGPL
16072  *
16073  * Radio
16074  * 
16075  */
16076
16077 /**
16078  * @class Roo.bootstrap.Radio
16079  * @extends Roo.bootstrap.CheckBox
16080  * Bootstrap Radio class
16081
16082  * @constructor
16083  * Create a new Radio
16084  * @param {Object} config The config object
16085  */
16086
16087 Roo.bootstrap.Radio = function(config){
16088     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16089    
16090 };
16091
16092 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16093     
16094     inputType: 'radio',
16095     inputValue: '',
16096     valueOff: '',
16097     
16098     getAutoCreate : function()
16099     {
16100         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16101         
16102         var id = Roo.id();
16103         
16104         var cfg = {};
16105         
16106         cfg.cls = 'form-group radio' //input-group
16107         
16108         var input =  {
16109             tag: 'input',
16110             id : id,
16111             type : this.inputType,
16112             value : (!this.checked) ? this.valueOff : this.inputValue,
16113             cls : 'roo-radio',
16114             placeholder : this.placeholder || ''
16115             
16116         };
16117           if (this.weight) { // Validity check?
16118             cfg.cls += " radio-" + this.weight;
16119         }
16120         if (this.disabled) {
16121             input.disabled=true;
16122         }
16123         
16124         if(this.checked){
16125             input.checked = this.checked;
16126         }
16127         
16128         if (this.name) {
16129             input.name = this.name;
16130         }
16131         
16132         if (this.size) {
16133             input.cls += ' input-' + this.size;
16134         }
16135         
16136         var settings=this;
16137         ['xs','sm','md','lg'].map(function(size){
16138             if (settings[size]) {
16139                 cfg.cls += ' col-' + size + '-' + settings[size];
16140             }
16141         });
16142         
16143         var inputblock = input;
16144         
16145         if (this.before || this.after) {
16146             
16147             inputblock = {
16148                 cls : 'input-group',
16149                 cn :  [] 
16150             };
16151             if (this.before) {
16152                 inputblock.cn.push({
16153                     tag :'span',
16154                     cls : 'input-group-addon',
16155                     html : this.before
16156                 });
16157             }
16158             inputblock.cn.push(input);
16159             if (this.after) {
16160                 inputblock.cn.push({
16161                     tag :'span',
16162                     cls : 'input-group-addon',
16163                     html : this.after
16164                 });
16165             }
16166             
16167         };
16168         
16169         if (align ==='left' && this.fieldLabel.length) {
16170                 Roo.log("left and has label");
16171                 cfg.cn = [
16172                     
16173                     {
16174                         tag: 'label',
16175                         'for' :  id,
16176                         cls : 'control-label col-md-' + this.labelWidth,
16177                         html : this.fieldLabel
16178                         
16179                     },
16180                     {
16181                         cls : "col-md-" + (12 - this.labelWidth), 
16182                         cn: [
16183                             inputblock
16184                         ]
16185                     }
16186                     
16187                 ];
16188         } else if ( this.fieldLabel.length) {
16189                 Roo.log(" label");
16190                  cfg.cn = [
16191                    
16192                     {
16193                         tag: 'label',
16194                         'for': id,
16195                         cls: 'control-label box-input-label',
16196                         //cls : 'input-group-addon',
16197                         html : this.fieldLabel
16198                         
16199                     },
16200                     
16201                     inputblock
16202                     
16203                 ];
16204
16205         } else {
16206             
16207                    Roo.log(" no label && no align");
16208                 cfg.cn = [
16209                     
16210                         inputblock
16211                     
16212                 ];
16213                 
16214                 
16215         };
16216         
16217         if(this.boxLabel){
16218             cfg.cn.push({
16219                 tag: 'label',
16220                 'for': id,
16221                 cls: 'box-label',
16222                 html: this.boxLabel
16223             })
16224         }
16225         
16226         return cfg;
16227         
16228     },
16229     inputEl: function ()
16230     {
16231         return this.el.select('input.roo-radio',true).first();
16232     },
16233     onClick : function()
16234     {   
16235         this.setChecked(true);
16236     },
16237     
16238     setChecked : function(state,suppressEvent)
16239     {
16240         if(state){
16241             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16242                 v.dom.checked = false;
16243             });
16244         }
16245         
16246         this.checked = state;
16247         this.inputEl().dom.checked = state;
16248         
16249         if(suppressEvent !== true){
16250             this.fireEvent('check', this, state);
16251         }
16252         
16253         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16254         
16255     },
16256     
16257     getGroupValue : function()
16258     {
16259         var value = ''
16260         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16261             if(v.dom.checked == true){
16262                 value = v.dom.value;
16263             }
16264         });
16265         
16266         return value;
16267     },
16268     
16269     /**
16270      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16271      * @return {Mixed} value The field value
16272      */
16273     getValue : function(){
16274         return this.getGroupValue();
16275     }
16276     
16277 });
16278
16279  
16280 //<script type="text/javascript">
16281
16282 /*
16283  * Based  Ext JS Library 1.1.1
16284  * Copyright(c) 2006-2007, Ext JS, LLC.
16285  * LGPL
16286  *
16287  */
16288  
16289 /**
16290  * @class Roo.HtmlEditorCore
16291  * @extends Roo.Component
16292  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16293  *
16294  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16295  */
16296
16297 Roo.HtmlEditorCore = function(config){
16298     
16299     
16300     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16301     
16302     
16303     this.addEvents({
16304         /**
16305          * @event initialize
16306          * Fires when the editor is fully initialized (including the iframe)
16307          * @param {Roo.HtmlEditorCore} this
16308          */
16309         initialize: true,
16310         /**
16311          * @event activate
16312          * Fires when the editor is first receives the focus. Any insertion must wait
16313          * until after this event.
16314          * @param {Roo.HtmlEditorCore} this
16315          */
16316         activate: true,
16317          /**
16318          * @event beforesync
16319          * Fires before the textarea is updated with content from the editor iframe. Return false
16320          * to cancel the sync.
16321          * @param {Roo.HtmlEditorCore} this
16322          * @param {String} html
16323          */
16324         beforesync: true,
16325          /**
16326          * @event beforepush
16327          * Fires before the iframe editor is updated with content from the textarea. Return false
16328          * to cancel the push.
16329          * @param {Roo.HtmlEditorCore} this
16330          * @param {String} html
16331          */
16332         beforepush: true,
16333          /**
16334          * @event sync
16335          * Fires when the textarea is updated with content from the editor iframe.
16336          * @param {Roo.HtmlEditorCore} this
16337          * @param {String} html
16338          */
16339         sync: true,
16340          /**
16341          * @event push
16342          * Fires when the iframe editor is updated with content from the textarea.
16343          * @param {Roo.HtmlEditorCore} this
16344          * @param {String} html
16345          */
16346         push: true,
16347         
16348         /**
16349          * @event editorevent
16350          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16351          * @param {Roo.HtmlEditorCore} this
16352          */
16353         editorevent: true
16354     });
16355     
16356     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16357     
16358     // defaults : white / black...
16359     this.applyBlacklists();
16360     
16361     
16362     
16363 };
16364
16365
16366 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16367
16368
16369      /**
16370      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16371      */
16372     
16373     owner : false,
16374     
16375      /**
16376      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16377      *                        Roo.resizable.
16378      */
16379     resizable : false,
16380      /**
16381      * @cfg {Number} height (in pixels)
16382      */   
16383     height: 300,
16384    /**
16385      * @cfg {Number} width (in pixels)
16386      */   
16387     width: 500,
16388     
16389     /**
16390      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16391      * 
16392      */
16393     stylesheets: false,
16394     
16395     // id of frame..
16396     frameId: false,
16397     
16398     // private properties
16399     validationEvent : false,
16400     deferHeight: true,
16401     initialized : false,
16402     activated : false,
16403     sourceEditMode : false,
16404     onFocus : Roo.emptyFn,
16405     iframePad:3,
16406     hideMode:'offsets',
16407     
16408     clearUp: true,
16409     
16410     // blacklist + whitelisted elements..
16411     black: false,
16412     white: false,
16413      
16414     
16415
16416     /**
16417      * Protected method that will not generally be called directly. It
16418      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16419      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16420      */
16421     getDocMarkup : function(){
16422         // body styles..
16423         var st = '';
16424         Roo.log(this.stylesheets);
16425         
16426         // inherit styels from page...?? 
16427         if (this.stylesheets === false) {
16428             
16429             Roo.get(document.head).select('style').each(function(node) {
16430                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16431             });
16432             
16433             Roo.get(document.head).select('link').each(function(node) { 
16434                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16435             });
16436             
16437         } else if (!this.stylesheets.length) {
16438                 // simple..
16439                 st = '<style type="text/css">' +
16440                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16441                    '</style>';
16442         } else {
16443             Roo.each(this.stylesheets, function(s) {
16444                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16445             });
16446             
16447         }
16448         
16449         st +=  '<style type="text/css">' +
16450             'IMG { cursor: pointer } ' +
16451         '</style>';
16452
16453         
16454         return '<html><head>' + st  +
16455             //<style type="text/css">' +
16456             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16457             //'</style>' +
16458             ' </head><body class="roo-htmleditor-body"></body></html>';
16459     },
16460
16461     // private
16462     onRender : function(ct, position)
16463     {
16464         var _t = this;
16465         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16466         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16467         
16468         
16469         this.el.dom.style.border = '0 none';
16470         this.el.dom.setAttribute('tabIndex', -1);
16471         this.el.addClass('x-hidden hide');
16472         
16473         
16474         
16475         if(Roo.isIE){ // fix IE 1px bogus margin
16476             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16477         }
16478        
16479         
16480         this.frameId = Roo.id();
16481         
16482          
16483         
16484         var iframe = this.owner.wrap.createChild({
16485             tag: 'iframe',
16486             cls: 'form-control', // bootstrap..
16487             id: this.frameId,
16488             name: this.frameId,
16489             frameBorder : 'no',
16490             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16491         }, this.el
16492         );
16493         
16494         
16495         this.iframe = iframe.dom;
16496
16497          this.assignDocWin();
16498         
16499         this.doc.designMode = 'on';
16500        
16501         this.doc.open();
16502         this.doc.write(this.getDocMarkup());
16503         this.doc.close();
16504
16505         
16506         var task = { // must defer to wait for browser to be ready
16507             run : function(){
16508                 //console.log("run task?" + this.doc.readyState);
16509                 this.assignDocWin();
16510                 if(this.doc.body || this.doc.readyState == 'complete'){
16511                     try {
16512                         this.doc.designMode="on";
16513                     } catch (e) {
16514                         return;
16515                     }
16516                     Roo.TaskMgr.stop(task);
16517                     this.initEditor.defer(10, this);
16518                 }
16519             },
16520             interval : 10,
16521             duration: 10000,
16522             scope: this
16523         };
16524         Roo.TaskMgr.start(task);
16525
16526         
16527          
16528     },
16529
16530     // private
16531     onResize : function(w, h)
16532     {
16533          Roo.log('resize: ' +w + ',' + h );
16534         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16535         if(!this.iframe){
16536             return;
16537         }
16538         if(typeof w == 'number'){
16539             
16540             this.iframe.style.width = w + 'px';
16541         }
16542         if(typeof h == 'number'){
16543             
16544             this.iframe.style.height = h + 'px';
16545             if(this.doc){
16546                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16547             }
16548         }
16549         
16550     },
16551
16552     /**
16553      * Toggles the editor between standard and source edit mode.
16554      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16555      */
16556     toggleSourceEdit : function(sourceEditMode){
16557         
16558         this.sourceEditMode = sourceEditMode === true;
16559         
16560         if(this.sourceEditMode){
16561  
16562             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16563             
16564         }else{
16565             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16566             //this.iframe.className = '';
16567             this.deferFocus();
16568         }
16569         //this.setSize(this.owner.wrap.getSize());
16570         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16571     },
16572
16573     
16574   
16575
16576     /**
16577      * Protected method that will not generally be called directly. If you need/want
16578      * custom HTML cleanup, this is the method you should override.
16579      * @param {String} html The HTML to be cleaned
16580      * return {String} The cleaned HTML
16581      */
16582     cleanHtml : function(html){
16583         html = String(html);
16584         if(html.length > 5){
16585             if(Roo.isSafari){ // strip safari nonsense
16586                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16587             }
16588         }
16589         if(html == '&nbsp;'){
16590             html = '';
16591         }
16592         return html;
16593     },
16594
16595     /**
16596      * HTML Editor -> Textarea
16597      * Protected method that will not generally be called directly. Syncs the contents
16598      * of the editor iframe with the textarea.
16599      */
16600     syncValue : function(){
16601         if(this.initialized){
16602             var bd = (this.doc.body || this.doc.documentElement);
16603             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16604             var html = bd.innerHTML;
16605             if(Roo.isSafari){
16606                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16607                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16608                 if(m && m[1]){
16609                     html = '<div style="'+m[0]+'">' + html + '</div>';
16610                 }
16611             }
16612             html = this.cleanHtml(html);
16613             // fix up the special chars.. normaly like back quotes in word...
16614             // however we do not want to do this with chinese..
16615             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16616                 var cc = b.charCodeAt();
16617                 if (
16618                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16619                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16620                     (cc >= 0xf900 && cc < 0xfb00 )
16621                 ) {
16622                         return b;
16623                 }
16624                 return "&#"+cc+";" 
16625             });
16626             if(this.owner.fireEvent('beforesync', this, html) !== false){
16627                 this.el.dom.value = html;
16628                 this.owner.fireEvent('sync', this, html);
16629             }
16630         }
16631     },
16632
16633     /**
16634      * Protected method that will not generally be called directly. Pushes the value of the textarea
16635      * into the iframe editor.
16636      */
16637     pushValue : function(){
16638         if(this.initialized){
16639             var v = this.el.dom.value.trim();
16640             
16641 //            if(v.length < 1){
16642 //                v = '&#160;';
16643 //            }
16644             
16645             if(this.owner.fireEvent('beforepush', this, v) !== false){
16646                 var d = (this.doc.body || this.doc.documentElement);
16647                 d.innerHTML = v;
16648                 this.cleanUpPaste();
16649                 this.el.dom.value = d.innerHTML;
16650                 this.owner.fireEvent('push', this, v);
16651             }
16652         }
16653     },
16654
16655     // private
16656     deferFocus : function(){
16657         this.focus.defer(10, this);
16658     },
16659
16660     // doc'ed in Field
16661     focus : function(){
16662         if(this.win && !this.sourceEditMode){
16663             this.win.focus();
16664         }else{
16665             this.el.focus();
16666         }
16667     },
16668     
16669     assignDocWin: function()
16670     {
16671         var iframe = this.iframe;
16672         
16673          if(Roo.isIE){
16674             this.doc = iframe.contentWindow.document;
16675             this.win = iframe.contentWindow;
16676         } else {
16677 //            if (!Roo.get(this.frameId)) {
16678 //                return;
16679 //            }
16680 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16681 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16682             
16683             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16684                 return;
16685             }
16686             
16687             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16688             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16689         }
16690     },
16691     
16692     // private
16693     initEditor : function(){
16694         //console.log("INIT EDITOR");
16695         this.assignDocWin();
16696         
16697         
16698         
16699         this.doc.designMode="on";
16700         this.doc.open();
16701         this.doc.write(this.getDocMarkup());
16702         this.doc.close();
16703         
16704         var dbody = (this.doc.body || this.doc.documentElement);
16705         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16706         // this copies styles from the containing element into thsi one..
16707         // not sure why we need all of this..
16708         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16709         
16710         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16711         //ss['background-attachment'] = 'fixed'; // w3c
16712         dbody.bgProperties = 'fixed'; // ie
16713         //Roo.DomHelper.applyStyles(dbody, ss);
16714         Roo.EventManager.on(this.doc, {
16715             //'mousedown': this.onEditorEvent,
16716             'mouseup': this.onEditorEvent,
16717             'dblclick': this.onEditorEvent,
16718             'click': this.onEditorEvent,
16719             'keyup': this.onEditorEvent,
16720             buffer:100,
16721             scope: this
16722         });
16723         if(Roo.isGecko){
16724             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16725         }
16726         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16727             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16728         }
16729         this.initialized = true;
16730
16731         this.owner.fireEvent('initialize', this);
16732         this.pushValue();
16733     },
16734
16735     // private
16736     onDestroy : function(){
16737         
16738         
16739         
16740         if(this.rendered){
16741             
16742             //for (var i =0; i < this.toolbars.length;i++) {
16743             //    // fixme - ask toolbars for heights?
16744             //    this.toolbars[i].onDestroy();
16745            // }
16746             
16747             //this.wrap.dom.innerHTML = '';
16748             //this.wrap.remove();
16749         }
16750     },
16751
16752     // private
16753     onFirstFocus : function(){
16754         
16755         this.assignDocWin();
16756         
16757         
16758         this.activated = true;
16759          
16760     
16761         if(Roo.isGecko){ // prevent silly gecko errors
16762             this.win.focus();
16763             var s = this.win.getSelection();
16764             if(!s.focusNode || s.focusNode.nodeType != 3){
16765                 var r = s.getRangeAt(0);
16766                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16767                 r.collapse(true);
16768                 this.deferFocus();
16769             }
16770             try{
16771                 this.execCmd('useCSS', true);
16772                 this.execCmd('styleWithCSS', false);
16773             }catch(e){}
16774         }
16775         this.owner.fireEvent('activate', this);
16776     },
16777
16778     // private
16779     adjustFont: function(btn){
16780         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16781         //if(Roo.isSafari){ // safari
16782         //    adjust *= 2;
16783        // }
16784         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16785         if(Roo.isSafari){ // safari
16786             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16787             v =  (v < 10) ? 10 : v;
16788             v =  (v > 48) ? 48 : v;
16789             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16790             
16791         }
16792         
16793         
16794         v = Math.max(1, v+adjust);
16795         
16796         this.execCmd('FontSize', v  );
16797     },
16798
16799     onEditorEvent : function(e){
16800         this.owner.fireEvent('editorevent', this, e);
16801       //  this.updateToolbar();
16802         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16803     },
16804
16805     insertTag : function(tg)
16806     {
16807         // could be a bit smarter... -> wrap the current selected tRoo..
16808         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16809             
16810             range = this.createRange(this.getSelection());
16811             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16812             wrappingNode.appendChild(range.extractContents());
16813             range.insertNode(wrappingNode);
16814
16815             return;
16816             
16817             
16818             
16819         }
16820         this.execCmd("formatblock",   tg);
16821         
16822     },
16823     
16824     insertText : function(txt)
16825     {
16826         
16827         
16828         var range = this.createRange();
16829         range.deleteContents();
16830                //alert(Sender.getAttribute('label'));
16831                
16832         range.insertNode(this.doc.createTextNode(txt));
16833     } ,
16834     
16835      
16836
16837     /**
16838      * Executes a Midas editor command on the editor document and performs necessary focus and
16839      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16840      * @param {String} cmd The Midas command
16841      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16842      */
16843     relayCmd : function(cmd, value){
16844         this.win.focus();
16845         this.execCmd(cmd, value);
16846         this.owner.fireEvent('editorevent', this);
16847         //this.updateToolbar();
16848         this.owner.deferFocus();
16849     },
16850
16851     /**
16852      * Executes a Midas editor command directly on the editor document.
16853      * For visual commands, you should use {@link #relayCmd} instead.
16854      * <b>This should only be called after the editor is initialized.</b>
16855      * @param {String} cmd The Midas command
16856      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16857      */
16858     execCmd : function(cmd, value){
16859         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16860         this.syncValue();
16861     },
16862  
16863  
16864    
16865     /**
16866      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16867      * to insert tRoo.
16868      * @param {String} text | dom node.. 
16869      */
16870     insertAtCursor : function(text)
16871     {
16872         
16873         
16874         
16875         if(!this.activated){
16876             return;
16877         }
16878         /*
16879         if(Roo.isIE){
16880             this.win.focus();
16881             var r = this.doc.selection.createRange();
16882             if(r){
16883                 r.collapse(true);
16884                 r.pasteHTML(text);
16885                 this.syncValue();
16886                 this.deferFocus();
16887             
16888             }
16889             return;
16890         }
16891         */
16892         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16893             this.win.focus();
16894             
16895             
16896             // from jquery ui (MIT licenced)
16897             var range, node;
16898             var win = this.win;
16899             
16900             if (win.getSelection && win.getSelection().getRangeAt) {
16901                 range = win.getSelection().getRangeAt(0);
16902                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16903                 range.insertNode(node);
16904             } else if (win.document.selection && win.document.selection.createRange) {
16905                 // no firefox support
16906                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16907                 win.document.selection.createRange().pasteHTML(txt);
16908             } else {
16909                 // no firefox support
16910                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16911                 this.execCmd('InsertHTML', txt);
16912             } 
16913             
16914             this.syncValue();
16915             
16916             this.deferFocus();
16917         }
16918     },
16919  // private
16920     mozKeyPress : function(e){
16921         if(e.ctrlKey){
16922             var c = e.getCharCode(), cmd;
16923           
16924             if(c > 0){
16925                 c = String.fromCharCode(c).toLowerCase();
16926                 switch(c){
16927                     case 'b':
16928                         cmd = 'bold';
16929                         break;
16930                     case 'i':
16931                         cmd = 'italic';
16932                         break;
16933                     
16934                     case 'u':
16935                         cmd = 'underline';
16936                         break;
16937                     
16938                     case 'v':
16939                         this.cleanUpPaste.defer(100, this);
16940                         return;
16941                         
16942                 }
16943                 if(cmd){
16944                     this.win.focus();
16945                     this.execCmd(cmd);
16946                     this.deferFocus();
16947                     e.preventDefault();
16948                 }
16949                 
16950             }
16951         }
16952     },
16953
16954     // private
16955     fixKeys : function(){ // load time branching for fastest keydown performance
16956         if(Roo.isIE){
16957             return function(e){
16958                 var k = e.getKey(), r;
16959                 if(k == e.TAB){
16960                     e.stopEvent();
16961                     r = this.doc.selection.createRange();
16962                     if(r){
16963                         r.collapse(true);
16964                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16965                         this.deferFocus();
16966                     }
16967                     return;
16968                 }
16969                 
16970                 if(k == e.ENTER){
16971                     r = this.doc.selection.createRange();
16972                     if(r){
16973                         var target = r.parentElement();
16974                         if(!target || target.tagName.toLowerCase() != 'li'){
16975                             e.stopEvent();
16976                             r.pasteHTML('<br />');
16977                             r.collapse(false);
16978                             r.select();
16979                         }
16980                     }
16981                 }
16982                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16983                     this.cleanUpPaste.defer(100, this);
16984                     return;
16985                 }
16986                 
16987                 
16988             };
16989         }else if(Roo.isOpera){
16990             return function(e){
16991                 var k = e.getKey();
16992                 if(k == e.TAB){
16993                     e.stopEvent();
16994                     this.win.focus();
16995                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16996                     this.deferFocus();
16997                 }
16998                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16999                     this.cleanUpPaste.defer(100, this);
17000                     return;
17001                 }
17002                 
17003             };
17004         }else if(Roo.isSafari){
17005             return function(e){
17006                 var k = e.getKey();
17007                 
17008                 if(k == e.TAB){
17009                     e.stopEvent();
17010                     this.execCmd('InsertText','\t');
17011                     this.deferFocus();
17012                     return;
17013                 }
17014                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17015                     this.cleanUpPaste.defer(100, this);
17016                     return;
17017                 }
17018                 
17019              };
17020         }
17021     }(),
17022     
17023     getAllAncestors: function()
17024     {
17025         var p = this.getSelectedNode();
17026         var a = [];
17027         if (!p) {
17028             a.push(p); // push blank onto stack..
17029             p = this.getParentElement();
17030         }
17031         
17032         
17033         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17034             a.push(p);
17035             p = p.parentNode;
17036         }
17037         a.push(this.doc.body);
17038         return a;
17039     },
17040     lastSel : false,
17041     lastSelNode : false,
17042     
17043     
17044     getSelection : function() 
17045     {
17046         this.assignDocWin();
17047         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17048     },
17049     
17050     getSelectedNode: function() 
17051     {
17052         // this may only work on Gecko!!!
17053         
17054         // should we cache this!!!!
17055         
17056         
17057         
17058          
17059         var range = this.createRange(this.getSelection()).cloneRange();
17060         
17061         if (Roo.isIE) {
17062             var parent = range.parentElement();
17063             while (true) {
17064                 var testRange = range.duplicate();
17065                 testRange.moveToElementText(parent);
17066                 if (testRange.inRange(range)) {
17067                     break;
17068                 }
17069                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17070                     break;
17071                 }
17072                 parent = parent.parentElement;
17073             }
17074             return parent;
17075         }
17076         
17077         // is ancestor a text element.
17078         var ac =  range.commonAncestorContainer;
17079         if (ac.nodeType == 3) {
17080             ac = ac.parentNode;
17081         }
17082         
17083         var ar = ac.childNodes;
17084          
17085         var nodes = [];
17086         var other_nodes = [];
17087         var has_other_nodes = false;
17088         for (var i=0;i<ar.length;i++) {
17089             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17090                 continue;
17091             }
17092             // fullly contained node.
17093             
17094             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17095                 nodes.push(ar[i]);
17096                 continue;
17097             }
17098             
17099             // probably selected..
17100             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17101                 other_nodes.push(ar[i]);
17102                 continue;
17103             }
17104             // outer..
17105             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17106                 continue;
17107             }
17108             
17109             
17110             has_other_nodes = true;
17111         }
17112         if (!nodes.length && other_nodes.length) {
17113             nodes= other_nodes;
17114         }
17115         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17116             return false;
17117         }
17118         
17119         return nodes[0];
17120     },
17121     createRange: function(sel)
17122     {
17123         // this has strange effects when using with 
17124         // top toolbar - not sure if it's a great idea.
17125         //this.editor.contentWindow.focus();
17126         if (typeof sel != "undefined") {
17127             try {
17128                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17129             } catch(e) {
17130                 return this.doc.createRange();
17131             }
17132         } else {
17133             return this.doc.createRange();
17134         }
17135     },
17136     getParentElement: function()
17137     {
17138         
17139         this.assignDocWin();
17140         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17141         
17142         var range = this.createRange(sel);
17143          
17144         try {
17145             var p = range.commonAncestorContainer;
17146             while (p.nodeType == 3) { // text node
17147                 p = p.parentNode;
17148             }
17149             return p;
17150         } catch (e) {
17151             return null;
17152         }
17153     
17154     },
17155     /***
17156      *
17157      * Range intersection.. the hard stuff...
17158      *  '-1' = before
17159      *  '0' = hits..
17160      *  '1' = after.
17161      *         [ -- selected range --- ]
17162      *   [fail]                        [fail]
17163      *
17164      *    basically..
17165      *      if end is before start or  hits it. fail.
17166      *      if start is after end or hits it fail.
17167      *
17168      *   if either hits (but other is outside. - then it's not 
17169      *   
17170      *    
17171      **/
17172     
17173     
17174     // @see http://www.thismuchiknow.co.uk/?p=64.
17175     rangeIntersectsNode : function(range, node)
17176     {
17177         var nodeRange = node.ownerDocument.createRange();
17178         try {
17179             nodeRange.selectNode(node);
17180         } catch (e) {
17181             nodeRange.selectNodeContents(node);
17182         }
17183     
17184         var rangeStartRange = range.cloneRange();
17185         rangeStartRange.collapse(true);
17186     
17187         var rangeEndRange = range.cloneRange();
17188         rangeEndRange.collapse(false);
17189     
17190         var nodeStartRange = nodeRange.cloneRange();
17191         nodeStartRange.collapse(true);
17192     
17193         var nodeEndRange = nodeRange.cloneRange();
17194         nodeEndRange.collapse(false);
17195     
17196         return rangeStartRange.compareBoundaryPoints(
17197                  Range.START_TO_START, nodeEndRange) == -1 &&
17198                rangeEndRange.compareBoundaryPoints(
17199                  Range.START_TO_START, nodeStartRange) == 1;
17200         
17201          
17202     },
17203     rangeCompareNode : function(range, node)
17204     {
17205         var nodeRange = node.ownerDocument.createRange();
17206         try {
17207             nodeRange.selectNode(node);
17208         } catch (e) {
17209             nodeRange.selectNodeContents(node);
17210         }
17211         
17212         
17213         range.collapse(true);
17214     
17215         nodeRange.collapse(true);
17216      
17217         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17218         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17219          
17220         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17221         
17222         var nodeIsBefore   =  ss == 1;
17223         var nodeIsAfter    = ee == -1;
17224         
17225         if (nodeIsBefore && nodeIsAfter)
17226             return 0; // outer
17227         if (!nodeIsBefore && nodeIsAfter)
17228             return 1; //right trailed.
17229         
17230         if (nodeIsBefore && !nodeIsAfter)
17231             return 2;  // left trailed.
17232         // fully contined.
17233         return 3;
17234     },
17235
17236     // private? - in a new class?
17237     cleanUpPaste :  function()
17238     {
17239         // cleans up the whole document..
17240         Roo.log('cleanuppaste');
17241         
17242         this.cleanUpChildren(this.doc.body);
17243         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17244         if (clean != this.doc.body.innerHTML) {
17245             this.doc.body.innerHTML = clean;
17246         }
17247         
17248     },
17249     
17250     cleanWordChars : function(input) {// change the chars to hex code
17251         var he = Roo.HtmlEditorCore;
17252         
17253         var output = input;
17254         Roo.each(he.swapCodes, function(sw) { 
17255             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17256             
17257             output = output.replace(swapper, sw[1]);
17258         });
17259         
17260         return output;
17261     },
17262     
17263     
17264     cleanUpChildren : function (n)
17265     {
17266         if (!n.childNodes.length) {
17267             return;
17268         }
17269         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17270            this.cleanUpChild(n.childNodes[i]);
17271         }
17272     },
17273     
17274     
17275         
17276     
17277     cleanUpChild : function (node)
17278     {
17279         var ed = this;
17280         //console.log(node);
17281         if (node.nodeName == "#text") {
17282             // clean up silly Windows -- stuff?
17283             return; 
17284         }
17285         if (node.nodeName == "#comment") {
17286             node.parentNode.removeChild(node);
17287             // clean up silly Windows -- stuff?
17288             return; 
17289         }
17290         var lcname = node.tagName.toLowerCase();
17291         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17292         // whitelist of tags..
17293         
17294         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17295             // remove node.
17296             node.parentNode.removeChild(node);
17297             return;
17298             
17299         }
17300         
17301         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17302         
17303         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17304         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17305         
17306         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17307         //    remove_keep_children = true;
17308         //}
17309         
17310         if (remove_keep_children) {
17311             this.cleanUpChildren(node);
17312             // inserts everything just before this node...
17313             while (node.childNodes.length) {
17314                 var cn = node.childNodes[0];
17315                 node.removeChild(cn);
17316                 node.parentNode.insertBefore(cn, node);
17317             }
17318             node.parentNode.removeChild(node);
17319             return;
17320         }
17321         
17322         if (!node.attributes || !node.attributes.length) {
17323             this.cleanUpChildren(node);
17324             return;
17325         }
17326         
17327         function cleanAttr(n,v)
17328         {
17329             
17330             if (v.match(/^\./) || v.match(/^\//)) {
17331                 return;
17332             }
17333             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17334                 return;
17335             }
17336             if (v.match(/^#/)) {
17337                 return;
17338             }
17339 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17340             node.removeAttribute(n);
17341             
17342         }
17343         
17344         function cleanStyle(n,v)
17345         {
17346             if (v.match(/expression/)) { //XSS?? should we even bother..
17347                 node.removeAttribute(n);
17348                 return;
17349             }
17350             var cwhite = this.cwhite;
17351             var cblack = this.cblack;
17352             
17353             
17354             var parts = v.split(/;/);
17355             var clean = [];
17356             
17357             Roo.each(parts, function(p) {
17358                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17359                 if (!p.length) {
17360                     return true;
17361                 }
17362                 var l = p.split(':').shift().replace(/\s+/g,'');
17363                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17364                 
17365                 if ( cblack.indexOf(l) > -1) {
17366 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17367                     //node.removeAttribute(n);
17368                     return true;
17369                 }
17370                 //Roo.log()
17371                 // only allow 'c whitelisted system attributes'
17372                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17373 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17374                     //node.removeAttribute(n);
17375                     return true;
17376                 }
17377                 
17378                 
17379                  
17380                 
17381                 clean.push(p);
17382                 return true;
17383             });
17384             if (clean.length) { 
17385                 node.setAttribute(n, clean.join(';'));
17386             } else {
17387                 node.removeAttribute(n);
17388             }
17389             
17390         }
17391         
17392         
17393         for (var i = node.attributes.length-1; i > -1 ; i--) {
17394             var a = node.attributes[i];
17395             //console.log(a);
17396             
17397             if (a.name.toLowerCase().substr(0,2)=='on')  {
17398                 node.removeAttribute(a.name);
17399                 continue;
17400             }
17401             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17402                 node.removeAttribute(a.name);
17403                 continue;
17404             }
17405             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17406                 cleanAttr(a.name,a.value); // fixme..
17407                 continue;
17408             }
17409             if (a.name == 'style') {
17410                 cleanStyle(a.name,a.value);
17411                 continue;
17412             }
17413             /// clean up MS crap..
17414             // tecnically this should be a list of valid class'es..
17415             
17416             
17417             if (a.name == 'class') {
17418                 if (a.value.match(/^Mso/)) {
17419                     node.className = '';
17420                 }
17421                 
17422                 if (a.value.match(/body/)) {
17423                     node.className = '';
17424                 }
17425                 continue;
17426             }
17427             
17428             // style cleanup!?
17429             // class cleanup?
17430             
17431         }
17432         
17433         
17434         this.cleanUpChildren(node);
17435         
17436         
17437     },
17438     /**
17439      * Clean up MS wordisms...
17440      */
17441     cleanWord : function(node)
17442     {
17443         var _t = this;
17444         var cleanWordChildren = function()
17445         {
17446             if (!node.childNodes.length) {
17447                 return;
17448             }
17449             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17450                _t.cleanWord(node.childNodes[i]);
17451             }
17452         }
17453         
17454         
17455         if (!node) {
17456             this.cleanWord(this.doc.body);
17457             return;
17458         }
17459         if (node.nodeName == "#text") {
17460             // clean up silly Windows -- stuff?
17461             return; 
17462         }
17463         if (node.nodeName == "#comment") {
17464             node.parentNode.removeChild(node);
17465             // clean up silly Windows -- stuff?
17466             return; 
17467         }
17468         
17469         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17470             node.parentNode.removeChild(node);
17471             return;
17472         }
17473         
17474         // remove - but keep children..
17475         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17476             while (node.childNodes.length) {
17477                 var cn = node.childNodes[0];
17478                 node.removeChild(cn);
17479                 node.parentNode.insertBefore(cn, node);
17480             }
17481             node.parentNode.removeChild(node);
17482             cleanWordChildren();
17483             return;
17484         }
17485         // clean styles
17486         if (node.className.length) {
17487             
17488             var cn = node.className.split(/\W+/);
17489             var cna = [];
17490             Roo.each(cn, function(cls) {
17491                 if (cls.match(/Mso[a-zA-Z]+/)) {
17492                     return;
17493                 }
17494                 cna.push(cls);
17495             });
17496             node.className = cna.length ? cna.join(' ') : '';
17497             if (!cna.length) {
17498                 node.removeAttribute("class");
17499             }
17500         }
17501         
17502         if (node.hasAttribute("lang")) {
17503             node.removeAttribute("lang");
17504         }
17505         
17506         if (node.hasAttribute("style")) {
17507             
17508             var styles = node.getAttribute("style").split(";");
17509             var nstyle = [];
17510             Roo.each(styles, function(s) {
17511                 if (!s.match(/:/)) {
17512                     return;
17513                 }
17514                 var kv = s.split(":");
17515                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17516                     return;
17517                 }
17518                 // what ever is left... we allow.
17519                 nstyle.push(s);
17520             });
17521             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17522             if (!nstyle.length) {
17523                 node.removeAttribute('style');
17524             }
17525         }
17526         
17527         cleanWordChildren();
17528         
17529         
17530     },
17531     domToHTML : function(currentElement, depth, nopadtext) {
17532         
17533         depth = depth || 0;
17534         nopadtext = nopadtext || false;
17535     
17536         if (!currentElement) {
17537             return this.domToHTML(this.doc.body);
17538         }
17539         
17540         //Roo.log(currentElement);
17541         var j;
17542         var allText = false;
17543         var nodeName = currentElement.nodeName;
17544         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17545         
17546         if  (nodeName == '#text') {
17547             return currentElement.nodeValue;
17548         }
17549         
17550         
17551         var ret = '';
17552         if (nodeName != 'BODY') {
17553              
17554             var i = 0;
17555             // Prints the node tagName, such as <A>, <IMG>, etc
17556             if (tagName) {
17557                 var attr = [];
17558                 for(i = 0; i < currentElement.attributes.length;i++) {
17559                     // quoting?
17560                     var aname = currentElement.attributes.item(i).name;
17561                     if (!currentElement.attributes.item(i).value.length) {
17562                         continue;
17563                     }
17564                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17565                 }
17566                 
17567                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17568             } 
17569             else {
17570                 
17571                 // eack
17572             }
17573         } else {
17574             tagName = false;
17575         }
17576         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17577             return ret;
17578         }
17579         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17580             nopadtext = true;
17581         }
17582         
17583         
17584         // Traverse the tree
17585         i = 0;
17586         var currentElementChild = currentElement.childNodes.item(i);
17587         var allText = true;
17588         var innerHTML  = '';
17589         lastnode = '';
17590         while (currentElementChild) {
17591             // Formatting code (indent the tree so it looks nice on the screen)
17592             var nopad = nopadtext;
17593             if (lastnode == 'SPAN') {
17594                 nopad  = true;
17595             }
17596             // text
17597             if  (currentElementChild.nodeName == '#text') {
17598                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17599                 if (!nopad && toadd.length > 80) {
17600                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17601                 }
17602                 innerHTML  += toadd;
17603                 
17604                 i++;
17605                 currentElementChild = currentElement.childNodes.item(i);
17606                 lastNode = '';
17607                 continue;
17608             }
17609             allText = false;
17610             
17611             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17612                 
17613             // Recursively traverse the tree structure of the child node
17614             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17615             lastnode = currentElementChild.nodeName;
17616             i++;
17617             currentElementChild=currentElement.childNodes.item(i);
17618         }
17619         
17620         ret += innerHTML;
17621         
17622         if (!allText) {
17623                 // The remaining code is mostly for formatting the tree
17624             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17625         }
17626         
17627         
17628         if (tagName) {
17629             ret+= "</"+tagName+">";
17630         }
17631         return ret;
17632         
17633     },
17634         
17635     applyBlacklists : function()
17636     {
17637         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17638         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17639         
17640         this.white = [];
17641         this.black = [];
17642         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17643             if (b.indexOf(tag) > -1) {
17644                 return;
17645             }
17646             this.white.push(tag);
17647             
17648         }, this);
17649         
17650         Roo.each(w, function(tag) {
17651             if (b.indexOf(tag) > -1) {
17652                 return;
17653             }
17654             if (this.white.indexOf(tag) > -1) {
17655                 return;
17656             }
17657             this.white.push(tag);
17658             
17659         }, this);
17660         
17661         
17662         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17663             if (w.indexOf(tag) > -1) {
17664                 return;
17665             }
17666             this.black.push(tag);
17667             
17668         }, this);
17669         
17670         Roo.each(b, function(tag) {
17671             if (w.indexOf(tag) > -1) {
17672                 return;
17673             }
17674             if (this.black.indexOf(tag) > -1) {
17675                 return;
17676             }
17677             this.black.push(tag);
17678             
17679         }, this);
17680         
17681         
17682         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17683         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17684         
17685         this.cwhite = [];
17686         this.cblack = [];
17687         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17688             if (b.indexOf(tag) > -1) {
17689                 return;
17690             }
17691             this.cwhite.push(tag);
17692             
17693         }, this);
17694         
17695         Roo.each(w, function(tag) {
17696             if (b.indexOf(tag) > -1) {
17697                 return;
17698             }
17699             if (this.cwhite.indexOf(tag) > -1) {
17700                 return;
17701             }
17702             this.cwhite.push(tag);
17703             
17704         }, this);
17705         
17706         
17707         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17708             if (w.indexOf(tag) > -1) {
17709                 return;
17710             }
17711             this.cblack.push(tag);
17712             
17713         }, this);
17714         
17715         Roo.each(b, function(tag) {
17716             if (w.indexOf(tag) > -1) {
17717                 return;
17718             }
17719             if (this.cblack.indexOf(tag) > -1) {
17720                 return;
17721             }
17722             this.cblack.push(tag);
17723             
17724         }, this);
17725     }
17726     
17727     // hide stuff that is not compatible
17728     /**
17729      * @event blur
17730      * @hide
17731      */
17732     /**
17733      * @event change
17734      * @hide
17735      */
17736     /**
17737      * @event focus
17738      * @hide
17739      */
17740     /**
17741      * @event specialkey
17742      * @hide
17743      */
17744     /**
17745      * @cfg {String} fieldClass @hide
17746      */
17747     /**
17748      * @cfg {String} focusClass @hide
17749      */
17750     /**
17751      * @cfg {String} autoCreate @hide
17752      */
17753     /**
17754      * @cfg {String} inputType @hide
17755      */
17756     /**
17757      * @cfg {String} invalidClass @hide
17758      */
17759     /**
17760      * @cfg {String} invalidText @hide
17761      */
17762     /**
17763      * @cfg {String} msgFx @hide
17764      */
17765     /**
17766      * @cfg {String} validateOnBlur @hide
17767      */
17768 });
17769
17770 Roo.HtmlEditorCore.white = [
17771         'area', 'br', 'img', 'input', 'hr', 'wbr',
17772         
17773        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17774        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17775        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17776        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17777        'table',   'ul',         'xmp', 
17778        
17779        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17780       'thead',   'tr', 
17781      
17782       'dir', 'menu', 'ol', 'ul', 'dl',
17783        
17784       'embed',  'object'
17785 ];
17786
17787
17788 Roo.HtmlEditorCore.black = [
17789     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17790         'applet', // 
17791         'base',   'basefont', 'bgsound', 'blink',  'body', 
17792         'frame',  'frameset', 'head',    'html',   'ilayer', 
17793         'iframe', 'layer',  'link',     'meta',    'object',   
17794         'script', 'style' ,'title',  'xml' // clean later..
17795 ];
17796 Roo.HtmlEditorCore.clean = [
17797     'script', 'style', 'title', 'xml'
17798 ];
17799 Roo.HtmlEditorCore.remove = [
17800     'font'
17801 ];
17802 // attributes..
17803
17804 Roo.HtmlEditorCore.ablack = [
17805     'on'
17806 ];
17807     
17808 Roo.HtmlEditorCore.aclean = [ 
17809     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17810 ];
17811
17812 // protocols..
17813 Roo.HtmlEditorCore.pwhite= [
17814         'http',  'https',  'mailto'
17815 ];
17816
17817 // white listed style attributes.
17818 Roo.HtmlEditorCore.cwhite= [
17819       //  'text-align', /// default is to allow most things..
17820       
17821          
17822 //        'font-size'//??
17823 ];
17824
17825 // black listed style attributes.
17826 Roo.HtmlEditorCore.cblack= [
17827       //  'font-size' -- this can be set by the project 
17828 ];
17829
17830
17831 Roo.HtmlEditorCore.swapCodes   =[ 
17832     [    8211, "--" ], 
17833     [    8212, "--" ], 
17834     [    8216,  "'" ],  
17835     [    8217, "'" ],  
17836     [    8220, '"' ],  
17837     [    8221, '"' ],  
17838     [    8226, "*" ],  
17839     [    8230, "..." ]
17840 ]; 
17841
17842     /*
17843  * - LGPL
17844  *
17845  * HtmlEditor
17846  * 
17847  */
17848
17849 /**
17850  * @class Roo.bootstrap.HtmlEditor
17851  * @extends Roo.bootstrap.TextArea
17852  * Bootstrap HtmlEditor class
17853
17854  * @constructor
17855  * Create a new HtmlEditor
17856  * @param {Object} config The config object
17857  */
17858
17859 Roo.bootstrap.HtmlEditor = function(config){
17860     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17861     if (!this.toolbars) {
17862         this.toolbars = [];
17863     }
17864     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17865     this.addEvents({
17866             /**
17867              * @event initialize
17868              * Fires when the editor is fully initialized (including the iframe)
17869              * @param {HtmlEditor} this
17870              */
17871             initialize: true,
17872             /**
17873              * @event activate
17874              * Fires when the editor is first receives the focus. Any insertion must wait
17875              * until after this event.
17876              * @param {HtmlEditor} this
17877              */
17878             activate: true,
17879              /**
17880              * @event beforesync
17881              * Fires before the textarea is updated with content from the editor iframe. Return false
17882              * to cancel the sync.
17883              * @param {HtmlEditor} this
17884              * @param {String} html
17885              */
17886             beforesync: true,
17887              /**
17888              * @event beforepush
17889              * Fires before the iframe editor is updated with content from the textarea. Return false
17890              * to cancel the push.
17891              * @param {HtmlEditor} this
17892              * @param {String} html
17893              */
17894             beforepush: true,
17895              /**
17896              * @event sync
17897              * Fires when the textarea is updated with content from the editor iframe.
17898              * @param {HtmlEditor} this
17899              * @param {String} html
17900              */
17901             sync: true,
17902              /**
17903              * @event push
17904              * Fires when the iframe editor is updated with content from the textarea.
17905              * @param {HtmlEditor} this
17906              * @param {String} html
17907              */
17908             push: true,
17909              /**
17910              * @event editmodechange
17911              * Fires when the editor switches edit modes
17912              * @param {HtmlEditor} this
17913              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17914              */
17915             editmodechange: true,
17916             /**
17917              * @event editorevent
17918              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17919              * @param {HtmlEditor} this
17920              */
17921             editorevent: true,
17922             /**
17923              * @event firstfocus
17924              * Fires when on first focus - needed by toolbars..
17925              * @param {HtmlEditor} this
17926              */
17927             firstfocus: true,
17928             /**
17929              * @event autosave
17930              * Auto save the htmlEditor value as a file into Events
17931              * @param {HtmlEditor} this
17932              */
17933             autosave: true,
17934             /**
17935              * @event savedpreview
17936              * preview the saved version of htmlEditor
17937              * @param {HtmlEditor} this
17938              */
17939             savedpreview: true
17940         });
17941 };
17942
17943
17944 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17945     
17946     
17947       /**
17948      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17949      */
17950     toolbars : false,
17951    
17952      /**
17953      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17954      *                        Roo.resizable.
17955      */
17956     resizable : false,
17957      /**
17958      * @cfg {Number} height (in pixels)
17959      */   
17960     height: 300,
17961    /**
17962      * @cfg {Number} width (in pixels)
17963      */   
17964     width: false,
17965     
17966     /**
17967      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17968      * 
17969      */
17970     stylesheets: false,
17971     
17972     // id of frame..
17973     frameId: false,
17974     
17975     // private properties
17976     validationEvent : false,
17977     deferHeight: true,
17978     initialized : false,
17979     activated : false,
17980     
17981     onFocus : Roo.emptyFn,
17982     iframePad:3,
17983     hideMode:'offsets',
17984     
17985     
17986     tbContainer : false,
17987     
17988     toolbarContainer :function() {
17989         return this.wrap.select('.x-html-editor-tb',true).first();
17990     },
17991
17992     /**
17993      * Protected method that will not generally be called directly. It
17994      * is called when the editor creates its toolbar. Override this method if you need to
17995      * add custom toolbar buttons.
17996      * @param {HtmlEditor} editor
17997      */
17998     createToolbar : function(){
17999         
18000         Roo.log("create toolbars");
18001         
18002         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18003         this.toolbars[0].render(this.toolbarContainer());
18004         
18005         return;
18006         
18007 //        if (!editor.toolbars || !editor.toolbars.length) {
18008 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18009 //        }
18010 //        
18011 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18012 //            editor.toolbars[i] = Roo.factory(
18013 //                    typeof(editor.toolbars[i]) == 'string' ?
18014 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18015 //                Roo.bootstrap.HtmlEditor);
18016 //            editor.toolbars[i].init(editor);
18017 //        }
18018     },
18019
18020      
18021     // private
18022     onRender : function(ct, position)
18023     {
18024        // Roo.log("Call onRender: " + this.xtype);
18025         var _t = this;
18026         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18027       
18028         this.wrap = this.inputEl().wrap({
18029             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18030         });
18031         
18032         this.editorcore.onRender(ct, position);
18033          
18034         if (this.resizable) {
18035             this.resizeEl = new Roo.Resizable(this.wrap, {
18036                 pinned : true,
18037                 wrap: true,
18038                 dynamic : true,
18039                 minHeight : this.height,
18040                 height: this.height,
18041                 handles : this.resizable,
18042                 width: this.width,
18043                 listeners : {
18044                     resize : function(r, w, h) {
18045                         _t.onResize(w,h); // -something
18046                     }
18047                 }
18048             });
18049             
18050         }
18051         this.createToolbar(this);
18052        
18053         
18054         if(!this.width && this.resizable){
18055             this.setSize(this.wrap.getSize());
18056         }
18057         if (this.resizeEl) {
18058             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18059             // should trigger onReize..
18060         }
18061         
18062     },
18063
18064     // private
18065     onResize : function(w, h)
18066     {
18067         Roo.log('resize: ' +w + ',' + h );
18068         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18069         var ew = false;
18070         var eh = false;
18071         
18072         if(this.inputEl() ){
18073             if(typeof w == 'number'){
18074                 var aw = w - this.wrap.getFrameWidth('lr');
18075                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18076                 ew = aw;
18077             }
18078             if(typeof h == 'number'){
18079                  var tbh = -11;  // fixme it needs to tool bar size!
18080                 for (var i =0; i < this.toolbars.length;i++) {
18081                     // fixme - ask toolbars for heights?
18082                     tbh += this.toolbars[i].el.getHeight();
18083                     //if (this.toolbars[i].footer) {
18084                     //    tbh += this.toolbars[i].footer.el.getHeight();
18085                     //}
18086                 }
18087               
18088                 
18089                 
18090                 
18091                 
18092                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18093                 ah -= 5; // knock a few pixes off for look..
18094                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18095                 var eh = ah;
18096             }
18097         }
18098         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18099         this.editorcore.onResize(ew,eh);
18100         
18101     },
18102
18103     /**
18104      * Toggles the editor between standard and source edit mode.
18105      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18106      */
18107     toggleSourceEdit : function(sourceEditMode)
18108     {
18109         this.editorcore.toggleSourceEdit(sourceEditMode);
18110         
18111         if(this.editorcore.sourceEditMode){
18112             Roo.log('editor - showing textarea');
18113             
18114 //            Roo.log('in');
18115 //            Roo.log(this.syncValue());
18116             this.syncValue();
18117             this.inputEl().removeClass(['hide', 'x-hidden']);
18118             this.inputEl().dom.removeAttribute('tabIndex');
18119             this.inputEl().focus();
18120         }else{
18121             Roo.log('editor - hiding textarea');
18122 //            Roo.log('out')
18123 //            Roo.log(this.pushValue()); 
18124             this.pushValue();
18125             
18126             this.inputEl().addClass(['hide', 'x-hidden']);
18127             this.inputEl().dom.setAttribute('tabIndex', -1);
18128             //this.deferFocus();
18129         }
18130          
18131         if(this.resizable){
18132             this.setSize(this.wrap.getSize());
18133         }
18134         
18135         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18136     },
18137  
18138     // private (for BoxComponent)
18139     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18140
18141     // private (for BoxComponent)
18142     getResizeEl : function(){
18143         return this.wrap;
18144     },
18145
18146     // private (for BoxComponent)
18147     getPositionEl : function(){
18148         return this.wrap;
18149     },
18150
18151     // private
18152     initEvents : function(){
18153         this.originalValue = this.getValue();
18154     },
18155
18156 //    /**
18157 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18158 //     * @method
18159 //     */
18160 //    markInvalid : Roo.emptyFn,
18161 //    /**
18162 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18163 //     * @method
18164 //     */
18165 //    clearInvalid : Roo.emptyFn,
18166
18167     setValue : function(v){
18168         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18169         this.editorcore.pushValue();
18170     },
18171
18172      
18173     // private
18174     deferFocus : function(){
18175         this.focus.defer(10, this);
18176     },
18177
18178     // doc'ed in Field
18179     focus : function(){
18180         this.editorcore.focus();
18181         
18182     },
18183       
18184
18185     // private
18186     onDestroy : function(){
18187         
18188         
18189         
18190         if(this.rendered){
18191             
18192             for (var i =0; i < this.toolbars.length;i++) {
18193                 // fixme - ask toolbars for heights?
18194                 this.toolbars[i].onDestroy();
18195             }
18196             
18197             this.wrap.dom.innerHTML = '';
18198             this.wrap.remove();
18199         }
18200     },
18201
18202     // private
18203     onFirstFocus : function(){
18204         //Roo.log("onFirstFocus");
18205         this.editorcore.onFirstFocus();
18206          for (var i =0; i < this.toolbars.length;i++) {
18207             this.toolbars[i].onFirstFocus();
18208         }
18209         
18210     },
18211     
18212     // private
18213     syncValue : function()
18214     {   
18215         this.editorcore.syncValue();
18216     },
18217     
18218     pushValue : function()
18219     {   
18220         this.editorcore.pushValue();
18221     }
18222      
18223     
18224     // hide stuff that is not compatible
18225     /**
18226      * @event blur
18227      * @hide
18228      */
18229     /**
18230      * @event change
18231      * @hide
18232      */
18233     /**
18234      * @event focus
18235      * @hide
18236      */
18237     /**
18238      * @event specialkey
18239      * @hide
18240      */
18241     /**
18242      * @cfg {String} fieldClass @hide
18243      */
18244     /**
18245      * @cfg {String} focusClass @hide
18246      */
18247     /**
18248      * @cfg {String} autoCreate @hide
18249      */
18250     /**
18251      * @cfg {String} inputType @hide
18252      */
18253     /**
18254      * @cfg {String} invalidClass @hide
18255      */
18256     /**
18257      * @cfg {String} invalidText @hide
18258      */
18259     /**
18260      * @cfg {String} msgFx @hide
18261      */
18262     /**
18263      * @cfg {String} validateOnBlur @hide
18264      */
18265 });
18266  
18267     
18268    
18269    
18270    
18271       
18272 Roo.namespace('Roo.bootstrap.htmleditor');
18273 /**
18274  * @class Roo.bootstrap.HtmlEditorToolbar1
18275  * Basic Toolbar
18276  * 
18277  * Usage:
18278  *
18279  new Roo.bootstrap.HtmlEditor({
18280     ....
18281     toolbars : [
18282         new Roo.bootstrap.HtmlEditorToolbar1({
18283             disable : { fonts: 1 , format: 1, ..., ... , ...],
18284             btns : [ .... ]
18285         })
18286     }
18287      
18288  * 
18289  * @cfg {Object} disable List of elements to disable..
18290  * @cfg {Array} btns List of additional buttons.
18291  * 
18292  * 
18293  * NEEDS Extra CSS? 
18294  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18295  */
18296  
18297 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18298 {
18299     
18300     Roo.apply(this, config);
18301     
18302     // default disabled, based on 'good practice'..
18303     this.disable = this.disable || {};
18304     Roo.applyIf(this.disable, {
18305         fontSize : true,
18306         colors : true,
18307         specialElements : true
18308     });
18309     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18310     
18311     this.editor = config.editor;
18312     this.editorcore = config.editor.editorcore;
18313     
18314     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18315     
18316     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18317     // dont call parent... till later.
18318 }
18319 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18320      
18321     bar : true,
18322     
18323     editor : false,
18324     editorcore : false,
18325     
18326     
18327     formats : [
18328         "p" ,  
18329         "h1","h2","h3","h4","h5","h6", 
18330         "pre", "code", 
18331         "abbr", "acronym", "address", "cite", "samp", "var",
18332         'div','span'
18333     ],
18334     
18335     onRender : function(ct, position)
18336     {
18337        // Roo.log("Call onRender: " + this.xtype);
18338         
18339        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18340        Roo.log(this.el);
18341        this.el.dom.style.marginBottom = '0';
18342        var _this = this;
18343        var editorcore = this.editorcore;
18344        var editor= this.editor;
18345        
18346        var children = [];
18347        var btn = function(id,cmd , toggle, handler){
18348        
18349             var  event = toggle ? 'toggle' : 'click';
18350        
18351             var a = {
18352                 size : 'sm',
18353                 xtype: 'Button',
18354                 xns: Roo.bootstrap,
18355                 glyphicon : id,
18356                 cmd : id || cmd,
18357                 enableToggle:toggle !== false,
18358                 //html : 'submit'
18359                 pressed : toggle ? false : null,
18360                 listeners : {}
18361             }
18362             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18363                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18364             }
18365             children.push(a);
18366             return a;
18367        }
18368         
18369         var style = {
18370                 xtype: 'Button',
18371                 size : 'sm',
18372                 xns: Roo.bootstrap,
18373                 glyphicon : 'font',
18374                 //html : 'submit'
18375                 menu : {
18376                     xtype: 'Menu',
18377                     xns: Roo.bootstrap,
18378                     items:  []
18379                 }
18380         };
18381         Roo.each(this.formats, function(f) {
18382             style.menu.items.push({
18383                 xtype :'MenuItem',
18384                 xns: Roo.bootstrap,
18385                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18386                 tagname : f,
18387                 listeners : {
18388                     click : function()
18389                     {
18390                         editorcore.insertTag(this.tagname);
18391                         editor.focus();
18392                     }
18393                 }
18394                 
18395             });
18396         });
18397          children.push(style);   
18398             
18399             
18400         btn('bold',false,true);
18401         btn('italic',false,true);
18402         btn('align-left', 'justifyleft',true);
18403         btn('align-center', 'justifycenter',true);
18404         btn('align-right' , 'justifyright',true);
18405         btn('link', false, false, function(btn) {
18406             //Roo.log("create link?");
18407             var url = prompt(this.createLinkText, this.defaultLinkValue);
18408             if(url && url != 'http:/'+'/'){
18409                 this.editorcore.relayCmd('createlink', url);
18410             }
18411         }),
18412         btn('list','insertunorderedlist',true);
18413         btn('pencil', false,true, function(btn){
18414                 Roo.log(this);
18415                 
18416                 this.toggleSourceEdit(btn.pressed);
18417         });
18418         /*
18419         var cog = {
18420                 xtype: 'Button',
18421                 size : 'sm',
18422                 xns: Roo.bootstrap,
18423                 glyphicon : 'cog',
18424                 //html : 'submit'
18425                 menu : {
18426                     xtype: 'Menu',
18427                     xns: Roo.bootstrap,
18428                     items:  []
18429                 }
18430         };
18431         
18432         cog.menu.items.push({
18433             xtype :'MenuItem',
18434             xns: Roo.bootstrap,
18435             html : Clean styles,
18436             tagname : f,
18437             listeners : {
18438                 click : function()
18439                 {
18440                     editorcore.insertTag(this.tagname);
18441                     editor.focus();
18442                 }
18443             }
18444             
18445         });
18446        */
18447         
18448          
18449        this.xtype = 'NavSimplebar';
18450         
18451         for(var i=0;i< children.length;i++) {
18452             
18453             this.buttons.add(this.addxtypeChild(children[i]));
18454             
18455         }
18456         
18457         editor.on('editorevent', this.updateToolbar, this);
18458     },
18459     onBtnClick : function(id)
18460     {
18461        this.editorcore.relayCmd(id);
18462        this.editorcore.focus();
18463     },
18464     
18465     /**
18466      * Protected method that will not generally be called directly. It triggers
18467      * a toolbar update by reading the markup state of the current selection in the editor.
18468      */
18469     updateToolbar: function(){
18470
18471         if(!this.editorcore.activated){
18472             this.editor.onFirstFocus(); // is this neeed?
18473             return;
18474         }
18475
18476         var btns = this.buttons; 
18477         var doc = this.editorcore.doc;
18478         btns.get('bold').setActive(doc.queryCommandState('bold'));
18479         btns.get('italic').setActive(doc.queryCommandState('italic'));
18480         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18481         
18482         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18483         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18484         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18485         
18486         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18487         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18488          /*
18489         
18490         var ans = this.editorcore.getAllAncestors();
18491         if (this.formatCombo) {
18492             
18493             
18494             var store = this.formatCombo.store;
18495             this.formatCombo.setValue("");
18496             for (var i =0; i < ans.length;i++) {
18497                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18498                     // select it..
18499                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18500                     break;
18501                 }
18502             }
18503         }
18504         
18505         
18506         
18507         // hides menus... - so this cant be on a menu...
18508         Roo.bootstrap.MenuMgr.hideAll();
18509         */
18510         Roo.bootstrap.MenuMgr.hideAll();
18511         //this.editorsyncValue();
18512     },
18513     onFirstFocus: function() {
18514         this.buttons.each(function(item){
18515            item.enable();
18516         });
18517     },
18518     toggleSourceEdit : function(sourceEditMode){
18519         
18520           
18521         if(sourceEditMode){
18522             Roo.log("disabling buttons");
18523            this.buttons.each( function(item){
18524                 if(item.cmd != 'pencil'){
18525                     item.disable();
18526                 }
18527             });
18528           
18529         }else{
18530             Roo.log("enabling buttons");
18531             if(this.editorcore.initialized){
18532                 this.buttons.each( function(item){
18533                     item.enable();
18534                 });
18535             }
18536             
18537         }
18538         Roo.log("calling toggole on editor");
18539         // tell the editor that it's been pressed..
18540         this.editor.toggleSourceEdit(sourceEditMode);
18541        
18542     }
18543 });
18544
18545
18546
18547
18548
18549 /**
18550  * @class Roo.bootstrap.Table.AbstractSelectionModel
18551  * @extends Roo.util.Observable
18552  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18553  * implemented by descendant classes.  This class should not be directly instantiated.
18554  * @constructor
18555  */
18556 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18557     this.locked = false;
18558     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18559 };
18560
18561
18562 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18563     /** @ignore Called by the grid automatically. Do not call directly. */
18564     init : function(grid){
18565         this.grid = grid;
18566         this.initEvents();
18567     },
18568
18569     /**
18570      * Locks the selections.
18571      */
18572     lock : function(){
18573         this.locked = true;
18574     },
18575
18576     /**
18577      * Unlocks the selections.
18578      */
18579     unlock : function(){
18580         this.locked = false;
18581     },
18582
18583     /**
18584      * Returns true if the selections are locked.
18585      * @return {Boolean}
18586      */
18587     isLocked : function(){
18588         return this.locked;
18589     }
18590 });
18591 /**
18592  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18593  * @class Roo.bootstrap.Table.RowSelectionModel
18594  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18595  * It supports multiple selections and keyboard selection/navigation. 
18596  * @constructor
18597  * @param {Object} config
18598  */
18599
18600 Roo.bootstrap.Table.RowSelectionModel = function(config){
18601     Roo.apply(this, config);
18602     this.selections = new Roo.util.MixedCollection(false, function(o){
18603         return o.id;
18604     });
18605
18606     this.last = false;
18607     this.lastActive = false;
18608
18609     this.addEvents({
18610         /**
18611              * @event selectionchange
18612              * Fires when the selection changes
18613              * @param {SelectionModel} this
18614              */
18615             "selectionchange" : true,
18616         /**
18617              * @event afterselectionchange
18618              * Fires after the selection changes (eg. by key press or clicking)
18619              * @param {SelectionModel} this
18620              */
18621             "afterselectionchange" : true,
18622         /**
18623              * @event beforerowselect
18624              * Fires when a row is selected being selected, return false to cancel.
18625              * @param {SelectionModel} this
18626              * @param {Number} rowIndex The selected index
18627              * @param {Boolean} keepExisting False if other selections will be cleared
18628              */
18629             "beforerowselect" : true,
18630         /**
18631              * @event rowselect
18632              * Fires when a row is selected.
18633              * @param {SelectionModel} this
18634              * @param {Number} rowIndex The selected index
18635              * @param {Roo.data.Record} r The record
18636              */
18637             "rowselect" : true,
18638         /**
18639              * @event rowdeselect
18640              * Fires when a row is deselected.
18641              * @param {SelectionModel} this
18642              * @param {Number} rowIndex The selected index
18643              */
18644         "rowdeselect" : true
18645     });
18646     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18647     this.locked = false;
18648 };
18649
18650 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18651     /**
18652      * @cfg {Boolean} singleSelect
18653      * True to allow selection of only one row at a time (defaults to false)
18654      */
18655     singleSelect : false,
18656
18657     // private
18658     initEvents : function(){
18659
18660         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18661             this.grid.on("mousedown", this.handleMouseDown, this);
18662         }else{ // allow click to work like normal
18663             this.grid.on("rowclick", this.handleDragableRowClick, this);
18664         }
18665
18666         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18667             "up" : function(e){
18668                 if(!e.shiftKey){
18669                     this.selectPrevious(e.shiftKey);
18670                 }else if(this.last !== false && this.lastActive !== false){
18671                     var last = this.last;
18672                     this.selectRange(this.last,  this.lastActive-1);
18673                     this.grid.getView().focusRow(this.lastActive);
18674                     if(last !== false){
18675                         this.last = last;
18676                     }
18677                 }else{
18678                     this.selectFirstRow();
18679                 }
18680                 this.fireEvent("afterselectionchange", this);
18681             },
18682             "down" : function(e){
18683                 if(!e.shiftKey){
18684                     this.selectNext(e.shiftKey);
18685                 }else if(this.last !== false && this.lastActive !== false){
18686                     var last = this.last;
18687                     this.selectRange(this.last,  this.lastActive+1);
18688                     this.grid.getView().focusRow(this.lastActive);
18689                     if(last !== false){
18690                         this.last = last;
18691                     }
18692                 }else{
18693                     this.selectFirstRow();
18694                 }
18695                 this.fireEvent("afterselectionchange", this);
18696             },
18697             scope: this
18698         });
18699
18700         var view = this.grid.view;
18701         view.on("refresh", this.onRefresh, this);
18702         view.on("rowupdated", this.onRowUpdated, this);
18703         view.on("rowremoved", this.onRemove, this);
18704     },
18705
18706     // private
18707     onRefresh : function(){
18708         var ds = this.grid.dataSource, i, v = this.grid.view;
18709         var s = this.selections;
18710         s.each(function(r){
18711             if((i = ds.indexOfId(r.id)) != -1){
18712                 v.onRowSelect(i);
18713             }else{
18714                 s.remove(r);
18715             }
18716         });
18717     },
18718
18719     // private
18720     onRemove : function(v, index, r){
18721         this.selections.remove(r);
18722     },
18723
18724     // private
18725     onRowUpdated : function(v, index, r){
18726         if(this.isSelected(r)){
18727             v.onRowSelect(index);
18728         }
18729     },
18730
18731     /**
18732      * Select records.
18733      * @param {Array} records The records to select
18734      * @param {Boolean} keepExisting (optional) True to keep existing selections
18735      */
18736     selectRecords : function(records, keepExisting){
18737         if(!keepExisting){
18738             this.clearSelections();
18739         }
18740         var ds = this.grid.dataSource;
18741         for(var i = 0, len = records.length; i < len; i++){
18742             this.selectRow(ds.indexOf(records[i]), true);
18743         }
18744     },
18745
18746     /**
18747      * Gets the number of selected rows.
18748      * @return {Number}
18749      */
18750     getCount : function(){
18751         return this.selections.length;
18752     },
18753
18754     /**
18755      * Selects the first row in the grid.
18756      */
18757     selectFirstRow : function(){
18758         this.selectRow(0);
18759     },
18760
18761     /**
18762      * Select the last row.
18763      * @param {Boolean} keepExisting (optional) True to keep existing selections
18764      */
18765     selectLastRow : function(keepExisting){
18766         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18767     },
18768
18769     /**
18770      * Selects the row immediately following the last selected row.
18771      * @param {Boolean} keepExisting (optional) True to keep existing selections
18772      */
18773     selectNext : function(keepExisting){
18774         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18775             this.selectRow(this.last+1, keepExisting);
18776             this.grid.getView().focusRow(this.last);
18777         }
18778     },
18779
18780     /**
18781      * Selects the row that precedes the last selected row.
18782      * @param {Boolean} keepExisting (optional) True to keep existing selections
18783      */
18784     selectPrevious : function(keepExisting){
18785         if(this.last){
18786             this.selectRow(this.last-1, keepExisting);
18787             this.grid.getView().focusRow(this.last);
18788         }
18789     },
18790
18791     /**
18792      * Returns the selected records
18793      * @return {Array} Array of selected records
18794      */
18795     getSelections : function(){
18796         return [].concat(this.selections.items);
18797     },
18798
18799     /**
18800      * Returns the first selected record.
18801      * @return {Record}
18802      */
18803     getSelected : function(){
18804         return this.selections.itemAt(0);
18805     },
18806
18807
18808     /**
18809      * Clears all selections.
18810      */
18811     clearSelections : function(fast){
18812         if(this.locked) return;
18813         if(fast !== true){
18814             var ds = this.grid.dataSource;
18815             var s = this.selections;
18816             s.each(function(r){
18817                 this.deselectRow(ds.indexOfId(r.id));
18818             }, this);
18819             s.clear();
18820         }else{
18821             this.selections.clear();
18822         }
18823         this.last = false;
18824     },
18825
18826
18827     /**
18828      * Selects all rows.
18829      */
18830     selectAll : function(){
18831         if(this.locked) return;
18832         this.selections.clear();
18833         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18834             this.selectRow(i, true);
18835         }
18836     },
18837
18838     /**
18839      * Returns True if there is a selection.
18840      * @return {Boolean}
18841      */
18842     hasSelection : function(){
18843         return this.selections.length > 0;
18844     },
18845
18846     /**
18847      * Returns True if the specified row is selected.
18848      * @param {Number/Record} record The record or index of the record to check
18849      * @return {Boolean}
18850      */
18851     isSelected : function(index){
18852         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18853         return (r && this.selections.key(r.id) ? true : false);
18854     },
18855
18856     /**
18857      * Returns True if the specified record id is selected.
18858      * @param {String} id The id of record to check
18859      * @return {Boolean}
18860      */
18861     isIdSelected : function(id){
18862         return (this.selections.key(id) ? true : false);
18863     },
18864
18865     // private
18866     handleMouseDown : function(e, t){
18867         var view = this.grid.getView(), rowIndex;
18868         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18869             return;
18870         };
18871         if(e.shiftKey && this.last !== false){
18872             var last = this.last;
18873             this.selectRange(last, rowIndex, e.ctrlKey);
18874             this.last = last; // reset the last
18875             view.focusRow(rowIndex);
18876         }else{
18877             var isSelected = this.isSelected(rowIndex);
18878             if(e.button !== 0 && isSelected){
18879                 view.focusRow(rowIndex);
18880             }else if(e.ctrlKey && isSelected){
18881                 this.deselectRow(rowIndex);
18882             }else if(!isSelected){
18883                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18884                 view.focusRow(rowIndex);
18885             }
18886         }
18887         this.fireEvent("afterselectionchange", this);
18888     },
18889     // private
18890     handleDragableRowClick :  function(grid, rowIndex, e) 
18891     {
18892         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18893             this.selectRow(rowIndex, false);
18894             grid.view.focusRow(rowIndex);
18895              this.fireEvent("afterselectionchange", this);
18896         }
18897     },
18898     
18899     /**
18900      * Selects multiple rows.
18901      * @param {Array} rows Array of the indexes of the row to select
18902      * @param {Boolean} keepExisting (optional) True to keep existing selections
18903      */
18904     selectRows : function(rows, keepExisting){
18905         if(!keepExisting){
18906             this.clearSelections();
18907         }
18908         for(var i = 0, len = rows.length; i < len; i++){
18909             this.selectRow(rows[i], true);
18910         }
18911     },
18912
18913     /**
18914      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18915      * @param {Number} startRow The index of the first row in the range
18916      * @param {Number} endRow The index of the last row in the range
18917      * @param {Boolean} keepExisting (optional) True to retain existing selections
18918      */
18919     selectRange : function(startRow, endRow, keepExisting){
18920         if(this.locked) return;
18921         if(!keepExisting){
18922             this.clearSelections();
18923         }
18924         if(startRow <= endRow){
18925             for(var i = startRow; i <= endRow; i++){
18926                 this.selectRow(i, true);
18927             }
18928         }else{
18929             for(var i = startRow; i >= endRow; i--){
18930                 this.selectRow(i, true);
18931             }
18932         }
18933     },
18934
18935     /**
18936      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18937      * @param {Number} startRow The index of the first row in the range
18938      * @param {Number} endRow The index of the last row in the range
18939      */
18940     deselectRange : function(startRow, endRow, preventViewNotify){
18941         if(this.locked) return;
18942         for(var i = startRow; i <= endRow; i++){
18943             this.deselectRow(i, preventViewNotify);
18944         }
18945     },
18946
18947     /**
18948      * Selects a row.
18949      * @param {Number} row The index of the row to select
18950      * @param {Boolean} keepExisting (optional) True to keep existing selections
18951      */
18952     selectRow : function(index, keepExisting, preventViewNotify){
18953         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18954         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18955             if(!keepExisting || this.singleSelect){
18956                 this.clearSelections();
18957             }
18958             var r = this.grid.dataSource.getAt(index);
18959             this.selections.add(r);
18960             this.last = this.lastActive = index;
18961             if(!preventViewNotify){
18962                 this.grid.getView().onRowSelect(index);
18963             }
18964             this.fireEvent("rowselect", this, index, r);
18965             this.fireEvent("selectionchange", this);
18966         }
18967     },
18968
18969     /**
18970      * Deselects a row.
18971      * @param {Number} row The index of the row to deselect
18972      */
18973     deselectRow : function(index, preventViewNotify){
18974         if(this.locked) return;
18975         if(this.last == index){
18976             this.last = false;
18977         }
18978         if(this.lastActive == index){
18979             this.lastActive = false;
18980         }
18981         var r = this.grid.dataSource.getAt(index);
18982         this.selections.remove(r);
18983         if(!preventViewNotify){
18984             this.grid.getView().onRowDeselect(index);
18985         }
18986         this.fireEvent("rowdeselect", this, index);
18987         this.fireEvent("selectionchange", this);
18988     },
18989
18990     // private
18991     restoreLast : function(){
18992         if(this._last){
18993             this.last = this._last;
18994         }
18995     },
18996
18997     // private
18998     acceptsNav : function(row, col, cm){
18999         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19000     },
19001
19002     // private
19003     onEditorKey : function(field, e){
19004         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19005         if(k == e.TAB){
19006             e.stopEvent();
19007             ed.completeEdit();
19008             if(e.shiftKey){
19009                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19010             }else{
19011                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19012             }
19013         }else if(k == e.ENTER && !e.ctrlKey){
19014             e.stopEvent();
19015             ed.completeEdit();
19016             if(e.shiftKey){
19017                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19018             }else{
19019                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19020             }
19021         }else if(k == e.ESC){
19022             ed.cancelEdit();
19023         }
19024         if(newCell){
19025             g.startEditing(newCell[0], newCell[1]);
19026         }
19027     }
19028 });/*
19029  * Based on:
19030  * Ext JS Library 1.1.1
19031  * Copyright(c) 2006-2007, Ext JS, LLC.
19032  *
19033  * Originally Released Under LGPL - original licence link has changed is not relivant.
19034  *
19035  * Fork - LGPL
19036  * <script type="text/javascript">
19037  */
19038  
19039 /**
19040  * @class Roo.bootstrap.PagingToolbar
19041  * @extends Roo.Row
19042  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19043  * @constructor
19044  * Create a new PagingToolbar
19045  * @param {Object} config The config object
19046  */
19047 Roo.bootstrap.PagingToolbar = function(config)
19048 {
19049     // old args format still supported... - xtype is prefered..
19050         // created from xtype...
19051     var ds = config.dataSource;
19052     this.toolbarItems = [];
19053     if (config.items) {
19054         this.toolbarItems = config.items;
19055 //        config.items = [];
19056     }
19057     
19058     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19059     this.ds = ds;
19060     this.cursor = 0;
19061     if (ds) { 
19062         this.bind(ds);
19063     }
19064     
19065     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19066     
19067 };
19068
19069 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19070     /**
19071      * @cfg {Roo.data.Store} dataSource
19072      * The underlying data store providing the paged data
19073      */
19074     /**
19075      * @cfg {String/HTMLElement/Element} container
19076      * container The id or element that will contain the toolbar
19077      */
19078     /**
19079      * @cfg {Boolean} displayInfo
19080      * True to display the displayMsg (defaults to false)
19081      */
19082     /**
19083      * @cfg {Number} pageSize
19084      * The number of records to display per page (defaults to 20)
19085      */
19086     pageSize: 20,
19087     /**
19088      * @cfg {String} displayMsg
19089      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19090      */
19091     displayMsg : 'Displaying {0} - {1} of {2}',
19092     /**
19093      * @cfg {String} emptyMsg
19094      * The message to display when no records are found (defaults to "No data to display")
19095      */
19096     emptyMsg : 'No data to display',
19097     /**
19098      * Customizable piece of the default paging text (defaults to "Page")
19099      * @type String
19100      */
19101     beforePageText : "Page",
19102     /**
19103      * Customizable piece of the default paging text (defaults to "of %0")
19104      * @type String
19105      */
19106     afterPageText : "of {0}",
19107     /**
19108      * Customizable piece of the default paging text (defaults to "First Page")
19109      * @type String
19110      */
19111     firstText : "First Page",
19112     /**
19113      * Customizable piece of the default paging text (defaults to "Previous Page")
19114      * @type String
19115      */
19116     prevText : "Previous Page",
19117     /**
19118      * Customizable piece of the default paging text (defaults to "Next Page")
19119      * @type String
19120      */
19121     nextText : "Next Page",
19122     /**
19123      * Customizable piece of the default paging text (defaults to "Last Page")
19124      * @type String
19125      */
19126     lastText : "Last Page",
19127     /**
19128      * Customizable piece of the default paging text (defaults to "Refresh")
19129      * @type String
19130      */
19131     refreshText : "Refresh",
19132
19133     buttons : false,
19134     // private
19135     onRender : function(ct, position) 
19136     {
19137         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19138         this.navgroup.parentId = this.id;
19139         this.navgroup.onRender(this.el, null);
19140         // add the buttons to the navgroup
19141         
19142         if(this.displayInfo){
19143             Roo.log(this.el.select('ul.navbar-nav',true).first());
19144             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19145             this.displayEl = this.el.select('.x-paging-info', true).first();
19146 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19147 //            this.displayEl = navel.el.select('span',true).first();
19148         }
19149         
19150         var _this = this;
19151         
19152         if(this.buttons){
19153             Roo.each(_this.buttons, function(e){
19154                Roo.factory(e).onRender(_this.el, null);
19155             });
19156         }
19157             
19158         Roo.each(_this.toolbarItems, function(e) {
19159             _this.navgroup.addItem(e);
19160         });
19161         
19162         this.first = this.navgroup.addItem({
19163             tooltip: this.firstText,
19164             cls: "prev",
19165             icon : 'fa fa-backward',
19166             disabled: true,
19167             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19168         });
19169         
19170         this.prev =  this.navgroup.addItem({
19171             tooltip: this.prevText,
19172             cls: "prev",
19173             icon : 'fa fa-step-backward',
19174             disabled: true,
19175             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19176         });
19177     //this.addSeparator();
19178         
19179         
19180         var field = this.navgroup.addItem( {
19181             tagtype : 'span',
19182             cls : 'x-paging-position',
19183             
19184             html : this.beforePageText  +
19185                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19186                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19187          } ); //?? escaped?
19188         
19189         this.field = field.el.select('input', true).first();
19190         this.field.on("keydown", this.onPagingKeydown, this);
19191         this.field.on("focus", function(){this.dom.select();});
19192     
19193     
19194         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19195         //this.field.setHeight(18);
19196         //this.addSeparator();
19197         this.next = this.navgroup.addItem({
19198             tooltip: this.nextText,
19199             cls: "next",
19200             html : ' <i class="fa fa-step-forward">',
19201             disabled: true,
19202             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19203         });
19204         this.last = this.navgroup.addItem({
19205             tooltip: this.lastText,
19206             icon : 'fa fa-forward',
19207             cls: "next",
19208             disabled: true,
19209             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19210         });
19211     //this.addSeparator();
19212         this.loading = this.navgroup.addItem({
19213             tooltip: this.refreshText,
19214             icon: 'fa fa-refresh',
19215             
19216             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19217         });
19218
19219     },
19220
19221     // private
19222     updateInfo : function(){
19223         if(this.displayEl){
19224             var count = this.ds.getCount();
19225             var msg = count == 0 ?
19226                 this.emptyMsg :
19227                 String.format(
19228                     this.displayMsg,
19229                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19230                 );
19231             this.displayEl.update(msg);
19232         }
19233     },
19234
19235     // private
19236     onLoad : function(ds, r, o){
19237        this.cursor = o.params ? o.params.start : 0;
19238        var d = this.getPageData(),
19239             ap = d.activePage,
19240             ps = d.pages;
19241         
19242        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19243        this.field.dom.value = ap;
19244        this.first.setDisabled(ap == 1);
19245        this.prev.setDisabled(ap == 1);
19246        this.next.setDisabled(ap == ps);
19247        this.last.setDisabled(ap == ps);
19248        this.loading.enable();
19249        this.updateInfo();
19250     },
19251
19252     // private
19253     getPageData : function(){
19254         var total = this.ds.getTotalCount();
19255         return {
19256             total : total,
19257             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19258             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19259         };
19260     },
19261
19262     // private
19263     onLoadError : function(){
19264         this.loading.enable();
19265     },
19266
19267     // private
19268     onPagingKeydown : function(e){
19269         var k = e.getKey();
19270         var d = this.getPageData();
19271         if(k == e.RETURN){
19272             var v = this.field.dom.value, pageNum;
19273             if(!v || isNaN(pageNum = parseInt(v, 10))){
19274                 this.field.dom.value = d.activePage;
19275                 return;
19276             }
19277             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19278             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19279             e.stopEvent();
19280         }
19281         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))
19282         {
19283           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19284           this.field.dom.value = pageNum;
19285           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19286           e.stopEvent();
19287         }
19288         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19289         {
19290           var v = this.field.dom.value, pageNum; 
19291           var increment = (e.shiftKey) ? 10 : 1;
19292           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19293             increment *= -1;
19294           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19295             this.field.dom.value = d.activePage;
19296             return;
19297           }
19298           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19299           {
19300             this.field.dom.value = parseInt(v, 10) + increment;
19301             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19302             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19303           }
19304           e.stopEvent();
19305         }
19306     },
19307
19308     // private
19309     beforeLoad : function(){
19310         if(this.loading){
19311             this.loading.disable();
19312         }
19313     },
19314
19315     // private
19316     onClick : function(which){
19317         var ds = this.ds;
19318         if (!ds) {
19319             return;
19320         }
19321         switch(which){
19322             case "first":
19323                 ds.load({params:{start: 0, limit: this.pageSize}});
19324             break;
19325             case "prev":
19326                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19327             break;
19328             case "next":
19329                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19330             break;
19331             case "last":
19332                 var total = ds.getTotalCount();
19333                 var extra = total % this.pageSize;
19334                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19335                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19336             break;
19337             case "refresh":
19338                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19339             break;
19340         }
19341     },
19342
19343     /**
19344      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19345      * @param {Roo.data.Store} store The data store to unbind
19346      */
19347     unbind : function(ds){
19348         ds.un("beforeload", this.beforeLoad, this);
19349         ds.un("load", this.onLoad, this);
19350         ds.un("loadexception", this.onLoadError, this);
19351         ds.un("remove", this.updateInfo, this);
19352         ds.un("add", this.updateInfo, this);
19353         this.ds = undefined;
19354     },
19355
19356     /**
19357      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19358      * @param {Roo.data.Store} store The data store to bind
19359      */
19360     bind : function(ds){
19361         ds.on("beforeload", this.beforeLoad, this);
19362         ds.on("load", this.onLoad, this);
19363         ds.on("loadexception", this.onLoadError, this);
19364         ds.on("remove", this.updateInfo, this);
19365         ds.on("add", this.updateInfo, this);
19366         this.ds = ds;
19367     }
19368 });/*
19369  * - LGPL
19370  *
19371  * element
19372  * 
19373  */
19374
19375 /**
19376  * @class Roo.bootstrap.MessageBar
19377  * @extends Roo.bootstrap.Component
19378  * Bootstrap MessageBar class
19379  * @cfg {String} html contents of the MessageBar
19380  * @cfg {String} weight (info | success | warning | danger) default info
19381  * @cfg {String} beforeClass insert the bar before the given class
19382  * @cfg {Boolean} closable (true | false) default false
19383  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19384  * 
19385  * @constructor
19386  * Create a new Element
19387  * @param {Object} config The config object
19388  */
19389
19390 Roo.bootstrap.MessageBar = function(config){
19391     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19392 };
19393
19394 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19395     
19396     html: '',
19397     weight: 'info',
19398     closable: false,
19399     fixed: false,
19400     beforeClass: 'bootstrap-sticky-wrap',
19401     
19402     getAutoCreate : function(){
19403         
19404         var cfg = {
19405             tag: 'div',
19406             cls: 'alert alert-dismissable alert-' + this.weight,
19407             cn: [
19408                 {
19409                     tag: 'span',
19410                     cls: 'message',
19411                     html: this.html || ''
19412                 }
19413             ]
19414         }
19415         
19416         if(this.fixed){
19417             cfg.cls += ' alert-messages-fixed';
19418         }
19419         
19420         if(this.closable){
19421             cfg.cn.push({
19422                 tag: 'button',
19423                 cls: 'close',
19424                 html: 'x'
19425             });
19426         }
19427         
19428         return cfg;
19429     },
19430     
19431     onRender : function(ct, position)
19432     {
19433         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19434         
19435         if(!this.el){
19436             var cfg = Roo.apply({},  this.getAutoCreate());
19437             cfg.id = Roo.id();
19438             
19439             if (this.cls) {
19440                 cfg.cls += ' ' + this.cls;
19441             }
19442             if (this.style) {
19443                 cfg.style = this.style;
19444             }
19445             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19446             
19447             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19448         }
19449         
19450         this.el.select('>button.close').on('click', this.hide, this);
19451         
19452     },
19453     
19454     show : function()
19455     {
19456         if (!this.rendered) {
19457             this.render();
19458         }
19459         
19460         this.el.show();
19461         
19462         this.fireEvent('show', this);
19463         
19464     },
19465     
19466     hide : function()
19467     {
19468         if (!this.rendered) {
19469             this.render();
19470         }
19471         
19472         this.el.hide();
19473         
19474         this.fireEvent('hide', this);
19475     },
19476     
19477     update : function()
19478     {
19479 //        var e = this.el.dom.firstChild;
19480 //        
19481 //        if(this.closable){
19482 //            e = e.nextSibling;
19483 //        }
19484 //        
19485 //        e.data = this.html || '';
19486
19487         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19488     }
19489    
19490 });
19491
19492  
19493
19494      /*
19495  * - LGPL
19496  *
19497  * Graph
19498  * 
19499  */
19500
19501
19502 /**
19503  * @class Roo.bootstrap.Graph
19504  * @extends Roo.bootstrap.Component
19505  * Bootstrap Graph class
19506 > Prameters
19507  -sm {number} sm 4
19508  -md {number} md 5
19509  @cfg {String} graphtype  bar | vbar | pie
19510  @cfg {number} g_x coodinator | centre x (pie)
19511  @cfg {number} g_y coodinator | centre y (pie)
19512  @cfg {number} g_r radius (pie)
19513  @cfg {number} g_height height of the chart (respected by all elements in the set)
19514  @cfg {number} g_width width of the chart (respected by all elements in the set)
19515  @cfg {Object} title The title of the chart
19516     
19517  -{Array}  values
19518  -opts (object) options for the chart 
19519      o {
19520      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19521      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19522      o vgutter (number)
19523      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.
19524      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19525      o to
19526      o stretch (boolean)
19527      o }
19528  -opts (object) options for the pie
19529      o{
19530      o cut
19531      o startAngle (number)
19532      o endAngle (number)
19533      } 
19534  *
19535  * @constructor
19536  * Create a new Input
19537  * @param {Object} config The config object
19538  */
19539
19540 Roo.bootstrap.Graph = function(config){
19541     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19542     
19543     this.addEvents({
19544         // img events
19545         /**
19546          * @event click
19547          * The img click event for the img.
19548          * @param {Roo.EventObject} e
19549          */
19550         "click" : true
19551     });
19552 };
19553
19554 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19555     
19556     sm: 4,
19557     md: 5,
19558     graphtype: 'bar',
19559     g_height: 250,
19560     g_width: 400,
19561     g_x: 50,
19562     g_y: 50,
19563     g_r: 30,
19564     opts:{
19565         //g_colors: this.colors,
19566         g_type: 'soft',
19567         g_gutter: '20%'
19568
19569     },
19570     title : false,
19571
19572     getAutoCreate : function(){
19573         
19574         var cfg = {
19575             tag: 'div',
19576             html : null
19577         }
19578         
19579         
19580         return  cfg;
19581     },
19582
19583     onRender : function(ct,position){
19584         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19585         this.raphael = Raphael(this.el.dom);
19586         
19587                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19588                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19589                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19590                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19591                 /*
19592                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19593                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19594                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19595                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19596                 
19597                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19598                 r.barchart(330, 10, 300, 220, data1);
19599                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19600                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19601                 */
19602                 
19603                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19604                 // r.barchart(30, 30, 560, 250,  xdata, {
19605                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19606                 //     axis : "0 0 1 1",
19607                 //     axisxlabels :  xdata
19608                 //     //yvalues : cols,
19609                    
19610                 // });
19611 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19612 //        
19613 //        this.load(null,xdata,{
19614 //                axis : "0 0 1 1",
19615 //                axisxlabels :  xdata
19616 //                });
19617
19618     },
19619
19620     load : function(graphtype,xdata,opts){
19621         this.raphael.clear();
19622         if(!graphtype) {
19623             graphtype = this.graphtype;
19624         }
19625         if(!opts){
19626             opts = this.opts;
19627         }
19628         var r = this.raphael,
19629             fin = function () {
19630                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19631             },
19632             fout = function () {
19633                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19634             },
19635             pfin = function() {
19636                 this.sector.stop();
19637                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19638
19639                 if (this.label) {
19640                     this.label[0].stop();
19641                     this.label[0].attr({ r: 7.5 });
19642                     this.label[1].attr({ "font-weight": 800 });
19643                 }
19644             },
19645             pfout = function() {
19646                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19647
19648                 if (this.label) {
19649                     this.label[0].animate({ r: 5 }, 500, "bounce");
19650                     this.label[1].attr({ "font-weight": 400 });
19651                 }
19652             };
19653
19654         switch(graphtype){
19655             case 'bar':
19656                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19657                 break;
19658             case 'hbar':
19659                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19660                 break;
19661             case 'pie':
19662 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19663 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19664 //            
19665                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19666                 
19667                 break;
19668
19669         }
19670         
19671         if(this.title){
19672             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19673         }
19674         
19675     },
19676     
19677     setTitle: function(o)
19678     {
19679         this.title = o;
19680     },
19681     
19682     initEvents: function() {
19683         
19684         if(!this.href){
19685             this.el.on('click', this.onClick, this);
19686         }
19687     },
19688     
19689     onClick : function(e)
19690     {
19691         Roo.log('img onclick');
19692         this.fireEvent('click', this, e);
19693     }
19694    
19695 });
19696
19697  
19698 /*
19699  * - LGPL
19700  *
19701  * numberBox
19702  * 
19703  */
19704 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19705
19706 /**
19707  * @class Roo.bootstrap.dash.NumberBox
19708  * @extends Roo.bootstrap.Component
19709  * Bootstrap NumberBox class
19710  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19711  * @cfg {String} headline Box headline
19712  * @cfg {String} content Box content
19713  * @cfg {String} icon Box icon
19714  * @cfg {String} footer Footer text
19715  * @cfg {String} fhref Footer href
19716  * 
19717  * @constructor
19718  * Create a new NumberBox
19719  * @param {Object} config The config object
19720  */
19721
19722
19723 Roo.bootstrap.dash.NumberBox = function(config){
19724     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19725     
19726 };
19727
19728 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19729     
19730     bgcolor : 'aqua',
19731     headline : '',
19732     content : '',
19733     icon : '',
19734     footer : '',
19735     fhref : '',
19736     ficon : '',
19737     
19738     getAutoCreate : function(){
19739         
19740         var cfg = {
19741             tag : 'div',
19742             cls : 'small-box bg-' + this.bgcolor,
19743             cn : [
19744                 {
19745                     tag : 'div',
19746                     cls : 'inner',
19747                     cn :[
19748                         {
19749                             tag : 'h3',
19750                             cls : 'roo-headline',
19751                             html : this.headline
19752                         },
19753                         {
19754                             tag : 'p',
19755                             cls : 'roo-content',
19756                             html : this.content
19757                         }
19758                     ]
19759                 }
19760             ]
19761         }
19762         
19763         if(this.icon){
19764             cfg.cn.push({
19765                 tag : 'div',
19766                 cls : 'icon',
19767                 cn :[
19768                     {
19769                         tag : 'i',
19770                         cls : 'ion ' + this.icon
19771                     }
19772                 ]
19773             });
19774         }
19775         
19776         if(this.footer){
19777             var footer = {
19778                 tag : 'a',
19779                 cls : 'small-box-footer',
19780                 href : this.fhref || '#',
19781                 html : this.footer
19782             };
19783             
19784             cfg.cn.push(footer);
19785             
19786         }
19787         
19788         return  cfg;
19789     },
19790
19791     onRender : function(ct,position){
19792         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19793
19794
19795        
19796                 
19797     },
19798
19799     setHeadline: function (value)
19800     {
19801         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19802     },
19803     
19804     setFooter: function (value, href)
19805     {
19806         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19807         
19808         if(href){
19809             this.el.select('a.small-box-footer',true).first().attr('href', href);
19810         }
19811         
19812     },
19813
19814     setContent: function (value)
19815     {
19816         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19817     },
19818
19819     initEvents: function() 
19820     {   
19821         
19822     }
19823     
19824 });
19825
19826  
19827 /*
19828  * - LGPL
19829  *
19830  * TabBox
19831  * 
19832  */
19833 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19834
19835 /**
19836  * @class Roo.bootstrap.dash.TabBox
19837  * @extends Roo.bootstrap.Component
19838  * Bootstrap TabBox class
19839  * @cfg {String} title Title of the TabBox
19840  * @cfg {String} icon Icon of the TabBox
19841  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19842  * 
19843  * @constructor
19844  * Create a new TabBox
19845  * @param {Object} config The config object
19846  */
19847
19848
19849 Roo.bootstrap.dash.TabBox = function(config){
19850     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19851     this.addEvents({
19852         // raw events
19853         /**
19854          * @event addpane
19855          * When a pane is added
19856          * @param {Roo.bootstrap.dash.TabPane} pane
19857          */
19858         "addpane" : true
19859          
19860     });
19861 };
19862
19863 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19864
19865     title : '',
19866     icon : false,
19867     showtabs : true,
19868     
19869     getChildContainer : function()
19870     {
19871         return this.el.select('.tab-content', true).first();
19872     },
19873     
19874     getAutoCreate : function(){
19875         
19876         var header = {
19877             tag: 'li',
19878             cls: 'pull-left header',
19879             html: this.title,
19880             cn : []
19881         };
19882         
19883         if(this.icon){
19884             header.cn.push({
19885                 tag: 'i',
19886                 cls: 'fa ' + this.icon
19887             });
19888         }
19889         
19890         
19891         var cfg = {
19892             tag: 'div',
19893             cls: 'nav-tabs-custom',
19894             cn: [
19895                 {
19896                     tag: 'ul',
19897                     cls: 'nav nav-tabs pull-right',
19898                     cn: [
19899                         header
19900                     ]
19901                 },
19902                 {
19903                     tag: 'div',
19904                     cls: 'tab-content no-padding',
19905                     cn: []
19906                 }
19907             ]
19908         }
19909
19910         return  cfg;
19911     },
19912     initEvents : function()
19913     {
19914         //Roo.log('add add pane handler');
19915         this.on('addpane', this.onAddPane, this);
19916     },
19917      /**
19918      * Updates the box title
19919      * @param {String} html to set the title to.
19920      */
19921     setTitle : function(value)
19922     {
19923         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19924     },
19925     onAddPane : function(pane)
19926     {
19927         //Roo.log('addpane');
19928         //Roo.log(pane);
19929         // tabs are rendere left to right..
19930         if(!this.showtabs){
19931             return;
19932         }
19933         
19934         var ctr = this.el.select('.nav-tabs', true).first();
19935          
19936          
19937         var existing = ctr.select('.nav-tab',true);
19938         var qty = existing.getCount();;
19939         
19940         
19941         var tab = ctr.createChild({
19942             tag : 'li',
19943             cls : 'nav-tab' + (qty ? '' : ' active'),
19944             cn : [
19945                 {
19946                     tag : 'a',
19947                     href:'#',
19948                     html : pane.title
19949                 }
19950             ]
19951         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19952         pane.tab = tab;
19953         
19954         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19955         if (!qty) {
19956             pane.el.addClass('active');
19957         }
19958         
19959                 
19960     },
19961     onTabClick : function(ev,un,ob,pane)
19962     {
19963         //Roo.log('tab - prev default');
19964         ev.preventDefault();
19965         
19966         
19967         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19968         pane.tab.addClass('active');
19969         //Roo.log(pane.title);
19970         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19971         // technically we should have a deactivate event.. but maybe add later.
19972         // and it should not de-activate the selected tab...
19973         
19974         pane.el.addClass('active');
19975         pane.fireEvent('activate');
19976         
19977         
19978     }
19979     
19980     
19981 });
19982
19983  
19984 /*
19985  * - LGPL
19986  *
19987  * Tab pane
19988  * 
19989  */
19990 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19991 /**
19992  * @class Roo.bootstrap.TabPane
19993  * @extends Roo.bootstrap.Component
19994  * Bootstrap TabPane class
19995  * @cfg {Boolean} active (false | true) Default false
19996  * @cfg {String} title title of panel
19997
19998  * 
19999  * @constructor
20000  * Create a new TabPane
20001  * @param {Object} config The config object
20002  */
20003
20004 Roo.bootstrap.dash.TabPane = function(config){
20005     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20006     
20007     this.addEvents({
20008         // raw events
20009         /**
20010          * @event activate
20011          * When a pane is activated
20012          * @param {Roo.bootstrap.dash.TabPane} pane
20013          */
20014         "activate" : true
20015          
20016     });
20017 };
20018
20019 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20020     
20021     active : false,
20022     title : '',
20023     
20024     // the tabBox that this is attached to.
20025     tab : false,
20026      
20027     getAutoCreate : function() 
20028     {
20029         var cfg = {
20030             tag: 'div',
20031             cls: 'tab-pane'
20032         }
20033         
20034         if(this.active){
20035             cfg.cls += ' active';
20036         }
20037         
20038         return cfg;
20039     },
20040     initEvents  : function()
20041     {
20042         //Roo.log('trigger add pane handler');
20043         this.parent().fireEvent('addpane', this)
20044     },
20045     
20046      /**
20047      * Updates the tab title 
20048      * @param {String} html to set the title to.
20049      */
20050     setTitle: function(str)
20051     {
20052         if (!this.tab) {
20053             return;
20054         }
20055         this.title = str;
20056         this.tab.select('a', true).first().dom.innerHTML = str;
20057         
20058     }
20059     
20060     
20061     
20062 });
20063
20064  
20065
20066
20067  /*
20068  * - LGPL
20069  *
20070  * menu
20071  * 
20072  */
20073 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20074
20075 /**
20076  * @class Roo.bootstrap.menu.Menu
20077  * @extends Roo.bootstrap.Component
20078  * Bootstrap Menu class - container for Menu
20079  * @cfg {String} html Text of the menu
20080  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20081  * @cfg {String} icon Font awesome icon
20082  * @cfg {String} pos Menu align to (top | bottom) default bottom
20083  * 
20084  * 
20085  * @constructor
20086  * Create a new Menu
20087  * @param {Object} config The config object
20088  */
20089
20090
20091 Roo.bootstrap.menu.Menu = function(config){
20092     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20093     
20094     this.addEvents({
20095         /**
20096          * @event beforeshow
20097          * Fires before this menu is displayed
20098          * @param {Roo.bootstrap.menu.Menu} this
20099          */
20100         beforeshow : true,
20101         /**
20102          * @event beforehide
20103          * Fires before this menu is hidden
20104          * @param {Roo.bootstrap.menu.Menu} this
20105          */
20106         beforehide : true,
20107         /**
20108          * @event show
20109          * Fires after this menu is displayed
20110          * @param {Roo.bootstrap.menu.Menu} this
20111          */
20112         show : true,
20113         /**
20114          * @event hide
20115          * Fires after this menu is hidden
20116          * @param {Roo.bootstrap.menu.Menu} this
20117          */
20118         hide : true,
20119         /**
20120          * @event click
20121          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20122          * @param {Roo.bootstrap.menu.Menu} this
20123          * @param {Roo.EventObject} e
20124          */
20125         click : true
20126     });
20127     
20128 };
20129
20130 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20131     
20132     submenu : false,
20133     html : '',
20134     weight : 'default',
20135     icon : false,
20136     pos : 'bottom',
20137     
20138     
20139     getChildContainer : function() {
20140         if(this.isSubMenu){
20141             return this.el;
20142         }
20143         
20144         return this.el.select('ul.dropdown-menu', true).first();  
20145     },
20146     
20147     getAutoCreate : function()
20148     {
20149         var text = [
20150             {
20151                 tag : 'span',
20152                 cls : 'roo-menu-text',
20153                 html : this.html
20154             }
20155         ];
20156         
20157         if(this.icon){
20158             text.unshift({
20159                 tag : 'i',
20160                 cls : 'fa ' + this.icon
20161             })
20162         }
20163         
20164         
20165         var cfg = {
20166             tag : 'div',
20167             cls : 'btn-group',
20168             cn : [
20169                 {
20170                     tag : 'button',
20171                     cls : 'dropdown-button btn btn-' + this.weight,
20172                     cn : text
20173                 },
20174                 {
20175                     tag : 'button',
20176                     cls : 'dropdown-toggle btn btn-' + this.weight,
20177                     cn : [
20178                         {
20179                             tag : 'span',
20180                             cls : 'caret'
20181                         }
20182                     ]
20183                 },
20184                 {
20185                     tag : 'ul',
20186                     cls : 'dropdown-menu'
20187                 }
20188             ]
20189             
20190         };
20191         
20192         if(this.pos == 'top'){
20193             cfg.cls += ' dropup';
20194         }
20195         
20196         if(this.isSubMenu){
20197             cfg = {
20198                 tag : 'ul',
20199                 cls : 'dropdown-menu'
20200             }
20201         }
20202         
20203         return cfg;
20204     },
20205     
20206     onRender : function(ct, position)
20207     {
20208         this.isSubMenu = ct.hasClass('dropdown-submenu');
20209         
20210         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20211     },
20212     
20213     initEvents : function() 
20214     {
20215         if(this.isSubMenu){
20216             return;
20217         }
20218         
20219         this.hidden = true;
20220         
20221         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20222         this.triggerEl.on('click', this.onTriggerPress, this);
20223         
20224         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20225         this.buttonEl.on('click', this.onClick, this);
20226         
20227     },
20228     
20229     list : function()
20230     {
20231         if(this.isSubMenu){
20232             return this.el;
20233         }
20234         
20235         return this.el.select('ul.dropdown-menu', true).first();
20236     },
20237     
20238     onClick : function(e)
20239     {
20240         this.fireEvent("click", this, e);
20241     },
20242     
20243     onTriggerPress  : function(e)
20244     {   
20245         if (this.isVisible()) {
20246             this.hide();
20247         } else {
20248             this.show();
20249         }
20250     },
20251     
20252     isVisible : function(){
20253         return !this.hidden;
20254     },
20255     
20256     show : function()
20257     {
20258         this.fireEvent("beforeshow", this);
20259         
20260         this.hidden = false;
20261         this.el.addClass('open');
20262         
20263         Roo.get(document).on("mouseup", this.onMouseUp, this);
20264         
20265         this.fireEvent("show", this);
20266         
20267         
20268     },
20269     
20270     hide : function()
20271     {
20272         this.fireEvent("beforehide", this);
20273         
20274         this.hidden = true;
20275         this.el.removeClass('open');
20276         
20277         Roo.get(document).un("mouseup", this.onMouseUp);
20278         
20279         this.fireEvent("hide", this);
20280     },
20281     
20282     onMouseUp : function()
20283     {
20284         this.hide();
20285     }
20286     
20287 });
20288
20289  
20290  /*
20291  * - LGPL
20292  *
20293  * menu item
20294  * 
20295  */
20296 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20297
20298 /**
20299  * @class Roo.bootstrap.menu.Item
20300  * @extends Roo.bootstrap.Component
20301  * Bootstrap MenuItem class
20302  * @cfg {Boolean} submenu (true | false) default false
20303  * @cfg {String} html text of the item
20304  * @cfg {String} href the link
20305  * @cfg {Boolean} disable (true | false) default false
20306  * @cfg {Boolean} preventDefault (true | false) default true
20307  * @cfg {String} icon Font awesome icon
20308  * @cfg {String} pos Submenu align to (left | right) default right 
20309  * 
20310  * 
20311  * @constructor
20312  * Create a new Item
20313  * @param {Object} config The config object
20314  */
20315
20316
20317 Roo.bootstrap.menu.Item = function(config){
20318     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20319     this.addEvents({
20320         /**
20321          * @event mouseover
20322          * Fires when the mouse is hovering over this menu
20323          * @param {Roo.bootstrap.menu.Item} this
20324          * @param {Roo.EventObject} e
20325          */
20326         mouseover : true,
20327         /**
20328          * @event mouseout
20329          * Fires when the mouse exits this menu
20330          * @param {Roo.bootstrap.menu.Item} this
20331          * @param {Roo.EventObject} e
20332          */
20333         mouseout : true,
20334         // raw events
20335         /**
20336          * @event click
20337          * The raw click event for the entire grid.
20338          * @param {Roo.EventObject} e
20339          */
20340         click : true
20341     });
20342 };
20343
20344 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20345     
20346     submenu : false,
20347     href : '',
20348     html : '',
20349     preventDefault: true,
20350     disable : false,
20351     icon : false,
20352     pos : 'right',
20353     
20354     getAutoCreate : function()
20355     {
20356         var text = [
20357             {
20358                 tag : 'span',
20359                 cls : 'roo-menu-item-text',
20360                 html : this.html
20361             }
20362         ];
20363         
20364         if(this.icon){
20365             text.unshift({
20366                 tag : 'i',
20367                 cls : 'fa ' + this.icon
20368             })
20369         }
20370         
20371         var cfg = {
20372             tag : 'li',
20373             cn : [
20374                 {
20375                     tag : 'a',
20376                     href : this.href || '#',
20377                     cn : text
20378                 }
20379             ]
20380         };
20381         
20382         if(this.disable){
20383             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20384         }
20385         
20386         if(this.submenu){
20387             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20388             
20389             if(this.pos == 'left'){
20390                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20391             }
20392         }
20393         
20394         return cfg;
20395     },
20396     
20397     initEvents : function() 
20398     {
20399         this.el.on('mouseover', this.onMouseOver, this);
20400         this.el.on('mouseout', this.onMouseOut, this);
20401         
20402         this.el.select('a', true).first().on('click', this.onClick, this);
20403         
20404     },
20405     
20406     onClick : function(e)
20407     {
20408         if(this.preventDefault){
20409             e.preventDefault();
20410         }
20411         
20412         this.fireEvent("click", this, e);
20413     },
20414     
20415     onMouseOver : function(e)
20416     {
20417         if(this.submenu && this.pos == 'left'){
20418             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20419         }
20420         
20421         this.fireEvent("mouseover", this, e);
20422     },
20423     
20424     onMouseOut : function(e)
20425     {
20426         this.fireEvent("mouseout", this, e);
20427     }
20428 });
20429
20430  
20431
20432  /*
20433  * - LGPL
20434  *
20435  * menu separator
20436  * 
20437  */
20438 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20439
20440 /**
20441  * @class Roo.bootstrap.menu.Separator
20442  * @extends Roo.bootstrap.Component
20443  * Bootstrap Separator class
20444  * 
20445  * @constructor
20446  * Create a new Separator
20447  * @param {Object} config The config object
20448  */
20449
20450
20451 Roo.bootstrap.menu.Separator = function(config){
20452     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20453 };
20454
20455 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20456     
20457     getAutoCreate : function(){
20458         var cfg = {
20459             tag : 'li',
20460             cls: 'divider'
20461         };
20462         
20463         return cfg;
20464     }
20465    
20466 });
20467
20468  
20469
20470