Roo/form/ComboBoxArray.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192          skip_children = false;
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 
214                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
215                 // and are not displayed -this causes this to use up the wrong element when matching.
216                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
217                 
218                 
219                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
220                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
221                   
222                   
223                   
224                     cn.el = echild;
225                   //  Roo.log("GOT");
226                     //echild.dom.removeAttribute('xtype');
227                 } else {
228                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229                     Roo.log(self_cntr_el);
230                     Roo.log(echild);
231                     Roo.log(cn);
232                 }
233             }
234            
235             
236            
237             // if object has flexy:if - then it may or may not be rendered.
238             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
239                 // skip a flexy if element.
240                 Roo.log('skipping render');
241                 Roo.log(tree);
242                 if (!cn.el) {
243                     Roo.log('skipping all children');
244                     skip_children = true;
245                 }
246                 
247              } else {
248                  
249                 // actually if flexy:foreach is found, we really want to create 
250                 // multiple copies here...
251                 //Roo.log('render');
252                 //Roo.log(this[cntr]());
253                 cn.render(this[cntr](true));
254              }
255             // then add the element..
256         }
257         
258         
259         // handle the kids..
260         
261         var nitems = [];
262         /*
263         if (typeof (tree.menu) != 'undefined') {
264             tree.menu.parentType = cn.xtype;
265             tree.menu.triggerEl = cn.el;
266             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
267             
268         }
269         */
270         if (!tree.items || !tree.items.length) {
271             cn.items = nitems;
272             return cn;
273         }
274         var items = tree.items;
275         delete tree.items;
276         
277         //Roo.log(items.length);
278             // add the items..
279         if (!skip_children) {    
280             for(var i =0;i < items.length;i++) {
281                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
282             }
283         }
284         
285         cn.items = nitems;
286         
287         return cn;
288     }
289     
290     
291     
292     
293 });
294
295  /*
296  * - LGPL
297  *
298  * Body
299  * 
300  */
301
302 /**
303  * @class Roo.bootstrap.Body
304  * @extends Roo.bootstrap.Component
305  * Bootstrap Body class
306  * 
307  * @constructor
308  * Create a new body
309  * @param {Object} config The config object
310  */
311
312 Roo.bootstrap.Body = function(config){
313     Roo.bootstrap.Body.superclass.constructor.call(this, config);
314     this.el = Roo.get(document.body);
315     if (this.cls && this.cls.length) {
316         Roo.get(document.body).addClass(this.cls);
317     }
318 };
319
320 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
321       
322         autoCreate : {
323         cls: 'container'
324     },
325     onRender : function(ct, position)
326     {
327        /* Roo.log("Roo.bootstrap.Body - onRender");
328         if (this.cls && this.cls.length) {
329             Roo.get(document.body).addClass(this.cls);
330         }
331         // style??? xttr???
332         */
333     }
334     
335     
336  
337    
338 });
339
340  /*
341  * - LGPL
342  *
343  * button group
344  * 
345  */
346
347
348 /**
349  * @class Roo.bootstrap.ButtonGroup
350  * @extends Roo.bootstrap.Component
351  * Bootstrap ButtonGroup class
352  * @cfg {String} size lg | sm | xs (default empty normal)
353  * @cfg {String} align vertical | justified  (default none)
354  * @cfg {String} direction up | down (default down)
355  * @cfg {Boolean} toolbar false | true
356  * @cfg {Boolean} btn true | false
357  * 
358  * 
359  * @constructor
360  * Create a new Input
361  * @param {Object} config The config object
362  */
363
364 Roo.bootstrap.ButtonGroup = function(config){
365     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
366 };
367
368 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
369     
370     size: '',
371     align: '',
372     direction: '',
373     toolbar: false,
374     btn: true,
375
376     getAutoCreate : function(){
377         var cfg = {
378             cls: 'btn-group',
379             html : null
380         }
381         
382         cfg.html = this.html || cfg.html;
383         
384         if (this.toolbar) {
385             cfg = {
386                 cls: 'btn-toolbar',
387                 html: null
388             }
389             
390             return cfg;
391         }
392         
393         if (['vertical','justified'].indexOf(this.align)!==-1) {
394             cfg.cls = 'btn-group-' + this.align;
395             
396             if (this.align == 'justified') {
397                 console.log(this.items);
398             }
399         }
400         
401         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
402             cfg.cls += ' btn-group-' + this.size;
403         }
404         
405         if (this.direction == 'up') {
406             cfg.cls += ' dropup' ;
407         }
408         
409         return cfg;
410     }
411    
412 });
413
414  /*
415  * - LGPL
416  *
417  * button
418  * 
419  */
420
421 /**
422  * @class Roo.bootstrap.Button
423  * @extends Roo.bootstrap.Component
424  * Bootstrap Button class
425  * @cfg {String} html The button content
426  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
427  * @cfg {String} size empty | lg | sm | xs
428  * @cfg {String} tag empty | a | input | submit
429  * @cfg {String} href empty or href
430  * @cfg {Boolean} disabled false | true
431  * @cfg {Boolean} isClose false | true
432  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
433  * @cfg {String} badge text for badge
434  * @cfg {String} theme default (or empty) | glow
435  * @cfg {Boolean} inverse false | true
436  * @cfg {Boolean} toggle false | true
437  * @cfg {String} ontext text for on toggle state
438  * @cfg {String} offtext text for off toggle state
439  * @cfg {Boolean} defaulton true | false
440  * @cfg {Boolean} preventDefault (true | false) default true
441  * @cfg {Boolean} removeClass true | false remove the standard class..
442  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
443  * 
444  * @constructor
445  * Create a new button
446  * @param {Object} config The config object
447  */
448
449
450 Roo.bootstrap.Button = function(config){
451     Roo.bootstrap.Button.superclass.constructor.call(this, config);
452     this.addEvents({
453         // raw events
454         /**
455          * @event click
456          * When a butotn is pressed
457          * @param {Roo.EventObject} e
458          */
459         "click" : true,
460          /**
461          * @event toggle
462          * After the button has been toggles
463          * @param {Roo.EventObject} e
464          * @param {boolean} pressed (also available as button.pressed)
465          */
466         "toggle" : true
467     });
468 };
469
470 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
471     html: false,
472     active: false,
473     weight: '',
474     size: '',
475     tag: 'button',
476     href: '',
477     disabled: false,
478     isClose: false,
479     glyphicon: '',
480     badge: '',
481     theme: 'default',
482     inverse: false,
483     
484     toggle: false,
485     ontext: 'ON',
486     offtext: 'OFF',
487     defaulton: true,
488     preventDefault: true,
489     removeClass: false,
490     name: false,
491     target: false,
492     
493     
494     pressed : null,
495      
496     
497     getAutoCreate : function(){
498         
499         var cfg = {
500             tag : 'button',
501             cls : 'roo-button',
502             html: ''
503         };
504         
505         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
506             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
507             this.tag = 'button';
508         } else {
509             cfg.tag = this.tag;
510         }
511         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
512         
513         if (this.toggle == true) {
514             cfg={
515                 tag: 'div',
516                 cls: 'slider-frame roo-button',
517                 cn: [
518                     {
519                         tag: 'span',
520                         'data-on-text':'ON',
521                         'data-off-text':'OFF',
522                         cls: 'slider-button',
523                         html: this.offtext
524                     }
525                 ]
526             };
527             
528             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
529                 cfg.cls += ' '+this.weight;
530             }
531             
532             return cfg;
533         }
534         
535         if (this.isClose) {
536             cfg.cls += ' close';
537             
538             cfg["aria-hidden"] = true;
539             
540             cfg.html = "&times;";
541             
542             return cfg;
543         }
544         
545          
546         if (this.theme==='default') {
547             cfg.cls = 'btn roo-button';
548             
549             //if (this.parentType != 'Navbar') {
550             this.weight = this.weight.length ?  this.weight : 'default';
551             //}
552             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
553                 
554                 cfg.cls += ' btn-' + this.weight;
555             }
556         } else if (this.theme==='glow') {
557             
558             cfg.tag = 'a';
559             cfg.cls = 'btn-glow roo-button';
560             
561             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
562                 
563                 cfg.cls += ' ' + this.weight;
564             }
565         }
566    
567         
568         if (this.inverse) {
569             this.cls += ' inverse';
570         }
571         
572         
573         if (this.active) {
574             cfg.cls += ' active';
575         }
576         
577         if (this.disabled) {
578             cfg.disabled = 'disabled';
579         }
580         
581         if (this.items) {
582             Roo.log('changing to ul' );
583             cfg.tag = 'ul';
584             this.glyphicon = 'caret';
585         }
586         
587         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
588          
589         //gsRoo.log(this.parentType);
590         if (this.parentType === 'Navbar' && !this.parent().bar) {
591             Roo.log('changing to li?');
592             
593             cfg.tag = 'li';
594             
595             cfg.cls = '';
596             cfg.cn =  [{
597                 tag : 'a',
598                 cls : 'roo-button',
599                 html : this.html,
600                 href : this.href || '#'
601             }];
602             if (this.menu) {
603                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
604                 cfg.cls += ' dropdown';
605             }   
606             
607             delete cfg.html;
608             
609         }
610         
611        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
612         
613         if (this.glyphicon) {
614             cfg.html = ' ' + cfg.html;
615             
616             cfg.cn = [
617                 {
618                     tag: 'span',
619                     cls: 'glyphicon glyphicon-' + this.glyphicon
620                 }
621             ];
622         }
623         
624         if (this.badge) {
625             cfg.html += ' ';
626             
627             cfg.tag = 'a';
628             
629 //            cfg.cls='btn roo-button';
630             
631             cfg.href=this.href;
632             
633             var value = cfg.html;
634             
635             if(this.glyphicon){
636                 value = {
637                             tag: 'span',
638                             cls: 'glyphicon glyphicon-' + this.glyphicon,
639                             html: this.html
640                         };
641                 
642             }
643             
644             cfg.cn = [
645                 value,
646                 {
647                     tag: 'span',
648                     cls: 'badge',
649                     html: this.badge
650                 }
651             ];
652             
653             cfg.html='';
654         }
655         
656         if (this.menu) {
657             cfg.cls += ' dropdown';
658             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
659         }
660         
661         if (cfg.tag !== 'a' && this.href !== '') {
662             throw "Tag must be a to set href.";
663         } else if (this.href.length > 0) {
664             cfg.href = this.href;
665         }
666         
667         if(this.removeClass){
668             cfg.cls = '';
669         }
670         
671         if(this.target){
672             cfg.target = this.target;
673         }
674         
675         return cfg;
676     },
677     initEvents: function() {
678        // Roo.log('init events?');
679 //        Roo.log(this.el.dom);
680         // add the menu...
681         
682         if (typeof (this.menu) != 'undefined') {
683             this.menu.parentType = this.xtype;
684             this.menu.triggerEl = this.el;
685             this.addxtype(Roo.apply({}, this.menu));
686         }
687
688
689        if (this.el.hasClass('roo-button')) {
690             this.el.on('click', this.onClick, this);
691        } else {
692             this.el.select('.roo-button').on('click', this.onClick, this);
693        }
694        
695        if(this.removeClass){
696            this.el.on('click', this.onClick, this);
697        }
698        
699        this.el.enableDisplayMode();
700         
701     },
702     onClick : function(e)
703     {
704         if (this.disabled) {
705             return;
706         }
707         
708         Roo.log('button on click ');
709         if(this.preventDefault){
710             e.preventDefault();
711         }
712         if (this.pressed === true || this.pressed === false) {
713             this.pressed = !this.pressed;
714             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
715             this.fireEvent('toggle', this, e, this.pressed);
716         }
717         
718         
719         this.fireEvent('click', this, e);
720     },
721     
722     /**
723      * Enables this button
724      */
725     enable : function()
726     {
727         this.disabled = false;
728         this.el.removeClass('disabled');
729     },
730     
731     /**
732      * Disable this button
733      */
734     disable : function()
735     {
736         this.disabled = true;
737         this.el.addClass('disabled');
738     },
739      /**
740      * sets the active state on/off, 
741      * @param {Boolean} state (optional) Force a particular state
742      */
743     setActive : function(v) {
744         
745         this.el[v ? 'addClass' : 'removeClass']('active');
746     },
747      /**
748      * toggles the current active state 
749      */
750     toggleActive : function()
751     {
752        var active = this.el.hasClass('active');
753        this.setActive(!active);
754        
755         
756     },
757     setText : function(str)
758     {
759         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
760     },
761     getText : function()
762     {
763         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
764     },
765     hide: function() {
766        
767      
768         this.el.hide();   
769     },
770     show: function() {
771        
772         this.el.show();   
773     }
774     
775     
776 });
777
778  /*
779  * - LGPL
780  *
781  * column
782  * 
783  */
784
785 /**
786  * @class Roo.bootstrap.Column
787  * @extends Roo.bootstrap.Component
788  * Bootstrap Column class
789  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
790  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
791  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
792  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
793  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
794  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
795  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
796  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
797  *
798  * 
799  * @cfg {Boolean} hidden (true|false) hide the element
800  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
801  * @cfg {String} fa (ban|check|...) font awesome icon
802  * @cfg {Number} fasize (1|2|....) font awsome size
803
804  * @cfg {String} icon (info-sign|check|...) glyphicon name
805
806  * @cfg {String} html content of column.
807  * 
808  * @constructor
809  * Create a new Column
810  * @param {Object} config The config object
811  */
812
813 Roo.bootstrap.Column = function(config){
814     Roo.bootstrap.Column.superclass.constructor.call(this, config);
815 };
816
817 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
818     
819     xs: false,
820     sm: false,
821     md: false,
822     lg: false,
823     xsoff: false,
824     smoff: false,
825     mdoff: false,
826     lgoff: false,
827     html: '',
828     offset: 0,
829     alert: false,
830     fa: false,
831     icon : false,
832     hidden : false,
833     fasize : 1,
834     
835     getAutoCreate : function(){
836         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
837         
838         cfg = {
839             tag: 'div',
840             cls: 'column'
841         };
842         
843         var settings=this;
844         ['xs','sm','md','lg'].map(function(size){
845             //Roo.log( size + ':' + settings[size]);
846             
847             if (settings[size+'off'] !== false) {
848                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
849             }
850             
851             if (settings[size] === false) {
852                 return;
853             }
854             Roo.log(settings[size]);
855             if (!settings[size]) { // 0 = hidden
856                 cfg.cls += ' hidden-' + size;
857                 return;
858             }
859             cfg.cls += ' col-' + size + '-' + settings[size];
860             
861         });
862         
863         if (this.hidden) {
864             cfg.cls += ' hidden';
865         }
866         
867         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
868             cfg.cls +=' alert alert-' + this.alert;
869         }
870         
871         
872         if (this.html.length) {
873             cfg.html = this.html;
874         }
875         if (this.fa) {
876             var fasize = '';
877             if (this.fasize > 1) {
878                 fasize = ' fa-' + this.fasize + 'x';
879             }
880             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
881             
882             
883         }
884         if (this.icon) {
885             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
886         }
887         
888         return cfg;
889     }
890    
891 });
892
893  
894
895  /*
896  * - LGPL
897  *
898  * page container.
899  * 
900  */
901
902
903 /**
904  * @class Roo.bootstrap.Container
905  * @extends Roo.bootstrap.Component
906  * Bootstrap Container class
907  * @cfg {Boolean} jumbotron is it a jumbotron element
908  * @cfg {String} html content of element
909  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
910  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
911  * @cfg {String} header content of header (for panel)
912  * @cfg {String} footer content of footer (for panel)
913  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
914  * @cfg {String} tag (header|aside|section) type of HTML tag.
915  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
916  * @cfg {String} fa (ban|check|...) font awesome icon
917  * @cfg {String} icon (info-sign|check|...) glyphicon name
918  * @cfg {Boolean} hidden (true|false) hide the element
919
920  *     
921  * @constructor
922  * Create a new Container
923  * @param {Object} config The config object
924  */
925
926 Roo.bootstrap.Container = function(config){
927     Roo.bootstrap.Container.superclass.constructor.call(this, config);
928 };
929
930 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
931     
932     jumbotron : false,
933     well: '',
934     panel : '',
935     header: '',
936     footer : '',
937     sticky: '',
938     tag : false,
939     alert : false,
940     fa: false,
941     icon : false,
942   
943      
944     getChildContainer : function() {
945         
946         if(!this.el){
947             return false;
948         }
949         
950         if (this.panel.length) {
951             return this.el.select('.panel-body',true).first();
952         }
953         
954         return this.el;
955     },
956     
957     
958     getAutoCreate : function(){
959         
960         var cfg = {
961             tag : this.tag || 'div',
962             html : '',
963             cls : ''
964         };
965         if (this.jumbotron) {
966             cfg.cls = 'jumbotron';
967         }
968         
969         
970         
971         // - this is applied by the parent..
972         //if (this.cls) {
973         //    cfg.cls = this.cls + '';
974         //}
975         
976         if (this.sticky.length) {
977             
978             var bd = Roo.get(document.body);
979             if (!bd.hasClass('bootstrap-sticky')) {
980                 bd.addClass('bootstrap-sticky');
981                 Roo.select('html',true).setStyle('height', '100%');
982             }
983              
984             cfg.cls += 'bootstrap-sticky-' + this.sticky;
985         }
986         
987         
988         if (this.well.length) {
989             switch (this.well) {
990                 case 'lg':
991                 case 'sm':
992                     cfg.cls +=' well well-' +this.well;
993                     break;
994                 default:
995                     cfg.cls +=' well';
996                     break;
997             }
998         }
999         
1000         if (this.hidden) {
1001             cfg.cls += ' hidden';
1002         }
1003         
1004         
1005         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1006             cfg.cls +=' alert alert-' + this.alert;
1007         }
1008         
1009         var body = cfg;
1010         
1011         if (this.panel.length) {
1012             cfg.cls += ' panel panel-' + this.panel;
1013             cfg.cn = [];
1014             if (this.header.length) {
1015                 cfg.cn.push({
1016                     
1017                     cls : 'panel-heading',
1018                     cn : [{
1019                         tag: 'h3',
1020                         cls : 'panel-title',
1021                         html : this.header
1022                     }]
1023                     
1024                 });
1025             }
1026             body = false;
1027             cfg.cn.push({
1028                 cls : 'panel-body',
1029                 html : this.html
1030             });
1031             
1032             
1033             if (this.footer.length) {
1034                 cfg.cn.push({
1035                     cls : 'panel-footer',
1036                     html : this.footer
1037                     
1038                 });
1039             }
1040             
1041         }
1042         
1043         if (body) {
1044             body.html = this.html || cfg.html;
1045             // prefix with the icons..
1046             if (this.fa) {
1047                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1048             }
1049             if (this.icon) {
1050                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1051             }
1052             
1053             
1054         }
1055         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1056             cfg.cls =  'container';
1057         }
1058         
1059         return cfg;
1060     },
1061     
1062     titleEl : function()
1063     {
1064         if(!this.el || !this.panel.length || !this.header.length){
1065             return;
1066         }
1067         
1068         return this.el.select('.panel-title',true).first();
1069     },
1070     
1071     setTitle : function(v)
1072     {
1073         var titleEl = this.titleEl();
1074         
1075         if(!titleEl){
1076             return;
1077         }
1078         
1079         titleEl.dom.innerHTML = v;
1080     },
1081     
1082     getTitle : function()
1083     {
1084         
1085         var titleEl = this.titleEl();
1086         
1087         if(!titleEl){
1088             return '';
1089         }
1090         
1091         return titleEl.dom.innerHTML;
1092     }
1093    
1094 });
1095
1096  /*
1097  * - LGPL
1098  *
1099  * image
1100  * 
1101  */
1102
1103
1104 /**
1105  * @class Roo.bootstrap.Img
1106  * @extends Roo.bootstrap.Component
1107  * Bootstrap Img class
1108  * @cfg {Boolean} imgResponsive false | true
1109  * @cfg {String} border rounded | circle | thumbnail
1110  * @cfg {String} src image source
1111  * @cfg {String} alt image alternative text
1112  * @cfg {String} href a tag href
1113  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1114  * 
1115  * @constructor
1116  * Create a new Input
1117  * @param {Object} config The config object
1118  */
1119
1120 Roo.bootstrap.Img = function(config){
1121     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1122     
1123     this.addEvents({
1124         // img events
1125         /**
1126          * @event click
1127          * The img click event for the img.
1128          * @param {Roo.EventObject} e
1129          */
1130         "click" : true
1131     });
1132 };
1133
1134 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1135     
1136     imgResponsive: true,
1137     border: '',
1138     src: '',
1139     href: false,
1140     target: false,
1141
1142     getAutoCreate : function(){
1143         
1144         var cfg = {
1145             tag: 'img',
1146             cls: (this.imgResponsive) ? 'img-responsive' : '',
1147             html : null
1148         }
1149         
1150         cfg.html = this.html || cfg.html;
1151         
1152         cfg.src = this.src || cfg.src;
1153         
1154         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1155             cfg.cls += ' img-' + this.border;
1156         }
1157         
1158         if(this.alt){
1159             cfg.alt = this.alt;
1160         }
1161         
1162         if(this.href){
1163             var a = {
1164                 tag: 'a',
1165                 href: this.href,
1166                 cn: [
1167                     cfg
1168                 ]
1169             }
1170             
1171             if(this.target){
1172                 a.target = this.target;
1173             }
1174             
1175         }
1176         
1177         
1178         return (this.href) ? a : cfg;
1179     },
1180     
1181     initEvents: function() {
1182         
1183         if(!this.href){
1184             this.el.on('click', this.onClick, this);
1185         }
1186     },
1187     
1188     onClick : function(e)
1189     {
1190         Roo.log('img onclick');
1191         this.fireEvent('click', this, e);
1192     }
1193    
1194 });
1195
1196  /*
1197  * - LGPL
1198  *
1199  * image
1200  * 
1201  */
1202
1203
1204 /**
1205  * @class Roo.bootstrap.Link
1206  * @extends Roo.bootstrap.Component
1207  * Bootstrap Link Class
1208  * @cfg {String} alt image alternative text
1209  * @cfg {String} href a tag href
1210  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1211  * @cfg {String} html the content of the link.
1212  * @cfg {String} anchor name for the anchor link
1213
1214  * @cfg {Boolean} preventDefault (true | false) default false
1215
1216  * 
1217  * @constructor
1218  * Create a new Input
1219  * @param {Object} config The config object
1220  */
1221
1222 Roo.bootstrap.Link = function(config){
1223     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1224     
1225     this.addEvents({
1226         // img events
1227         /**
1228          * @event click
1229          * The img click event for the img.
1230          * @param {Roo.EventObject} e
1231          */
1232         "click" : true
1233     });
1234 };
1235
1236 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1237     
1238     href: false,
1239     target: false,
1240     preventDefault: false,
1241     anchor : false,
1242     alt : false,
1243
1244     getAutoCreate : function()
1245     {
1246         
1247         var cfg = {
1248             tag: 'a'
1249         };
1250         // anchor's do not require html/href...
1251         if (this.anchor === false) {
1252             cfg.html = this.html || 'html-missing';
1253             cfg.href = this.href || '#';
1254         } else {
1255             cfg.name = this.anchor;
1256             if (this.html !== false) {
1257                 cfg.html = this.html;
1258             }
1259             if (this.href !== false) {
1260                 cfg.href = this.href;
1261             }
1262         }
1263         
1264         if(this.alt !== false){
1265             cfg.alt = this.alt;
1266         }
1267         
1268         
1269         if(this.target !== false) {
1270             cfg.target = this.target;
1271         }
1272         
1273         return cfg;
1274     },
1275     
1276     initEvents: function() {
1277         
1278         if(!this.href || this.preventDefault){
1279             this.el.on('click', this.onClick, this);
1280         }
1281     },
1282     
1283     onClick : function(e)
1284     {
1285         if(this.preventDefault){
1286             e.preventDefault();
1287         }
1288         //Roo.log('img onclick');
1289         this.fireEvent('click', this, e);
1290     }
1291    
1292 });
1293
1294  /*
1295  * - LGPL
1296  *
1297  * header
1298  * 
1299  */
1300
1301 /**
1302  * @class Roo.bootstrap.Header
1303  * @extends Roo.bootstrap.Component
1304  * Bootstrap Header class
1305  * @cfg {String} html content of header
1306  * @cfg {Number} level (1|2|3|4|5|6) default 1
1307  * 
1308  * @constructor
1309  * Create a new Header
1310  * @param {Object} config The config object
1311  */
1312
1313
1314 Roo.bootstrap.Header  = function(config){
1315     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1316 };
1317
1318 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1319     
1320     //href : false,
1321     html : false,
1322     level : 1,
1323     
1324     
1325     
1326     getAutoCreate : function(){
1327         
1328         var cfg = {
1329             tag: 'h' + (1 *this.level),
1330             html: this.html || 'fill in html'
1331         } ;
1332         
1333         return cfg;
1334     }
1335    
1336 });
1337
1338  
1339
1340  /*
1341  * Based on:
1342  * Ext JS Library 1.1.1
1343  * Copyright(c) 2006-2007, Ext JS, LLC.
1344  *
1345  * Originally Released Under LGPL - original licence link has changed is not relivant.
1346  *
1347  * Fork - LGPL
1348  * <script type="text/javascript">
1349  */
1350  
1351 /**
1352  * @class Roo.bootstrap.MenuMgr
1353  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1354  * @singleton
1355  */
1356 Roo.bootstrap.MenuMgr = function(){
1357    var menus, active, groups = {}, attached = false, lastShow = new Date();
1358
1359    // private - called when first menu is created
1360    function init(){
1361        menus = {};
1362        active = new Roo.util.MixedCollection();
1363        Roo.get(document).addKeyListener(27, function(){
1364            if(active.length > 0){
1365                hideAll();
1366            }
1367        });
1368    }
1369
1370    // private
1371    function hideAll(){
1372        if(active && active.length > 0){
1373            var c = active.clone();
1374            c.each(function(m){
1375                m.hide();
1376            });
1377        }
1378    }
1379
1380    // private
1381    function onHide(m){
1382        active.remove(m);
1383        if(active.length < 1){
1384            Roo.get(document).un("mouseup", onMouseDown);
1385             
1386            attached = false;
1387        }
1388    }
1389
1390    // private
1391    function onShow(m){
1392        var last = active.last();
1393        lastShow = new Date();
1394        active.add(m);
1395        if(!attached){
1396           Roo.get(document).on("mouseup", onMouseDown);
1397            
1398            attached = true;
1399        }
1400        if(m.parentMenu){
1401           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1402           m.parentMenu.activeChild = m;
1403        }else if(last && last.isVisible()){
1404           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1405        }
1406    }
1407
1408    // private
1409    function onBeforeHide(m){
1410        if(m.activeChild){
1411            m.activeChild.hide();
1412        }
1413        if(m.autoHideTimer){
1414            clearTimeout(m.autoHideTimer);
1415            delete m.autoHideTimer;
1416        }
1417    }
1418
1419    // private
1420    function onBeforeShow(m){
1421        var pm = m.parentMenu;
1422        if(!pm && !m.allowOtherMenus){
1423            hideAll();
1424        }else if(pm && pm.activeChild && active != m){
1425            pm.activeChild.hide();
1426        }
1427    }
1428
1429    // private
1430    function onMouseDown(e){
1431         Roo.log("on MouseDown");
1432         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
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     * clear all the Navigation item
3486     */
3487    
3488     clearAll : function()
3489     {
3490         this.navItems = [];
3491         this.el.dom.innerHTML = '';
3492     },
3493     
3494     getNavItem: function(tabId)
3495     {
3496         var ret = false;
3497         Roo.each(this.navItems, function(e) {
3498             if (e.tabId == tabId) {
3499                ret =  e;
3500                return false;
3501             }
3502             return true;
3503             
3504         });
3505         return ret;
3506     },
3507     
3508     setActiveNext : function()
3509     {
3510         var i = this.indexOfNav(this.getActive());
3511         if (i > this.navItems.length) {
3512             return;
3513         }
3514         this.setActiveItem(this.navItems[i+1]);
3515     },
3516     setActivePrev : function()
3517     {
3518         var i = this.indexOfNav(this.getActive());
3519         if (i  < 1) {
3520             return;
3521         }
3522         this.setActiveItem(this.navItems[i-1]);
3523     },
3524     clearWasActive : function(except) {
3525         Roo.each(this.navItems, function(e) {
3526             if (e.tabId != except.tabId && e.was_active) {
3527                e.was_active = false;
3528                return false;
3529             }
3530             return true;
3531             
3532         });
3533     },
3534     getWasActive : function ()
3535     {
3536         var r = false;
3537         Roo.each(this.navItems, function(e) {
3538             if (e.was_active) {
3539                r = e;
3540                return false;
3541             }
3542             return true;
3543             
3544         });
3545         return r;
3546     }
3547     
3548     
3549 });
3550
3551  
3552 Roo.apply(Roo.bootstrap.NavGroup, {
3553     
3554     groups: {},
3555      /**
3556     * register a Navigation Group
3557     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3558     */
3559     register : function(navgrp)
3560     {
3561         this.groups[navgrp.navId] = navgrp;
3562         
3563     },
3564     /**
3565     * fetch a Navigation Group based on the navigation ID
3566     * @param {string} the navgroup to add
3567     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3568     */
3569     get: function(navId) {
3570         if (typeof(this.groups[navId]) == 'undefined') {
3571             return false;
3572             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3573         }
3574         return this.groups[navId] ;
3575     }
3576     
3577     
3578     
3579 });
3580
3581  /*
3582  * - LGPL
3583  *
3584  * row
3585  * 
3586  */
3587
3588 /**
3589  * @class Roo.bootstrap.NavItem
3590  * @extends Roo.bootstrap.Component
3591  * Bootstrap Navbar.NavItem class
3592  * @cfg {String} href  link to
3593  * @cfg {String} html content of button
3594  * @cfg {String} badge text inside badge
3595  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3596  * @cfg {String} glyphicon name of glyphicon
3597  * @cfg {String} icon name of font awesome icon
3598  * @cfg {Boolean} active Is item active
3599  * @cfg {Boolean} disabled Is item disabled
3600  
3601  * @cfg {Boolean} preventDefault (true | false) default false
3602  * @cfg {String} tabId the tab that this item activates.
3603  * @cfg {String} tagtype (a|span) render as a href or span?
3604   
3605  * @constructor
3606  * Create a new Navbar Item
3607  * @param {Object} config The config object
3608  */
3609 Roo.bootstrap.NavItem = function(config){
3610     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3611     this.addEvents({
3612         // raw events
3613         /**
3614          * @event click
3615          * The raw click event for the entire grid.
3616          * @param {Roo.EventObject} e
3617          */
3618         "click" : true,
3619          /**
3620             * @event changed
3621             * Fires when the active item active state changes
3622             * @param {Roo.bootstrap.NavItem} this
3623             * @param {boolean} state the new state
3624              
3625          */
3626         'changed': true
3627     });
3628    
3629 };
3630
3631 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3632     
3633     href: false,
3634     html: '',
3635     badge: '',
3636     icon: false,
3637     glyphicon: false,
3638     active: false,
3639     preventDefault : false,
3640     tabId : false,
3641     tagtype : 'a',
3642     disabled : false,
3643     
3644     was_active : false,
3645     
3646     getAutoCreate : function(){
3647          
3648         var cfg = {
3649             tag: 'li',
3650             cls: 'nav-item'
3651             
3652         }
3653         if (this.active) {
3654             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3655         }
3656         if (this.disabled) {
3657             cfg.cls += ' disabled';
3658         }
3659         
3660         if (this.href || this.html || this.glyphicon || this.icon) {
3661             cfg.cn = [
3662                 {
3663                     tag: this.tagtype,
3664                     href : this.href || "#",
3665                     html: this.html || ''
3666                 }
3667             ];
3668             
3669             if (this.icon) {
3670                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3671             }
3672
3673             if(this.glyphicon) {
3674                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3675             }
3676             
3677             if (this.menu) {
3678                 
3679                 cfg.cn[0].html += " <span class='caret'></span>";
3680              
3681             }
3682             
3683             if (this.badge !== '') {
3684                  
3685                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3686             }
3687         }
3688         
3689         
3690         
3691         return cfg;
3692     },
3693     initEvents: function() {
3694        // Roo.log('init events?');
3695        // Roo.log(this.el.dom);
3696         if (typeof (this.menu) != 'undefined') {
3697             this.menu.parentType = this.xtype;
3698             this.menu.triggerEl = this.el;
3699             this.addxtype(Roo.apply({}, this.menu));
3700         }
3701
3702        
3703         this.el.select('a',true).on('click', this.onClick, this);
3704         // at this point parent should be available..
3705         this.parent().register(this);
3706     },
3707     
3708     onClick : function(e)
3709     {
3710          
3711         if(this.preventDefault){
3712             e.preventDefault();
3713         }
3714         if (this.disabled) {
3715             return;
3716         }
3717         
3718         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3719         if (tg && tg.transition) {
3720             Roo.log("waiting for the transitionend");
3721             return;
3722         }
3723         
3724         Roo.log("fire event clicked");
3725         if(this.fireEvent('click', this, e) === false){
3726             return;
3727         };
3728         var p = this.parent();
3729         if (['tabs','pills'].indexOf(p.type)!==-1) {
3730             if (typeof(p.setActiveItem) !== 'undefined') {
3731                 p.setActiveItem(this);
3732             }
3733         }
3734         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3735         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3736             // remove the collapsed menu expand...
3737             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3738         }
3739         
3740     },
3741     
3742     isActive: function () {
3743         return this.active
3744     },
3745     setActive : function(state, fire, is_was_active)
3746     {
3747         if (this.active && !state & this.navId) {
3748             this.was_active = true;
3749             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3750             if (nv) {
3751                 nv.clearWasActive(this);
3752             }
3753             
3754         }
3755         this.active = state;
3756         
3757         if (!state ) {
3758             this.el.removeClass('active');
3759         } else if (!this.el.hasClass('active')) {
3760             this.el.addClass('active');
3761         }
3762         if (fire) {
3763             this.fireEvent('changed', this, state);
3764         }
3765         
3766         // show a panel if it's registered and related..
3767         
3768         if (!this.navId || !this.tabId || !state || is_was_active) {
3769             return;
3770         }
3771         
3772         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3773         if (!tg) {
3774             return;
3775         }
3776         var pan = tg.getPanelByName(this.tabId);
3777         if (!pan) {
3778             return;
3779         }
3780         // if we can not flip to new panel - go back to old nav highlight..
3781         if (false == tg.showPanel(pan)) {
3782             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3783             if (nv) {
3784                 var onav = nv.getWasActive();
3785                 if (onav) {
3786                     onav.setActive(true, false, true);
3787                 }
3788             }
3789             
3790         }
3791         
3792         
3793         
3794     },
3795      // this should not be here...
3796     setDisabled : function(state)
3797     {
3798         this.disabled = state;
3799         if (!state ) {
3800             this.el.removeClass('disabled');
3801         } else if (!this.el.hasClass('disabled')) {
3802             this.el.addClass('disabled');
3803         }
3804         
3805     }
3806 });
3807  
3808
3809  /*
3810  * - LGPL
3811  *
3812  * sidebar item
3813  *
3814  *  li
3815  *    <span> icon </span>
3816  *    <span> text </span>
3817  *    <span>badge </span>
3818  */
3819
3820 /**
3821  * @class Roo.bootstrap.NavSidebarItem
3822  * @extends Roo.bootstrap.NavItem
3823  * Bootstrap Navbar.NavSidebarItem class
3824  * @constructor
3825  * Create a new Navbar Button
3826  * @param {Object} config The config object
3827  */
3828 Roo.bootstrap.NavSidebarItem = function(config){
3829     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3830     this.addEvents({
3831         // raw events
3832         /**
3833          * @event click
3834          * The raw click event for the entire grid.
3835          * @param {Roo.EventObject} e
3836          */
3837         "click" : true,
3838          /**
3839             * @event changed
3840             * Fires when the active item active state changes
3841             * @param {Roo.bootstrap.NavSidebarItem} this
3842             * @param {boolean} state the new state
3843              
3844          */
3845         'changed': true
3846     });
3847    
3848 };
3849
3850 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3851     
3852     
3853     getAutoCreate : function(){
3854         
3855         
3856         var a = {
3857                 tag: 'a',
3858                 href : this.href || '#',
3859                 cls: '',
3860                 html : '',
3861                 cn : []
3862         };
3863         var cfg = {
3864             tag: 'li',
3865             cls: '',
3866             cn: [ a ]
3867         }
3868         var span = {
3869             tag: 'span',
3870             html : this.html || ''
3871         }
3872         
3873         
3874         if (this.active) {
3875             cfg.cls += ' active';
3876         }
3877         
3878         // left icon..
3879         if (this.glyphicon || this.icon) {
3880             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3881             a.cn.push({ tag : 'i', cls : c }) ;
3882         }
3883         // html..
3884         a.cn.push(span);
3885         // then badge..
3886         if (this.badge !== '') {
3887             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3888         }
3889         // fi
3890         if (this.menu) {
3891             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3892             a.cls += 'dropdown-toggle treeview' ;
3893             
3894         }
3895         
3896         
3897         
3898         return cfg;
3899          
3900            
3901     }
3902    
3903      
3904  
3905 });
3906  
3907
3908  /*
3909  * - LGPL
3910  *
3911  * row
3912  * 
3913  */
3914
3915 /**
3916  * @class Roo.bootstrap.Row
3917  * @extends Roo.bootstrap.Component
3918  * Bootstrap Row class (contains columns...)
3919  * 
3920  * @constructor
3921  * Create a new Row
3922  * @param {Object} config The config object
3923  */
3924
3925 Roo.bootstrap.Row = function(config){
3926     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3927 };
3928
3929 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3930     
3931     getAutoCreate : function(){
3932        return {
3933             cls: 'row clearfix'
3934        };
3935     }
3936     
3937     
3938 });
3939
3940  
3941
3942  /*
3943  * - LGPL
3944  *
3945  * element
3946  * 
3947  */
3948
3949 /**
3950  * @class Roo.bootstrap.Element
3951  * @extends Roo.bootstrap.Component
3952  * Bootstrap Element class
3953  * @cfg {String} html contents of the element
3954  * @cfg {String} tag tag of the element
3955  * @cfg {String} cls class of the element
3956  * 
3957  * @constructor
3958  * Create a new Element
3959  * @param {Object} config The config object
3960  */
3961
3962 Roo.bootstrap.Element = function(config){
3963     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3964 };
3965
3966 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3967     
3968     tag: 'div',
3969     cls: '',
3970     html: '',
3971      
3972     
3973     getAutoCreate : function(){
3974         
3975         var cfg = {
3976             tag: this.tag,
3977             cls: this.cls,
3978             html: this.html
3979         }
3980         
3981         
3982         
3983         return cfg;
3984     }
3985    
3986 });
3987
3988  
3989
3990  /*
3991  * - LGPL
3992  *
3993  * pagination
3994  * 
3995  */
3996
3997 /**
3998  * @class Roo.bootstrap.Pagination
3999  * @extends Roo.bootstrap.Component
4000  * Bootstrap Pagination class
4001  * @cfg {String} size xs | sm | md | lg
4002  * @cfg {Boolean} inverse false | true
4003  * 
4004  * @constructor
4005  * Create a new Pagination
4006  * @param {Object} config The config object
4007  */
4008
4009 Roo.bootstrap.Pagination = function(config){
4010     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4011 };
4012
4013 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4014     
4015     cls: false,
4016     size: false,
4017     inverse: false,
4018     
4019     getAutoCreate : function(){
4020         var cfg = {
4021             tag: 'ul',
4022                 cls: 'pagination'
4023         };
4024         if (this.inverse) {
4025             cfg.cls += ' inverse';
4026         }
4027         if (this.html) {
4028             cfg.html=this.html;
4029         }
4030         if (this.cls) {
4031             cfg.cls += " " + this.cls;
4032         }
4033         return cfg;
4034     }
4035    
4036 });
4037
4038  
4039
4040  /*
4041  * - LGPL
4042  *
4043  * Pagination item
4044  * 
4045  */
4046
4047
4048 /**
4049  * @class Roo.bootstrap.PaginationItem
4050  * @extends Roo.bootstrap.Component
4051  * Bootstrap PaginationItem class
4052  * @cfg {String} html text
4053  * @cfg {String} href the link
4054  * @cfg {Boolean} preventDefault (true | false) default true
4055  * @cfg {Boolean} active (true | false) default false
4056  * 
4057  * 
4058  * @constructor
4059  * Create a new PaginationItem
4060  * @param {Object} config The config object
4061  */
4062
4063
4064 Roo.bootstrap.PaginationItem = function(config){
4065     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4066     this.addEvents({
4067         // raw events
4068         /**
4069          * @event click
4070          * The raw click event for the entire grid.
4071          * @param {Roo.EventObject} e
4072          */
4073         "click" : true
4074     });
4075 };
4076
4077 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4078     
4079     href : false,
4080     html : false,
4081     preventDefault: true,
4082     active : false,
4083     cls : false,
4084     
4085     getAutoCreate : function(){
4086         var cfg= {
4087             tag: 'li',
4088             cn: [
4089                 {
4090                     tag : 'a',
4091                     href : this.href ? this.href : '#',
4092                     html : this.html ? this.html : ''
4093                 }
4094             ]
4095         };
4096         
4097         if(this.cls){
4098             cfg.cls = this.cls;
4099         }
4100         
4101         if(this.active){
4102             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4103         }
4104         
4105         return cfg;
4106     },
4107     
4108     initEvents: function() {
4109         
4110         this.el.on('click', this.onClick, this);
4111         
4112     },
4113     onClick : function(e)
4114     {
4115         Roo.log('PaginationItem on click ');
4116         if(this.preventDefault){
4117             e.preventDefault();
4118         }
4119         
4120         this.fireEvent('click', this, e);
4121     }
4122    
4123 });
4124
4125  
4126
4127  /*
4128  * - LGPL
4129  *
4130  * slider
4131  * 
4132  */
4133
4134
4135 /**
4136  * @class Roo.bootstrap.Slider
4137  * @extends Roo.bootstrap.Component
4138  * Bootstrap Slider class
4139  *    
4140  * @constructor
4141  * Create a new Slider
4142  * @param {Object} config The config object
4143  */
4144
4145 Roo.bootstrap.Slider = function(config){
4146     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4147 };
4148
4149 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4150     
4151     getAutoCreate : function(){
4152         
4153         var cfg = {
4154             tag: 'div',
4155             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4156             cn: [
4157                 {
4158                     tag: 'a',
4159                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4160                 }
4161             ]
4162         }
4163         
4164         return cfg;
4165     }
4166    
4167 });
4168
4169  /*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179  
4180
4181 /**
4182  * @class Roo.grid.ColumnModel
4183  * @extends Roo.util.Observable
4184  * This is the default implementation of a ColumnModel used by the Grid. It defines
4185  * the columns in the grid.
4186  * <br>Usage:<br>
4187  <pre><code>
4188  var colModel = new Roo.grid.ColumnModel([
4189         {header: "Ticker", width: 60, sortable: true, locked: true},
4190         {header: "Company Name", width: 150, sortable: true},
4191         {header: "Market Cap.", width: 100, sortable: true},
4192         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4193         {header: "Employees", width: 100, sortable: true, resizable: false}
4194  ]);
4195  </code></pre>
4196  * <p>
4197  
4198  * The config options listed for this class are options which may appear in each
4199  * individual column definition.
4200  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4201  * @constructor
4202  * @param {Object} config An Array of column config objects. See this class's
4203  * config objects for details.
4204 */
4205 Roo.grid.ColumnModel = function(config){
4206         /**
4207      * The config passed into the constructor
4208      */
4209     this.config = config;
4210     this.lookup = {};
4211
4212     // if no id, create one
4213     // if the column does not have a dataIndex mapping,
4214     // map it to the order it is in the config
4215     for(var i = 0, len = config.length; i < len; i++){
4216         var c = config[i];
4217         if(typeof c.dataIndex == "undefined"){
4218             c.dataIndex = i;
4219         }
4220         if(typeof c.renderer == "string"){
4221             c.renderer = Roo.util.Format[c.renderer];
4222         }
4223         if(typeof c.id == "undefined"){
4224             c.id = Roo.id();
4225         }
4226         if(c.editor && c.editor.xtype){
4227             c.editor  = Roo.factory(c.editor, Roo.grid);
4228         }
4229         if(c.editor && c.editor.isFormField){
4230             c.editor = new Roo.grid.GridEditor(c.editor);
4231         }
4232         this.lookup[c.id] = c;
4233     }
4234
4235     /**
4236      * The width of columns which have no width specified (defaults to 100)
4237      * @type Number
4238      */
4239     this.defaultWidth = 100;
4240
4241     /**
4242      * Default sortable of columns which have no sortable specified (defaults to false)
4243      * @type Boolean
4244      */
4245     this.defaultSortable = false;
4246
4247     this.addEvents({
4248         /**
4249              * @event widthchange
4250              * Fires when the width of a column changes.
4251              * @param {ColumnModel} this
4252              * @param {Number} columnIndex The column index
4253              * @param {Number} newWidth The new width
4254              */
4255             "widthchange": true,
4256         /**
4257              * @event headerchange
4258              * Fires when the text of a header changes.
4259              * @param {ColumnModel} this
4260              * @param {Number} columnIndex The column index
4261              * @param {Number} newText The new header text
4262              */
4263             "headerchange": true,
4264         /**
4265              * @event hiddenchange
4266              * Fires when a column is hidden or "unhidden".
4267              * @param {ColumnModel} this
4268              * @param {Number} columnIndex The column index
4269              * @param {Boolean} hidden true if hidden, false otherwise
4270              */
4271             "hiddenchange": true,
4272             /**
4273          * @event columnmoved
4274          * Fires when a column is moved.
4275          * @param {ColumnModel} this
4276          * @param {Number} oldIndex
4277          * @param {Number} newIndex
4278          */
4279         "columnmoved" : true,
4280         /**
4281          * @event columlockchange
4282          * Fires when a column's locked state is changed
4283          * @param {ColumnModel} this
4284          * @param {Number} colIndex
4285          * @param {Boolean} locked true if locked
4286          */
4287         "columnlockchange" : true
4288     });
4289     Roo.grid.ColumnModel.superclass.constructor.call(this);
4290 };
4291 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4292     /**
4293      * @cfg {String} header The header text to display in the Grid view.
4294      */
4295     /**
4296      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4297      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4298      * specified, the column's index is used as an index into the Record's data Array.
4299      */
4300     /**
4301      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4302      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4303      */
4304     /**
4305      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4306      * Defaults to the value of the {@link #defaultSortable} property.
4307      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4308      */
4309     /**
4310      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4311      */
4312     /**
4313      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4314      */
4315     /**
4316      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4317      */
4318     /**
4319      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4320      */
4321     /**
4322      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4323      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4324      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4325      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4326      */
4327        /**
4328      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4329      */
4330     /**
4331      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4332      */
4333
4334     /**
4335      * Returns the id of the column at the specified index.
4336      * @param {Number} index The column index
4337      * @return {String} the id
4338      */
4339     getColumnId : function(index){
4340         return this.config[index].id;
4341     },
4342
4343     /**
4344      * Returns the column for a specified id.
4345      * @param {String} id The column id
4346      * @return {Object} the column
4347      */
4348     getColumnById : function(id){
4349         return this.lookup[id];
4350     },
4351
4352     
4353     /**
4354      * Returns the column for a specified dataIndex.
4355      * @param {String} dataIndex The column dataIndex
4356      * @return {Object|Boolean} the column or false if not found
4357      */
4358     getColumnByDataIndex: function(dataIndex){
4359         var index = this.findColumnIndex(dataIndex);
4360         return index > -1 ? this.config[index] : false;
4361     },
4362     
4363     /**
4364      * Returns the index for a specified column id.
4365      * @param {String} id The column id
4366      * @return {Number} the index, or -1 if not found
4367      */
4368     getIndexById : function(id){
4369         for(var i = 0, len = this.config.length; i < len; i++){
4370             if(this.config[i].id == id){
4371                 return i;
4372             }
4373         }
4374         return -1;
4375     },
4376     
4377     /**
4378      * Returns the index for a specified column dataIndex.
4379      * @param {String} dataIndex The column dataIndex
4380      * @return {Number} the index, or -1 if not found
4381      */
4382     
4383     findColumnIndex : function(dataIndex){
4384         for(var i = 0, len = this.config.length; i < len; i++){
4385             if(this.config[i].dataIndex == dataIndex){
4386                 return i;
4387             }
4388         }
4389         return -1;
4390     },
4391     
4392     
4393     moveColumn : function(oldIndex, newIndex){
4394         var c = this.config[oldIndex];
4395         this.config.splice(oldIndex, 1);
4396         this.config.splice(newIndex, 0, c);
4397         this.dataMap = null;
4398         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4399     },
4400
4401     isLocked : function(colIndex){
4402         return this.config[colIndex].locked === true;
4403     },
4404
4405     setLocked : function(colIndex, value, suppressEvent){
4406         if(this.isLocked(colIndex) == value){
4407             return;
4408         }
4409         this.config[colIndex].locked = value;
4410         if(!suppressEvent){
4411             this.fireEvent("columnlockchange", this, colIndex, value);
4412         }
4413     },
4414
4415     getTotalLockedWidth : function(){
4416         var totalWidth = 0;
4417         for(var i = 0; i < this.config.length; i++){
4418             if(this.isLocked(i) && !this.isHidden(i)){
4419                 this.totalWidth += this.getColumnWidth(i);
4420             }
4421         }
4422         return totalWidth;
4423     },
4424
4425     getLockedCount : function(){
4426         for(var i = 0, len = this.config.length; i < len; i++){
4427             if(!this.isLocked(i)){
4428                 return i;
4429             }
4430         }
4431     },
4432
4433     /**
4434      * Returns the number of columns.
4435      * @return {Number}
4436      */
4437     getColumnCount : function(visibleOnly){
4438         if(visibleOnly === true){
4439             var c = 0;
4440             for(var i = 0, len = this.config.length; i < len; i++){
4441                 if(!this.isHidden(i)){
4442                     c++;
4443                 }
4444             }
4445             return c;
4446         }
4447         return this.config.length;
4448     },
4449
4450     /**
4451      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4452      * @param {Function} fn
4453      * @param {Object} scope (optional)
4454      * @return {Array} result
4455      */
4456     getColumnsBy : function(fn, scope){
4457         var r = [];
4458         for(var i = 0, len = this.config.length; i < len; i++){
4459             var c = this.config[i];
4460             if(fn.call(scope||this, c, i) === true){
4461                 r[r.length] = c;
4462             }
4463         }
4464         return r;
4465     },
4466
4467     /**
4468      * Returns true if the specified column is sortable.
4469      * @param {Number} col The column index
4470      * @return {Boolean}
4471      */
4472     isSortable : function(col){
4473         if(typeof this.config[col].sortable == "undefined"){
4474             return this.defaultSortable;
4475         }
4476         return this.config[col].sortable;
4477     },
4478
4479     /**
4480      * Returns the rendering (formatting) function defined for the column.
4481      * @param {Number} col The column index.
4482      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4483      */
4484     getRenderer : function(col){
4485         if(!this.config[col].renderer){
4486             return Roo.grid.ColumnModel.defaultRenderer;
4487         }
4488         return this.config[col].renderer;
4489     },
4490
4491     /**
4492      * Sets the rendering (formatting) function for a column.
4493      * @param {Number} col The column index
4494      * @param {Function} fn The function to use to process the cell's raw data
4495      * to return HTML markup for the grid view. The render function is called with
4496      * the following parameters:<ul>
4497      * <li>Data value.</li>
4498      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4499      * <li>css A CSS style string to apply to the table cell.</li>
4500      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4501      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4502      * <li>Row index</li>
4503      * <li>Column index</li>
4504      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4505      */
4506     setRenderer : function(col, fn){
4507         this.config[col].renderer = fn;
4508     },
4509
4510     /**
4511      * Returns the width for the specified column.
4512      * @param {Number} col The column index
4513      * @return {Number}
4514      */
4515     getColumnWidth : function(col){
4516         return this.config[col].width * 1 || this.defaultWidth;
4517     },
4518
4519     /**
4520      * Sets the width for a column.
4521      * @param {Number} col The column index
4522      * @param {Number} width The new width
4523      */
4524     setColumnWidth : function(col, width, suppressEvent){
4525         this.config[col].width = width;
4526         this.totalWidth = null;
4527         if(!suppressEvent){
4528              this.fireEvent("widthchange", this, col, width);
4529         }
4530     },
4531
4532     /**
4533      * Returns the total width of all columns.
4534      * @param {Boolean} includeHidden True to include hidden column widths
4535      * @return {Number}
4536      */
4537     getTotalWidth : function(includeHidden){
4538         if(!this.totalWidth){
4539             this.totalWidth = 0;
4540             for(var i = 0, len = this.config.length; i < len; i++){
4541                 if(includeHidden || !this.isHidden(i)){
4542                     this.totalWidth += this.getColumnWidth(i);
4543                 }
4544             }
4545         }
4546         return this.totalWidth;
4547     },
4548
4549     /**
4550      * Returns the header for the specified column.
4551      * @param {Number} col The column index
4552      * @return {String}
4553      */
4554     getColumnHeader : function(col){
4555         return this.config[col].header;
4556     },
4557
4558     /**
4559      * Sets the header for a column.
4560      * @param {Number} col The column index
4561      * @param {String} header The new header
4562      */
4563     setColumnHeader : function(col, header){
4564         this.config[col].header = header;
4565         this.fireEvent("headerchange", this, col, header);
4566     },
4567
4568     /**
4569      * Returns the tooltip for the specified column.
4570      * @param {Number} col The column index
4571      * @return {String}
4572      */
4573     getColumnTooltip : function(col){
4574             return this.config[col].tooltip;
4575     },
4576     /**
4577      * Sets the tooltip for a column.
4578      * @param {Number} col The column index
4579      * @param {String} tooltip The new tooltip
4580      */
4581     setColumnTooltip : function(col, tooltip){
4582             this.config[col].tooltip = tooltip;
4583     },
4584
4585     /**
4586      * Returns the dataIndex for the specified column.
4587      * @param {Number} col The column index
4588      * @return {Number}
4589      */
4590     getDataIndex : function(col){
4591         return this.config[col].dataIndex;
4592     },
4593
4594     /**
4595      * Sets the dataIndex for a column.
4596      * @param {Number} col The column index
4597      * @param {Number} dataIndex The new dataIndex
4598      */
4599     setDataIndex : function(col, dataIndex){
4600         this.config[col].dataIndex = dataIndex;
4601     },
4602
4603     
4604     
4605     /**
4606      * Returns true if the cell is editable.
4607      * @param {Number} colIndex The column index
4608      * @param {Number} rowIndex The row index
4609      * @return {Boolean}
4610      */
4611     isCellEditable : function(colIndex, rowIndex){
4612         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4613     },
4614
4615     /**
4616      * Returns the editor defined for the cell/column.
4617      * return false or null to disable editing.
4618      * @param {Number} colIndex The column index
4619      * @param {Number} rowIndex The row index
4620      * @return {Object}
4621      */
4622     getCellEditor : function(colIndex, rowIndex){
4623         return this.config[colIndex].editor;
4624     },
4625
4626     /**
4627      * Sets if a column is editable.
4628      * @param {Number} col The column index
4629      * @param {Boolean} editable True if the column is editable
4630      */
4631     setEditable : function(col, editable){
4632         this.config[col].editable = editable;
4633     },
4634
4635
4636     /**
4637      * Returns true if the column is hidden.
4638      * @param {Number} colIndex The column index
4639      * @return {Boolean}
4640      */
4641     isHidden : function(colIndex){
4642         return this.config[colIndex].hidden;
4643     },
4644
4645
4646     /**
4647      * Returns true if the column width cannot be changed
4648      */
4649     isFixed : function(colIndex){
4650         return this.config[colIndex].fixed;
4651     },
4652
4653     /**
4654      * Returns true if the column can be resized
4655      * @return {Boolean}
4656      */
4657     isResizable : function(colIndex){
4658         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4659     },
4660     /**
4661      * Sets if a column is hidden.
4662      * @param {Number} colIndex The column index
4663      * @param {Boolean} hidden True if the column is hidden
4664      */
4665     setHidden : function(colIndex, hidden){
4666         this.config[colIndex].hidden = hidden;
4667         this.totalWidth = null;
4668         this.fireEvent("hiddenchange", this, colIndex, hidden);
4669     },
4670
4671     /**
4672      * Sets the editor for a column.
4673      * @param {Number} col The column index
4674      * @param {Object} editor The editor object
4675      */
4676     setEditor : function(col, editor){
4677         this.config[col].editor = editor;
4678     }
4679 });
4680
4681 Roo.grid.ColumnModel.defaultRenderer = function(value){
4682         if(typeof value == "string" && value.length < 1){
4683             return "&#160;";
4684         }
4685         return value;
4686 };
4687
4688 // Alias for backwards compatibility
4689 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4690 /*
4691  * Based on:
4692  * Ext JS Library 1.1.1
4693  * Copyright(c) 2006-2007, Ext JS, LLC.
4694  *
4695  * Originally Released Under LGPL - original licence link has changed is not relivant.
4696  *
4697  * Fork - LGPL
4698  * <script type="text/javascript">
4699  */
4700  
4701 /**
4702  * @class Roo.LoadMask
4703  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4704  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4705  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4706  * element's UpdateManager load indicator and will be destroyed after the initial load.
4707  * @constructor
4708  * Create a new LoadMask
4709  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4710  * @param {Object} config The config object
4711  */
4712 Roo.LoadMask = function(el, config){
4713     this.el = Roo.get(el);
4714     Roo.apply(this, config);
4715     if(this.store){
4716         this.store.on('beforeload', this.onBeforeLoad, this);
4717         this.store.on('load', this.onLoad, this);
4718         this.store.on('loadexception', this.onLoadException, this);
4719         this.removeMask = false;
4720     }else{
4721         var um = this.el.getUpdateManager();
4722         um.showLoadIndicator = false; // disable the default indicator
4723         um.on('beforeupdate', this.onBeforeLoad, this);
4724         um.on('update', this.onLoad, this);
4725         um.on('failure', this.onLoad, this);
4726         this.removeMask = true;
4727     }
4728 };
4729
4730 Roo.LoadMask.prototype = {
4731     /**
4732      * @cfg {Boolean} removeMask
4733      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4734      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4735      */
4736     /**
4737      * @cfg {String} msg
4738      * The text to display in a centered loading message box (defaults to 'Loading...')
4739      */
4740     msg : 'Loading...',
4741     /**
4742      * @cfg {String} msgCls
4743      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4744      */
4745     msgCls : 'x-mask-loading',
4746
4747     /**
4748      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4749      * @type Boolean
4750      */
4751     disabled: false,
4752
4753     /**
4754      * Disables the mask to prevent it from being displayed
4755      */
4756     disable : function(){
4757        this.disabled = true;
4758     },
4759
4760     /**
4761      * Enables the mask so that it can be displayed
4762      */
4763     enable : function(){
4764         this.disabled = false;
4765     },
4766     
4767     onLoadException : function()
4768     {
4769         Roo.log(arguments);
4770         
4771         if (typeof(arguments[3]) != 'undefined') {
4772             Roo.MessageBox.alert("Error loading",arguments[3]);
4773         } 
4774         /*
4775         try {
4776             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4777                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4778             }   
4779         } catch(e) {
4780             
4781         }
4782         */
4783     
4784         
4785         
4786         this.el.unmask(this.removeMask);
4787     },
4788     // private
4789     onLoad : function()
4790     {
4791         this.el.unmask(this.removeMask);
4792     },
4793
4794     // private
4795     onBeforeLoad : function(){
4796         if(!this.disabled){
4797             this.el.mask(this.msg, this.msgCls);
4798         }
4799     },
4800
4801     // private
4802     destroy : function(){
4803         if(this.store){
4804             this.store.un('beforeload', this.onBeforeLoad, this);
4805             this.store.un('load', this.onLoad, this);
4806             this.store.un('loadexception', this.onLoadException, this);
4807         }else{
4808             var um = this.el.getUpdateManager();
4809             um.un('beforeupdate', this.onBeforeLoad, this);
4810             um.un('update', this.onLoad, this);
4811             um.un('failure', this.onLoad, this);
4812         }
4813     }
4814 };/*
4815  * - LGPL
4816  *
4817  * table
4818  * 
4819  */
4820
4821 /**
4822  * @class Roo.bootstrap.Table
4823  * @extends Roo.bootstrap.Component
4824  * Bootstrap Table class
4825  * @cfg {String} cls table class
4826  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4827  * @cfg {String} bgcolor Specifies the background color for a table
4828  * @cfg {Number} border Specifies whether the table cells should have borders or not
4829  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4830  * @cfg {Number} cellspacing Specifies the space between cells
4831  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4832  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4833  * @cfg {String} sortable Specifies that the table should be sortable
4834  * @cfg {String} summary Specifies a summary of the content of a table
4835  * @cfg {Number} width Specifies the width of a table
4836  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4837  * 
4838  * @cfg {boolean} striped Should the rows be alternative striped
4839  * @cfg {boolean} bordered Add borders to the table
4840  * @cfg {boolean} hover Add hover highlighting
4841  * @cfg {boolean} condensed Format condensed
4842  * @cfg {boolean} responsive Format condensed
4843  * @cfg {Boolean} loadMask (true|false) default false
4844  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4845  * @cfg {Boolean} thead (true|false) generate thead, default true
4846  * @cfg {Boolean} RowSelection (true|false) default false
4847  * @cfg {Boolean} CellSelection (true|false) default false
4848  *
4849  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4850  
4851  * 
4852  * @constructor
4853  * Create a new Table
4854  * @param {Object} config The config object
4855  */
4856
4857 Roo.bootstrap.Table = function(config){
4858     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4859     
4860     if (this.sm) {
4861         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4862         this.sm = this.selModel;
4863         this.sm.xmodule = this.xmodule || false;
4864     }
4865     if (this.cm && typeof(this.cm.config) == 'undefined') {
4866         this.colModel = new Roo.grid.ColumnModel(this.cm);
4867         this.cm = this.colModel;
4868         this.cm.xmodule = this.xmodule || false;
4869     }
4870     if (this.store) {
4871         this.store= Roo.factory(this.store, Roo.data);
4872         this.ds = this.store;
4873         this.ds.xmodule = this.xmodule || false;
4874          
4875     }
4876     if (this.footer && this.store) {
4877         this.footer.dataSource = this.ds;
4878         this.footer = Roo.factory(this.footer);
4879     }
4880     
4881     /** @private */
4882     this.addEvents({
4883         /**
4884          * @event cellclick
4885          * Fires when a cell is clicked
4886          * @param {Roo.bootstrap.Table} this
4887          * @param {Roo.Element} el
4888          * @param {Number} rowIndex
4889          * @param {Number} columnIndex
4890          * @param {Roo.EventObject} e
4891          */
4892         "cellclick" : true,
4893         /**
4894          * @event celldblclick
4895          * Fires when a cell is double clicked
4896          * @param {Roo.bootstrap.Table} this
4897          * @param {Roo.Element} el
4898          * @param {Number} rowIndex
4899          * @param {Number} columnIndex
4900          * @param {Roo.EventObject} e
4901          */
4902         "celldblclick" : true,
4903         /**
4904          * @event rowclick
4905          * Fires when a row is clicked
4906          * @param {Roo.bootstrap.Table} this
4907          * @param {Roo.Element} el
4908          * @param {Number} rowIndex
4909          * @param {Roo.EventObject} e
4910          */
4911         "rowclick" : true,
4912         /**
4913          * @event rowdblclick
4914          * Fires when a row is double clicked
4915          * @param {Roo.bootstrap.Table} this
4916          * @param {Roo.Element} el
4917          * @param {Number} rowIndex
4918          * @param {Roo.EventObject} e
4919          */
4920         "rowdblclick" : true,
4921         /**
4922          * @event mouseover
4923          * Fires when a mouseover occur
4924          * @param {Roo.bootstrap.Table} this
4925          * @param {Roo.Element} el
4926          * @param {Number} rowIndex
4927          * @param {Number} columnIndex
4928          * @param {Roo.EventObject} e
4929          */
4930         "mouseover" : true,
4931         /**
4932          * @event mouseout
4933          * Fires when a mouseout occur
4934          * @param {Roo.bootstrap.Table} this
4935          * @param {Roo.Element} el
4936          * @param {Number} rowIndex
4937          * @param {Number} columnIndex
4938          * @param {Roo.EventObject} e
4939          */
4940         "mouseout" : true,
4941         /**
4942          * @event rowclass
4943          * Fires when a row is rendered, so you can change add a style to it.
4944          * @param {Roo.bootstrap.Table} this
4945          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4946          */
4947         'rowclass' : true
4948         
4949     });
4950 };
4951
4952 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4953     
4954     cls: false,
4955     align: false,
4956     bgcolor: false,
4957     border: false,
4958     cellpadding: false,
4959     cellspacing: false,
4960     frame: false,
4961     rules: false,
4962     sortable: false,
4963     summary: false,
4964     width: false,
4965     striped : false,
4966     bordered: false,
4967     hover:  false,
4968     condensed : false,
4969     responsive : false,
4970     sm : false,
4971     cm : false,
4972     store : false,
4973     loadMask : false,
4974     tfoot : true,
4975     thead : true,
4976     RowSelection : false,
4977     CellSelection : false,
4978     layout : false,
4979     
4980     // Roo.Element - the tbody
4981     mainBody: false, 
4982     
4983     getAutoCreate : function(){
4984         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4985         
4986         cfg = {
4987             tag: 'table',
4988             cls : 'table',
4989             cn : []
4990         }
4991             
4992         if (this.striped) {
4993             cfg.cls += ' table-striped';
4994         }
4995         
4996         if (this.hover) {
4997             cfg.cls += ' table-hover';
4998         }
4999         if (this.bordered) {
5000             cfg.cls += ' table-bordered';
5001         }
5002         if (this.condensed) {
5003             cfg.cls += ' table-condensed';
5004         }
5005         if (this.responsive) {
5006             cfg.cls += ' table-responsive';
5007         }
5008         
5009         if (this.cls) {
5010             cfg.cls+=  ' ' +this.cls;
5011         }
5012         
5013         // this lot should be simplifed...
5014         
5015         if (this.align) {
5016             cfg.align=this.align;
5017         }
5018         if (this.bgcolor) {
5019             cfg.bgcolor=this.bgcolor;
5020         }
5021         if (this.border) {
5022             cfg.border=this.border;
5023         }
5024         if (this.cellpadding) {
5025             cfg.cellpadding=this.cellpadding;
5026         }
5027         if (this.cellspacing) {
5028             cfg.cellspacing=this.cellspacing;
5029         }
5030         if (this.frame) {
5031             cfg.frame=this.frame;
5032         }
5033         if (this.rules) {
5034             cfg.rules=this.rules;
5035         }
5036         if (this.sortable) {
5037             cfg.sortable=this.sortable;
5038         }
5039         if (this.summary) {
5040             cfg.summary=this.summary;
5041         }
5042         if (this.width) {
5043             cfg.width=this.width;
5044         }
5045         if (this.layout) {
5046             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5047         }
5048         
5049         if(this.store || this.cm){
5050             if(this.thead){
5051                 cfg.cn.push(this.renderHeader());
5052             }
5053             
5054             cfg.cn.push(this.renderBody());
5055             
5056             if(this.tfoot){
5057                 cfg.cn.push(this.renderFooter());
5058             }
5059             
5060             cfg.cls+=  ' TableGrid';
5061         }
5062         
5063         return { cn : [ cfg ] };
5064     },
5065     
5066     initEvents : function()
5067     {   
5068         if(!this.store || !this.cm){
5069             return;
5070         }
5071         
5072         //Roo.log('initEvents with ds!!!!');
5073         
5074         this.mainBody = this.el.select('tbody', true).first();
5075         
5076         
5077         var _this = this;
5078         
5079         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5080             e.on('click', _this.sort, _this);
5081         });
5082         
5083         this.el.on("click", this.onClick, this);
5084         this.el.on("dblclick", this.onDblClick, this);
5085         
5086         this.parent().el.setStyle('position', 'relative');
5087         if (this.footer) {
5088             this.footer.parentId = this.id;
5089             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5090         }
5091         
5092         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5093         
5094         this.store.on('load', this.onLoad, this);
5095         this.store.on('beforeload', this.onBeforeLoad, this);
5096         this.store.on('update', this.onUpdate, this);
5097         
5098     },
5099     
5100     onMouseover : function(e, el)
5101     {
5102         var cell = Roo.get(el);
5103         
5104         if(!cell){
5105             return;
5106         }
5107         
5108         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5109             cell = cell.findParent('td', false, true);
5110         }
5111         
5112         var row = cell.findParent('tr', false, true);
5113         var cellIndex = cell.dom.cellIndex;
5114         var rowIndex = row.dom.rowIndex - 1; // start from 0
5115         
5116         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5117         
5118     },
5119     
5120     onMouseout : function(e, el)
5121     {
5122         var cell = Roo.get(el);
5123         
5124         if(!cell){
5125             return;
5126         }
5127         
5128         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5129             cell = cell.findParent('td', false, true);
5130         }
5131         
5132         var row = cell.findParent('tr', false, true);
5133         var cellIndex = cell.dom.cellIndex;
5134         var rowIndex = row.dom.rowIndex - 1; // start from 0
5135         
5136         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5137         
5138     },
5139     
5140     onClick : function(e, el)
5141     {
5142         var cell = Roo.get(el);
5143         
5144         if(!cell || (!this.CellSelection && !this.RowSelection)){
5145             return;
5146         }
5147         
5148         
5149         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5150             cell = cell.findParent('td', false, true);
5151         }
5152         
5153         var row = cell.findParent('tr', false, true);
5154         var cellIndex = cell.dom.cellIndex;
5155         var rowIndex = row.dom.rowIndex - 1;
5156         
5157         if(this.CellSelection){
5158             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5159         }
5160         
5161         if(this.RowSelection){
5162             this.fireEvent('rowclick', this, row, rowIndex, e);
5163         }
5164         
5165         
5166     },
5167     
5168     onDblClick : function(e,el)
5169     {
5170         var cell = Roo.get(el);
5171         
5172         if(!cell || (!this.CellSelection && !this.RowSelection)){
5173             return;
5174         }
5175         
5176         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5177             cell = cell.findParent('td', false, true);
5178         }
5179         
5180         var row = cell.findParent('tr', false, true);
5181         var cellIndex = cell.dom.cellIndex;
5182         var rowIndex = row.dom.rowIndex - 1;
5183         
5184         if(this.CellSelection){
5185             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5186         }
5187         
5188         if(this.RowSelection){
5189             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5190         }
5191     },
5192     
5193     sort : function(e,el)
5194     {
5195         var col = Roo.get(el)
5196         
5197         if(!col.hasClass('sortable')){
5198             return;
5199         }
5200         
5201         var sort = col.attr('sort');
5202         var dir = 'ASC';
5203         
5204         if(col.hasClass('glyphicon-arrow-up')){
5205             dir = 'DESC';
5206         }
5207         
5208         this.store.sortInfo = {field : sort, direction : dir};
5209         
5210         if (this.footer) {
5211             Roo.log("calling footer first");
5212             this.footer.onClick('first');
5213         } else {
5214         
5215             this.store.load({ params : { start : 0 } });
5216         }
5217     },
5218     
5219     renderHeader : function()
5220     {
5221         var header = {
5222             tag: 'thead',
5223             cn : []
5224         };
5225         
5226         var cm = this.cm;
5227         
5228         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5229             
5230             var config = cm.config[i];
5231                     
5232             var c = {
5233                 tag: 'th',
5234                 style : '',
5235                 html: cm.getColumnHeader(i)
5236             };
5237             
5238             if(typeof(config.hidden) != 'undefined' && config.hidden){
5239                 c.style += ' display:none;';
5240             }
5241             
5242             if(typeof(config.dataIndex) != 'undefined'){
5243                 c.sort = config.dataIndex;
5244             }
5245             
5246             if(typeof(config.sortable) != 'undefined' && config.sortable){
5247                 c.cls = 'sortable';
5248             }
5249             
5250             if(typeof(config.align) != 'undefined' && config.align.length){
5251                 c.style += ' text-align:' + config.align + ';';
5252             }
5253             
5254             if(typeof(config.width) != 'undefined'){
5255                 c.style += ' width:' + config.width + 'px;';
5256             }
5257             
5258             header.cn.push(c)
5259         }
5260         
5261         return header;
5262     },
5263     
5264     renderBody : function()
5265     {
5266         var body = {
5267             tag: 'tbody',
5268             cn : [
5269                 {
5270                     tag: 'tr',
5271                     cn : [
5272                         {
5273                             tag : 'td',
5274                             colspan :  this.cm.getColumnCount()
5275                         }
5276                     ]
5277                 }
5278             ]
5279         };
5280         
5281         return body;
5282     },
5283     
5284     renderFooter : function()
5285     {
5286         var footer = {
5287             tag: 'tfoot',
5288             cn : [
5289                 {
5290                     tag: 'tr',
5291                     cn : [
5292                         {
5293                             tag : 'td',
5294                             colspan :  this.cm.getColumnCount()
5295                         }
5296                     ]
5297                 }
5298             ]
5299         };
5300         
5301         return footer;
5302     },
5303     
5304     
5305     
5306     onLoad : function()
5307     {
5308         Roo.log('ds onload');
5309         this.clear();
5310         
5311         var _this = this;
5312         var cm = this.cm;
5313         var ds = this.store;
5314         
5315         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5316             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5317             
5318             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5319                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5320             }
5321             
5322             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5323                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5324             }
5325         });
5326         
5327         var tbody =  this.mainBody;
5328               
5329         if(ds.getCount() > 0){
5330             ds.data.each(function(d,rowIndex){
5331                 var row =  this.renderRow(cm, ds, rowIndex);
5332                 
5333                 tbody.createChild(row);
5334                 
5335                 var _this = this;
5336                 
5337                 if(row.cellObjects.length){
5338                     Roo.each(row.cellObjects, function(r){
5339                         _this.renderCellObject(r);
5340                     })
5341                 }
5342                 
5343             }, this);
5344         }
5345         
5346         Roo.each(this.el.select('tbody td', true).elements, function(e){
5347             e.on('mouseover', _this.onMouseover, _this);
5348         });
5349         
5350         Roo.each(this.el.select('tbody td', true).elements, function(e){
5351             e.on('mouseout', _this.onMouseout, _this);
5352         });
5353
5354         //if(this.loadMask){
5355         //    this.maskEl.hide();
5356         //}
5357     },
5358     
5359     
5360     onUpdate : function(ds,record)
5361     {
5362         this.refreshRow(record);
5363     },
5364     onRemove : function(ds, record, index, isUpdate){
5365         if(isUpdate !== true){
5366             this.fireEvent("beforerowremoved", this, index, record);
5367         }
5368         var bt = this.mainBody.dom;
5369         if(bt.rows[index]){
5370             bt.removeChild(bt.rows[index]);
5371         }
5372         
5373         if(isUpdate !== true){
5374             //this.stripeRows(index);
5375             //this.syncRowHeights(index, index);
5376             //this.layout();
5377             this.fireEvent("rowremoved", this, index, record);
5378         }
5379     },
5380     
5381     
5382     refreshRow : function(record){
5383         var ds = this.store, index;
5384         if(typeof record == 'number'){
5385             index = record;
5386             record = ds.getAt(index);
5387         }else{
5388             index = ds.indexOf(record);
5389         }
5390         this.insertRow(ds, index, true);
5391         this.onRemove(ds, record, index+1, true);
5392         //this.syncRowHeights(index, index);
5393         //this.layout();
5394         this.fireEvent("rowupdated", this, index, record);
5395     },
5396     
5397     insertRow : function(dm, rowIndex, isUpdate){
5398         
5399         if(!isUpdate){
5400             this.fireEvent("beforerowsinserted", this, rowIndex);
5401         }
5402             //var s = this.getScrollState();
5403         var row = this.renderRow(this.cm, this.store, rowIndex);
5404         // insert before rowIndex..
5405         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5406         
5407         var _this = this;
5408                 
5409         if(row.cellObjects.length){
5410             Roo.each(row.cellObjects, function(r){
5411                 _this.renderCellObject(r);
5412             })
5413         }
5414             
5415         if(!isUpdate){
5416             this.fireEvent("rowsinserted", this, rowIndex);
5417             //this.syncRowHeights(firstRow, lastRow);
5418             //this.stripeRows(firstRow);
5419             //this.layout();
5420         }
5421         
5422     },
5423     
5424     
5425     getRowDom : function(rowIndex)
5426     {
5427         // not sure if I need to check this.. but let's do it anyway..
5428         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5429                 this.mainBody.dom.rows[rowIndex] : false
5430     },
5431     // returns the object tree for a tr..
5432   
5433     
5434     renderRow : function(cm, ds, rowIndex) {
5435         
5436         var d = ds.getAt(rowIndex);
5437         
5438         var row = {
5439             tag : 'tr',
5440             cn : []
5441         };
5442             
5443         var cellObjects = [];
5444         
5445         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5446             var config = cm.config[i];
5447             
5448             var renderer = cm.getRenderer(i);
5449             var value = '';
5450             var id = false;
5451             
5452             if(typeof(renderer) !== 'undefined'){
5453                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5454             }
5455             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5456             // and are rendered into the cells after the row is rendered - using the id for the element.
5457             
5458             if(typeof(value) === 'object'){
5459                 id = Roo.id();
5460                 cellObjects.push({
5461                     container : id,
5462                     cfg : value 
5463                 })
5464             }
5465             
5466             var rowcfg = {
5467                 record: d,
5468                 rowIndex : rowIndex,
5469                 colIndex : i,
5470                 rowClass : ''
5471             }
5472
5473             this.fireEvent('rowclass', this, rowcfg);
5474             
5475             var td = {
5476                 tag: 'td',
5477                 cls : rowcfg.rowClass,
5478                 style: '',
5479                 html: (typeof(value) === 'object') ? '' : value
5480             };
5481             
5482             if (id) {
5483                 td.id = id;
5484             }
5485             
5486             if(typeof(config.hidden) != 'undefined' && config.hidden){
5487                 td.style += ' display:none;';
5488             }
5489             
5490             if(typeof(config.align) != 'undefined' && config.align.length){
5491                 td.style += ' text-align:' + config.align + ';';
5492             }
5493             
5494             if(typeof(config.width) != 'undefined'){
5495                 td.style += ' width:' +  config.width + 'px;';
5496             }
5497              
5498             row.cn.push(td);
5499            
5500         }
5501         
5502         row.cellObjects = cellObjects;
5503         
5504         return row;
5505           
5506     },
5507     
5508     
5509     
5510     onBeforeLoad : function()
5511     {
5512         //Roo.log('ds onBeforeLoad');
5513         
5514         //this.clear();
5515         
5516         //if(this.loadMask){
5517         //    this.maskEl.show();
5518         //}
5519     },
5520     
5521     clear : function()
5522     {
5523         this.el.select('tbody', true).first().dom.innerHTML = '';
5524     },
5525     
5526     getSelectionModel : function(){
5527         if(!this.selModel){
5528             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5529         }
5530         return this.selModel;
5531     },
5532     /*
5533      * Render the Roo.bootstrap object from renderder
5534      */
5535     renderCellObject : function(r)
5536     {
5537         var _this = this;
5538         
5539         var t = r.cfg.render(r.container);
5540         
5541         if(r.cfg.cn){
5542             Roo.each(r.cfg.cn, function(c){
5543                 var child = {
5544                     container: t.getChildContainer(),
5545                     cfg: c
5546                 }
5547                 _this.renderCellObject(child);
5548             })
5549         }
5550     }
5551    
5552 });
5553
5554  
5555
5556  /*
5557  * - LGPL
5558  *
5559  * table cell
5560  * 
5561  */
5562
5563 /**
5564  * @class Roo.bootstrap.TableCell
5565  * @extends Roo.bootstrap.Component
5566  * Bootstrap TableCell class
5567  * @cfg {String} html cell contain text
5568  * @cfg {String} cls cell class
5569  * @cfg {String} tag cell tag (td|th) default td
5570  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5571  * @cfg {String} align Aligns the content in a cell
5572  * @cfg {String} axis Categorizes cells
5573  * @cfg {String} bgcolor Specifies the background color of a cell
5574  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5575  * @cfg {Number} colspan Specifies the number of columns a cell should span
5576  * @cfg {String} headers Specifies one or more header cells a cell is related to
5577  * @cfg {Number} height Sets the height of a cell
5578  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5579  * @cfg {Number} rowspan Sets the number of rows a cell should span
5580  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5581  * @cfg {String} valign Vertical aligns the content in a cell
5582  * @cfg {Number} width Specifies the width of a cell
5583  * 
5584  * @constructor
5585  * Create a new TableCell
5586  * @param {Object} config The config object
5587  */
5588
5589 Roo.bootstrap.TableCell = function(config){
5590     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5591 };
5592
5593 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5594     
5595     html: false,
5596     cls: false,
5597     tag: false,
5598     abbr: false,
5599     align: false,
5600     axis: false,
5601     bgcolor: false,
5602     charoff: false,
5603     colspan: false,
5604     headers: false,
5605     height: false,
5606     nowrap: false,
5607     rowspan: false,
5608     scope: false,
5609     valign: false,
5610     width: false,
5611     
5612     
5613     getAutoCreate : function(){
5614         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5615         
5616         cfg = {
5617             tag: 'td'
5618         }
5619         
5620         if(this.tag){
5621             cfg.tag = this.tag;
5622         }
5623         
5624         if (this.html) {
5625             cfg.html=this.html
5626         }
5627         if (this.cls) {
5628             cfg.cls=this.cls
5629         }
5630         if (this.abbr) {
5631             cfg.abbr=this.abbr
5632         }
5633         if (this.align) {
5634             cfg.align=this.align
5635         }
5636         if (this.axis) {
5637             cfg.axis=this.axis
5638         }
5639         if (this.bgcolor) {
5640             cfg.bgcolor=this.bgcolor
5641         }
5642         if (this.charoff) {
5643             cfg.charoff=this.charoff
5644         }
5645         if (this.colspan) {
5646             cfg.colspan=this.colspan
5647         }
5648         if (this.headers) {
5649             cfg.headers=this.headers
5650         }
5651         if (this.height) {
5652             cfg.height=this.height
5653         }
5654         if (this.nowrap) {
5655             cfg.nowrap=this.nowrap
5656         }
5657         if (this.rowspan) {
5658             cfg.rowspan=this.rowspan
5659         }
5660         if (this.scope) {
5661             cfg.scope=this.scope
5662         }
5663         if (this.valign) {
5664             cfg.valign=this.valign
5665         }
5666         if (this.width) {
5667             cfg.width=this.width
5668         }
5669         
5670         
5671         return cfg;
5672     }
5673    
5674 });
5675
5676  
5677
5678  /*
5679  * - LGPL
5680  *
5681  * table row
5682  * 
5683  */
5684
5685 /**
5686  * @class Roo.bootstrap.TableRow
5687  * @extends Roo.bootstrap.Component
5688  * Bootstrap TableRow class
5689  * @cfg {String} cls row class
5690  * @cfg {String} align Aligns the content in a table row
5691  * @cfg {String} bgcolor Specifies a background color for a table row
5692  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5693  * @cfg {String} valign Vertical aligns the content in a table row
5694  * 
5695  * @constructor
5696  * Create a new TableRow
5697  * @param {Object} config The config object
5698  */
5699
5700 Roo.bootstrap.TableRow = function(config){
5701     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5702 };
5703
5704 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5705     
5706     cls: false,
5707     align: false,
5708     bgcolor: false,
5709     charoff: false,
5710     valign: false,
5711     
5712     getAutoCreate : function(){
5713         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5714         
5715         cfg = {
5716             tag: 'tr'
5717         }
5718             
5719         if(this.cls){
5720             cfg.cls = this.cls;
5721         }
5722         if(this.align){
5723             cfg.align = this.align;
5724         }
5725         if(this.bgcolor){
5726             cfg.bgcolor = this.bgcolor;
5727         }
5728         if(this.charoff){
5729             cfg.charoff = this.charoff;
5730         }
5731         if(this.valign){
5732             cfg.valign = this.valign;
5733         }
5734         
5735         return cfg;
5736     }
5737    
5738 });
5739
5740  
5741
5742  /*
5743  * - LGPL
5744  *
5745  * table body
5746  * 
5747  */
5748
5749 /**
5750  * @class Roo.bootstrap.TableBody
5751  * @extends Roo.bootstrap.Component
5752  * Bootstrap TableBody class
5753  * @cfg {String} cls element class
5754  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5755  * @cfg {String} align Aligns the content inside the element
5756  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5757  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5758  * 
5759  * @constructor
5760  * Create a new TableBody
5761  * @param {Object} config The config object
5762  */
5763
5764 Roo.bootstrap.TableBody = function(config){
5765     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5766 };
5767
5768 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5769     
5770     cls: false,
5771     tag: false,
5772     align: false,
5773     charoff: false,
5774     valign: false,
5775     
5776     getAutoCreate : function(){
5777         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5778         
5779         cfg = {
5780             tag: 'tbody'
5781         }
5782             
5783         if (this.cls) {
5784             cfg.cls=this.cls
5785         }
5786         if(this.tag){
5787             cfg.tag = this.tag;
5788         }
5789         
5790         if(this.align){
5791             cfg.align = this.align;
5792         }
5793         if(this.charoff){
5794             cfg.charoff = this.charoff;
5795         }
5796         if(this.valign){
5797             cfg.valign = this.valign;
5798         }
5799         
5800         return cfg;
5801     }
5802     
5803     
5804 //    initEvents : function()
5805 //    {
5806 //        
5807 //        if(!this.store){
5808 //            return;
5809 //        }
5810 //        
5811 //        this.store = Roo.factory(this.store, Roo.data);
5812 //        this.store.on('load', this.onLoad, this);
5813 //        
5814 //        this.store.load();
5815 //        
5816 //    },
5817 //    
5818 //    onLoad: function () 
5819 //    {   
5820 //        this.fireEvent('load', this);
5821 //    }
5822 //    
5823 //   
5824 });
5825
5826  
5827
5828  /*
5829  * Based on:
5830  * Ext JS Library 1.1.1
5831  * Copyright(c) 2006-2007, Ext JS, LLC.
5832  *
5833  * Originally Released Under LGPL - original licence link has changed is not relivant.
5834  *
5835  * Fork - LGPL
5836  * <script type="text/javascript">
5837  */
5838
5839 // as we use this in bootstrap.
5840 Roo.namespace('Roo.form');
5841  /**
5842  * @class Roo.form.Action
5843  * Internal Class used to handle form actions
5844  * @constructor
5845  * @param {Roo.form.BasicForm} el The form element or its id
5846  * @param {Object} config Configuration options
5847  */
5848
5849  
5850  
5851 // define the action interface
5852 Roo.form.Action = function(form, options){
5853     this.form = form;
5854     this.options = options || {};
5855 };
5856 /**
5857  * Client Validation Failed
5858  * @const 
5859  */
5860 Roo.form.Action.CLIENT_INVALID = 'client';
5861 /**
5862  * Server Validation Failed
5863  * @const 
5864  */
5865 Roo.form.Action.SERVER_INVALID = 'server';
5866  /**
5867  * Connect to Server Failed
5868  * @const 
5869  */
5870 Roo.form.Action.CONNECT_FAILURE = 'connect';
5871 /**
5872  * Reading Data from Server Failed
5873  * @const 
5874  */
5875 Roo.form.Action.LOAD_FAILURE = 'load';
5876
5877 Roo.form.Action.prototype = {
5878     type : 'default',
5879     failureType : undefined,
5880     response : undefined,
5881     result : undefined,
5882
5883     // interface method
5884     run : function(options){
5885
5886     },
5887
5888     // interface method
5889     success : function(response){
5890
5891     },
5892
5893     // interface method
5894     handleResponse : function(response){
5895
5896     },
5897
5898     // default connection failure
5899     failure : function(response){
5900         
5901         this.response = response;
5902         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5903         this.form.afterAction(this, false);
5904     },
5905
5906     processResponse : function(response){
5907         this.response = response;
5908         if(!response.responseText){
5909             return true;
5910         }
5911         this.result = this.handleResponse(response);
5912         return this.result;
5913     },
5914
5915     // utility functions used internally
5916     getUrl : function(appendParams){
5917         var url = this.options.url || this.form.url || this.form.el.dom.action;
5918         if(appendParams){
5919             var p = this.getParams();
5920             if(p){
5921                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5922             }
5923         }
5924         return url;
5925     },
5926
5927     getMethod : function(){
5928         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5929     },
5930
5931     getParams : function(){
5932         var bp = this.form.baseParams;
5933         var p = this.options.params;
5934         if(p){
5935             if(typeof p == "object"){
5936                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5937             }else if(typeof p == 'string' && bp){
5938                 p += '&' + Roo.urlEncode(bp);
5939             }
5940         }else if(bp){
5941             p = Roo.urlEncode(bp);
5942         }
5943         return p;
5944     },
5945
5946     createCallback : function(){
5947         return {
5948             success: this.success,
5949             failure: this.failure,
5950             scope: this,
5951             timeout: (this.form.timeout*1000),
5952             upload: this.form.fileUpload ? this.success : undefined
5953         };
5954     }
5955 };
5956
5957 Roo.form.Action.Submit = function(form, options){
5958     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5959 };
5960
5961 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5962     type : 'submit',
5963
5964     haveProgress : false,
5965     uploadComplete : false,
5966     
5967     // uploadProgress indicator.
5968     uploadProgress : function()
5969     {
5970         if (!this.form.progressUrl) {
5971             return;
5972         }
5973         
5974         if (!this.haveProgress) {
5975             Roo.MessageBox.progress("Uploading", "Uploading");
5976         }
5977         if (this.uploadComplete) {
5978            Roo.MessageBox.hide();
5979            return;
5980         }
5981         
5982         this.haveProgress = true;
5983    
5984         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5985         
5986         var c = new Roo.data.Connection();
5987         c.request({
5988             url : this.form.progressUrl,
5989             params: {
5990                 id : uid
5991             },
5992             method: 'GET',
5993             success : function(req){
5994                //console.log(data);
5995                 var rdata = false;
5996                 var edata;
5997                 try  {
5998                    rdata = Roo.decode(req.responseText)
5999                 } catch (e) {
6000                     Roo.log("Invalid data from server..");
6001                     Roo.log(edata);
6002                     return;
6003                 }
6004                 if (!rdata || !rdata.success) {
6005                     Roo.log(rdata);
6006                     Roo.MessageBox.alert(Roo.encode(rdata));
6007                     return;
6008                 }
6009                 var data = rdata.data;
6010                 
6011                 if (this.uploadComplete) {
6012                    Roo.MessageBox.hide();
6013                    return;
6014                 }
6015                    
6016                 if (data){
6017                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6018                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6019                     );
6020                 }
6021                 this.uploadProgress.defer(2000,this);
6022             },
6023        
6024             failure: function(data) {
6025                 Roo.log('progress url failed ');
6026                 Roo.log(data);
6027             },
6028             scope : this
6029         });
6030            
6031     },
6032     
6033     
6034     run : function()
6035     {
6036         // run get Values on the form, so it syncs any secondary forms.
6037         this.form.getValues();
6038         
6039         var o = this.options;
6040         var method = this.getMethod();
6041         var isPost = method == 'POST';
6042         if(o.clientValidation === false || this.form.isValid()){
6043             
6044             if (this.form.progressUrl) {
6045                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6046                     (new Date() * 1) + '' + Math.random());
6047                     
6048             } 
6049             
6050             
6051             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6052                 form:this.form.el.dom,
6053                 url:this.getUrl(!isPost),
6054                 method: method,
6055                 params:isPost ? this.getParams() : null,
6056                 isUpload: this.form.fileUpload
6057             }));
6058             
6059             this.uploadProgress();
6060
6061         }else if (o.clientValidation !== false){ // client validation failed
6062             this.failureType = Roo.form.Action.CLIENT_INVALID;
6063             this.form.afterAction(this, false);
6064         }
6065     },
6066
6067     success : function(response)
6068     {
6069         this.uploadComplete= true;
6070         if (this.haveProgress) {
6071             Roo.MessageBox.hide();
6072         }
6073         
6074         
6075         var result = this.processResponse(response);
6076         if(result === true || result.success){
6077             this.form.afterAction(this, true);
6078             return;
6079         }
6080         if(result.errors){
6081             this.form.markInvalid(result.errors);
6082             this.failureType = Roo.form.Action.SERVER_INVALID;
6083         }
6084         this.form.afterAction(this, false);
6085     },
6086     failure : function(response)
6087     {
6088         this.uploadComplete= true;
6089         if (this.haveProgress) {
6090             Roo.MessageBox.hide();
6091         }
6092         
6093         this.response = response;
6094         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6095         this.form.afterAction(this, false);
6096     },
6097     
6098     handleResponse : function(response){
6099         if(this.form.errorReader){
6100             var rs = this.form.errorReader.read(response);
6101             var errors = [];
6102             if(rs.records){
6103                 for(var i = 0, len = rs.records.length; i < len; i++) {
6104                     var r = rs.records[i];
6105                     errors[i] = r.data;
6106                 }
6107             }
6108             if(errors.length < 1){
6109                 errors = null;
6110             }
6111             return {
6112                 success : rs.success,
6113                 errors : errors
6114             };
6115         }
6116         var ret = false;
6117         try {
6118             ret = Roo.decode(response.responseText);
6119         } catch (e) {
6120             ret = {
6121                 success: false,
6122                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6123                 errors : []
6124             };
6125         }
6126         return ret;
6127         
6128     }
6129 });
6130
6131
6132 Roo.form.Action.Load = function(form, options){
6133     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6134     this.reader = this.form.reader;
6135 };
6136
6137 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6138     type : 'load',
6139
6140     run : function(){
6141         
6142         Roo.Ajax.request(Roo.apply(
6143                 this.createCallback(), {
6144                     method:this.getMethod(),
6145                     url:this.getUrl(false),
6146                     params:this.getParams()
6147         }));
6148     },
6149
6150     success : function(response){
6151         
6152         var result = this.processResponse(response);
6153         if(result === true || !result.success || !result.data){
6154             this.failureType = Roo.form.Action.LOAD_FAILURE;
6155             this.form.afterAction(this, false);
6156             return;
6157         }
6158         this.form.clearInvalid();
6159         this.form.setValues(result.data);
6160         this.form.afterAction(this, true);
6161     },
6162
6163     handleResponse : function(response){
6164         if(this.form.reader){
6165             var rs = this.form.reader.read(response);
6166             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6167             return {
6168                 success : rs.success,
6169                 data : data
6170             };
6171         }
6172         return Roo.decode(response.responseText);
6173     }
6174 });
6175
6176 Roo.form.Action.ACTION_TYPES = {
6177     'load' : Roo.form.Action.Load,
6178     'submit' : Roo.form.Action.Submit
6179 };/*
6180  * - LGPL
6181  *
6182  * form
6183  * 
6184  */
6185
6186 /**
6187  * @class Roo.bootstrap.Form
6188  * @extends Roo.bootstrap.Component
6189  * Bootstrap Form class
6190  * @cfg {String} method  GET | POST (default POST)
6191  * @cfg {String} labelAlign top | left (default top)
6192  * @cfg {String} align left  | right - for navbars
6193  * @cfg {Boolean} loadMask load mask when submit (default true)
6194
6195  * 
6196  * @constructor
6197  * Create a new Form
6198  * @param {Object} config The config object
6199  */
6200
6201
6202 Roo.bootstrap.Form = function(config){
6203     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6204     this.addEvents({
6205         /**
6206          * @event clientvalidation
6207          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6208          * @param {Form} this
6209          * @param {Boolean} valid true if the form has passed client-side validation
6210          */
6211         clientvalidation: true,
6212         /**
6213          * @event beforeaction
6214          * Fires before any action is performed. Return false to cancel the action.
6215          * @param {Form} this
6216          * @param {Action} action The action to be performed
6217          */
6218         beforeaction: true,
6219         /**
6220          * @event actionfailed
6221          * Fires when an action fails.
6222          * @param {Form} this
6223          * @param {Action} action The action that failed
6224          */
6225         actionfailed : true,
6226         /**
6227          * @event actioncomplete
6228          * Fires when an action is completed.
6229          * @param {Form} this
6230          * @param {Action} action The action that completed
6231          */
6232         actioncomplete : true
6233     });
6234     
6235 };
6236
6237 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6238       
6239      /**
6240      * @cfg {String} method
6241      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6242      */
6243     method : 'POST',
6244     /**
6245      * @cfg {String} url
6246      * The URL to use for form actions if one isn't supplied in the action options.
6247      */
6248     /**
6249      * @cfg {Boolean} fileUpload
6250      * Set to true if this form is a file upload.
6251      */
6252      
6253     /**
6254      * @cfg {Object} baseParams
6255      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6256      */
6257       
6258     /**
6259      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6260      */
6261     timeout: 30,
6262     /**
6263      * @cfg {Sting} align (left|right) for navbar forms
6264      */
6265     align : 'left',
6266
6267     // private
6268     activeAction : null,
6269  
6270     /**
6271      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6272      * element by passing it or its id or mask the form itself by passing in true.
6273      * @type Mixed
6274      */
6275     waitMsgTarget : false,
6276     
6277     loadMask : true,
6278     
6279     getAutoCreate : function(){
6280         
6281         var cfg = {
6282             tag: 'form',
6283             method : this.method || 'POST',
6284             id : this.id || Roo.id(),
6285             cls : ''
6286         }
6287         if (this.parent().xtype.match(/^Nav/)) {
6288             cfg.cls = 'navbar-form navbar-' + this.align;
6289             
6290         }
6291         
6292         if (this.labelAlign == 'left' ) {
6293             cfg.cls += ' form-horizontal';
6294         }
6295         
6296         
6297         return cfg;
6298     },
6299     initEvents : function()
6300     {
6301         this.el.on('submit', this.onSubmit, this);
6302         // this was added as random key presses on the form where triggering form submit.
6303         this.el.on('keypress', function(e) {
6304             if (e.getCharCode() != 13) {
6305                 return true;
6306             }
6307             // we might need to allow it for textareas.. and some other items.
6308             // check e.getTarget().
6309             
6310             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6311                 return true;
6312             }
6313         
6314             Roo.log("keypress blocked");
6315             
6316             e.preventDefault();
6317             return false;
6318         });
6319         
6320     },
6321     // private
6322     onSubmit : function(e){
6323         e.stopEvent();
6324     },
6325     
6326      /**
6327      * Returns true if client-side validation on the form is successful.
6328      * @return Boolean
6329      */
6330     isValid : function(){
6331         var items = this.getItems();
6332         var valid = true;
6333         items.each(function(f){
6334            if(!f.validate()){
6335                valid = false;
6336                
6337            }
6338         });
6339         return valid;
6340     },
6341     /**
6342      * Returns true if any fields in this form have changed since their original load.
6343      * @return Boolean
6344      */
6345     isDirty : function(){
6346         var dirty = false;
6347         var items = this.getItems();
6348         items.each(function(f){
6349            if(f.isDirty()){
6350                dirty = true;
6351                return false;
6352            }
6353            return true;
6354         });
6355         return dirty;
6356     },
6357      /**
6358      * Performs a predefined action (submit or load) or custom actions you define on this form.
6359      * @param {String} actionName The name of the action type
6360      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6361      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6362      * accept other config options):
6363      * <pre>
6364 Property          Type             Description
6365 ----------------  ---------------  ----------------------------------------------------------------------------------
6366 url               String           The url for the action (defaults to the form's url)
6367 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6368 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6369 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6370                                    validate the form on the client (defaults to false)
6371      * </pre>
6372      * @return {BasicForm} this
6373      */
6374     doAction : function(action, options){
6375         if(typeof action == 'string'){
6376             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6377         }
6378         if(this.fireEvent('beforeaction', this, action) !== false){
6379             this.beforeAction(action);
6380             action.run.defer(100, action);
6381         }
6382         return this;
6383     },
6384     
6385     // private
6386     beforeAction : function(action){
6387         var o = action.options;
6388         
6389         if(this.loadMask){
6390             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6391         }
6392         // not really supported yet.. ??
6393         
6394         //if(this.waitMsgTarget === true){
6395         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6396         //}else if(this.waitMsgTarget){
6397         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6398         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6399         //}else {
6400         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6401        // }
6402          
6403     },
6404
6405     // private
6406     afterAction : function(action, success){
6407         this.activeAction = null;
6408         var o = action.options;
6409         
6410         //if(this.waitMsgTarget === true){
6411             this.el.unmask();
6412         //}else if(this.waitMsgTarget){
6413         //    this.waitMsgTarget.unmask();
6414         //}else{
6415         //    Roo.MessageBox.updateProgress(1);
6416         //    Roo.MessageBox.hide();
6417        // }
6418         // 
6419         if(success){
6420             if(o.reset){
6421                 this.reset();
6422             }
6423             Roo.callback(o.success, o.scope, [this, action]);
6424             this.fireEvent('actioncomplete', this, action);
6425             
6426         }else{
6427             
6428             // failure condition..
6429             // we have a scenario where updates need confirming.
6430             // eg. if a locking scenario exists..
6431             // we look for { errors : { needs_confirm : true }} in the response.
6432             if (
6433                 (typeof(action.result) != 'undefined')  &&
6434                 (typeof(action.result.errors) != 'undefined')  &&
6435                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6436            ){
6437                 var _t = this;
6438                 Roo.log("not supported yet");
6439                  /*
6440                 
6441                 Roo.MessageBox.confirm(
6442                     "Change requires confirmation",
6443                     action.result.errorMsg,
6444                     function(r) {
6445                         if (r != 'yes') {
6446                             return;
6447                         }
6448                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6449                     }
6450                     
6451                 );
6452                 */
6453                 
6454                 
6455                 return;
6456             }
6457             
6458             Roo.callback(o.failure, o.scope, [this, action]);
6459             // show an error message if no failed handler is set..
6460             if (!this.hasListener('actionfailed')) {
6461                 Roo.log("need to add dialog support");
6462                 /*
6463                 Roo.MessageBox.alert("Error",
6464                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6465                         action.result.errorMsg :
6466                         "Saving Failed, please check your entries or try again"
6467                 );
6468                 */
6469             }
6470             
6471             this.fireEvent('actionfailed', this, action);
6472         }
6473         
6474     },
6475     /**
6476      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6477      * @param {String} id The value to search for
6478      * @return Field
6479      */
6480     findField : function(id){
6481         var items = this.getItems();
6482         var field = items.get(id);
6483         if(!field){
6484              items.each(function(f){
6485                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6486                     field = f;
6487                     return false;
6488                 }
6489                 return true;
6490             });
6491         }
6492         return field || null;
6493     },
6494      /**
6495      * Mark fields in this form invalid in bulk.
6496      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6497      * @return {BasicForm} this
6498      */
6499     markInvalid : function(errors){
6500         if(errors instanceof Array){
6501             for(var i = 0, len = errors.length; i < len; i++){
6502                 var fieldError = errors[i];
6503                 var f = this.findField(fieldError.id);
6504                 if(f){
6505                     f.markInvalid(fieldError.msg);
6506                 }
6507             }
6508         }else{
6509             var field, id;
6510             for(id in errors){
6511                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6512                     field.markInvalid(errors[id]);
6513                 }
6514             }
6515         }
6516         //Roo.each(this.childForms || [], function (f) {
6517         //    f.markInvalid(errors);
6518         //});
6519         
6520         return this;
6521     },
6522
6523     /**
6524      * Set values for fields in this form in bulk.
6525      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6526      * @return {BasicForm} this
6527      */
6528     setValues : function(values){
6529         if(values instanceof Array){ // array of objects
6530             for(var i = 0, len = values.length; i < len; i++){
6531                 var v = values[i];
6532                 var f = this.findField(v.id);
6533                 if(f){
6534                     f.setValue(v.value);
6535                     if(this.trackResetOnLoad){
6536                         f.originalValue = f.getValue();
6537                     }
6538                 }
6539             }
6540         }else{ // object hash
6541             var field, id;
6542             for(id in values){
6543                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6544                     
6545                     if (field.setFromData && 
6546                         field.valueField && 
6547                         field.displayField &&
6548                         // combos' with local stores can 
6549                         // be queried via setValue()
6550                         // to set their value..
6551                         (field.store && !field.store.isLocal)
6552                         ) {
6553                         // it's a combo
6554                         var sd = { };
6555                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6556                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6557                         field.setFromData(sd);
6558                         
6559                     } else {
6560                         field.setValue(values[id]);
6561                     }
6562                     
6563                     
6564                     if(this.trackResetOnLoad){
6565                         field.originalValue = field.getValue();
6566                     }
6567                 }
6568             }
6569         }
6570          
6571         //Roo.each(this.childForms || [], function (f) {
6572         //    f.setValues(values);
6573         //});
6574                 
6575         return this;
6576     },
6577
6578     /**
6579      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6580      * they are returned as an array.
6581      * @param {Boolean} asString
6582      * @return {Object}
6583      */
6584     getValues : function(asString){
6585         //if (this.childForms) {
6586             // copy values from the child forms
6587         //    Roo.each(this.childForms, function (f) {
6588         //        this.setValues(f.getValues());
6589         //    }, this);
6590         //}
6591         
6592         
6593         
6594         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6595         if(asString === true){
6596             return fs;
6597         }
6598         return Roo.urlDecode(fs);
6599     },
6600     
6601     /**
6602      * Returns the fields in this form as an object with key/value pairs. 
6603      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6604      * @return {Object}
6605      */
6606     getFieldValues : function(with_hidden)
6607     {
6608         var items = this.getItems();
6609         var ret = {};
6610         items.each(function(f){
6611             if (!f.getName()) {
6612                 return;
6613             }
6614             var v = f.getValue();
6615             if (f.inputType =='radio') {
6616                 if (typeof(ret[f.getName()]) == 'undefined') {
6617                     ret[f.getName()] = ''; // empty..
6618                 }
6619                 
6620                 if (!f.el.dom.checked) {
6621                     return;
6622                     
6623                 }
6624                 v = f.el.dom.value;
6625                 
6626             }
6627             
6628             // not sure if this supported any more..
6629             if ((typeof(v) == 'object') && f.getRawValue) {
6630                 v = f.getRawValue() ; // dates..
6631             }
6632             // combo boxes where name != hiddenName...
6633             if (f.name != f.getName()) {
6634                 ret[f.name] = f.getRawValue();
6635             }
6636             ret[f.getName()] = v;
6637         });
6638         
6639         return ret;
6640     },
6641
6642     /**
6643      * Clears all invalid messages in this form.
6644      * @return {BasicForm} this
6645      */
6646     clearInvalid : function(){
6647         var items = this.getItems();
6648         
6649         items.each(function(f){
6650            f.clearInvalid();
6651         });
6652         
6653         
6654         
6655         return this;
6656     },
6657
6658     /**
6659      * Resets this form.
6660      * @return {BasicForm} this
6661      */
6662     reset : function(){
6663         var items = this.getItems();
6664         items.each(function(f){
6665             f.reset();
6666         });
6667         
6668         Roo.each(this.childForms || [], function (f) {
6669             f.reset();
6670         });
6671        
6672         
6673         return this;
6674     },
6675     getItems : function()
6676     {
6677         var r=new Roo.util.MixedCollection(false, function(o){
6678             return o.id || (o.id = Roo.id());
6679         });
6680         var iter = function(el) {
6681             if (el.inputEl) {
6682                 r.add(el);
6683             }
6684             if (!el.items) {
6685                 return;
6686             }
6687             Roo.each(el.items,function(e) {
6688                 iter(e);
6689             });
6690             
6691             
6692         };
6693         iter(this);
6694         return r;
6695         
6696         
6697         
6698         
6699     }
6700     
6701 });
6702
6703  
6704 /*
6705  * Based on:
6706  * Ext JS Library 1.1.1
6707  * Copyright(c) 2006-2007, Ext JS, LLC.
6708  *
6709  * Originally Released Under LGPL - original licence link has changed is not relivant.
6710  *
6711  * Fork - LGPL
6712  * <script type="text/javascript">
6713  */
6714 /**
6715  * @class Roo.form.VTypes
6716  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6717  * @singleton
6718  */
6719 Roo.form.VTypes = function(){
6720     // closure these in so they are only created once.
6721     var alpha = /^[a-zA-Z_]+$/;
6722     var alphanum = /^[a-zA-Z0-9_]+$/;
6723     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6724     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6725
6726     // All these messages and functions are configurable
6727     return {
6728         /**
6729          * The function used to validate email addresses
6730          * @param {String} value The email address
6731          */
6732         'email' : function(v){
6733             return email.test(v);
6734         },
6735         /**
6736          * The error text to display when the email validation function returns false
6737          * @type String
6738          */
6739         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6740         /**
6741          * The keystroke filter mask to be applied on email input
6742          * @type RegExp
6743          */
6744         'emailMask' : /[a-z0-9_\.\-@]/i,
6745
6746         /**
6747          * The function used to validate URLs
6748          * @param {String} value The URL
6749          */
6750         'url' : function(v){
6751             return url.test(v);
6752         },
6753         /**
6754          * The error text to display when the url validation function returns false
6755          * @type String
6756          */
6757         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6758         
6759         /**
6760          * The function used to validate alpha values
6761          * @param {String} value The value
6762          */
6763         'alpha' : function(v){
6764             return alpha.test(v);
6765         },
6766         /**
6767          * The error text to display when the alpha validation function returns false
6768          * @type String
6769          */
6770         'alphaText' : 'This field should only contain letters and _',
6771         /**
6772          * The keystroke filter mask to be applied on alpha input
6773          * @type RegExp
6774          */
6775         'alphaMask' : /[a-z_]/i,
6776
6777         /**
6778          * The function used to validate alphanumeric values
6779          * @param {String} value The value
6780          */
6781         'alphanum' : function(v){
6782             return alphanum.test(v);
6783         },
6784         /**
6785          * The error text to display when the alphanumeric validation function returns false
6786          * @type String
6787          */
6788         'alphanumText' : 'This field should only contain letters, numbers and _',
6789         /**
6790          * The keystroke filter mask to be applied on alphanumeric input
6791          * @type RegExp
6792          */
6793         'alphanumMask' : /[a-z0-9_]/i
6794     };
6795 }();/*
6796  * - LGPL
6797  *
6798  * Input
6799  * 
6800  */
6801
6802 /**
6803  * @class Roo.bootstrap.Input
6804  * @extends Roo.bootstrap.Component
6805  * Bootstrap Input class
6806  * @cfg {Boolean} disabled is it disabled
6807  * @cfg {String} fieldLabel - the label associated
6808  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6809  * @cfg {String} name name of the input
6810  * @cfg {string} fieldLabel - the label associated
6811  * @cfg {string}  inputType - input / file submit ...
6812  * @cfg {string} placeholder - placeholder to put in text.
6813  * @cfg {string}  before - input group add on before
6814  * @cfg {string} after - input group add on after
6815  * @cfg {string} size - (lg|sm) or leave empty..
6816  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6817  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6818  * @cfg {Number} md colspan out of 12 for computer-sized screens
6819  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6820  * @cfg {string} value default value of the input
6821  * @cfg {Number} labelWidth set the width of label (0-12)
6822  * @cfg {String} labelAlign (top|left)
6823  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6824  * @cfg {String} align (left|center|right) Default left
6825  * 
6826  * 
6827  * @constructor
6828  * Create a new Input
6829  * @param {Object} config The config object
6830  */
6831
6832 Roo.bootstrap.Input = function(config){
6833     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6834    
6835         this.addEvents({
6836             /**
6837              * @event focus
6838              * Fires when this field receives input focus.
6839              * @param {Roo.form.Field} this
6840              */
6841             focus : true,
6842             /**
6843              * @event blur
6844              * Fires when this field loses input focus.
6845              * @param {Roo.form.Field} this
6846              */
6847             blur : true,
6848             /**
6849              * @event specialkey
6850              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6851              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6852              * @param {Roo.form.Field} this
6853              * @param {Roo.EventObject} e The event object
6854              */
6855             specialkey : true,
6856             /**
6857              * @event change
6858              * Fires just before the field blurs if the field value has changed.
6859              * @param {Roo.form.Field} this
6860              * @param {Mixed} newValue The new value
6861              * @param {Mixed} oldValue The original value
6862              */
6863             change : true,
6864             /**
6865              * @event invalid
6866              * Fires after the field has been marked as invalid.
6867              * @param {Roo.form.Field} this
6868              * @param {String} msg The validation message
6869              */
6870             invalid : true,
6871             /**
6872              * @event valid
6873              * Fires after the field has been validated with no errors.
6874              * @param {Roo.form.Field} this
6875              */
6876             valid : true,
6877              /**
6878              * @event keyup
6879              * Fires after the key up
6880              * @param {Roo.form.Field} this
6881              * @param {Roo.EventObject}  e The event Object
6882              */
6883             keyup : true
6884         });
6885 };
6886
6887 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6888      /**
6889      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6890       automatic validation (defaults to "keyup").
6891      */
6892     validationEvent : "keyup",
6893      /**
6894      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6895      */
6896     validateOnBlur : true,
6897     /**
6898      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6899      */
6900     validationDelay : 250,
6901      /**
6902      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6903      */
6904     focusClass : "x-form-focus",  // not needed???
6905     
6906        
6907     /**
6908      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6909      */
6910     invalidClass : "has-error",
6911     
6912     /**
6913      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6914      */
6915     selectOnFocus : false,
6916     
6917      /**
6918      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6919      */
6920     maskRe : null,
6921        /**
6922      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6923      */
6924     vtype : null,
6925     
6926       /**
6927      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6928      */
6929     disableKeyFilter : false,
6930     
6931        /**
6932      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6933      */
6934     disabled : false,
6935      /**
6936      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6937      */
6938     allowBlank : true,
6939     /**
6940      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6941      */
6942     blankText : "This field is required",
6943     
6944      /**
6945      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6946      */
6947     minLength : 0,
6948     /**
6949      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6950      */
6951     maxLength : Number.MAX_VALUE,
6952     /**
6953      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6954      */
6955     minLengthText : "The minimum length for this field is {0}",
6956     /**
6957      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6958      */
6959     maxLengthText : "The maximum length for this field is {0}",
6960   
6961     
6962     /**
6963      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6964      * If available, this function will be called only after the basic validators all return true, and will be passed the
6965      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6966      */
6967     validator : null,
6968     /**
6969      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6970      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6971      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6972      */
6973     regex : null,
6974     /**
6975      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6976      */
6977     regexText : "",
6978     
6979     
6980     
6981     fieldLabel : '',
6982     inputType : 'text',
6983     
6984     name : false,
6985     placeholder: false,
6986     before : false,
6987     after : false,
6988     size : false,
6989     // private
6990     hasFocus : false,
6991     preventMark: false,
6992     isFormField : true,
6993     value : '',
6994     labelWidth : 2,
6995     labelAlign : false,
6996     readOnly : false,
6997     align : false,
6998     formatedValue : false,
6999     
7000     parentLabelAlign : function()
7001     {
7002         var parent = this;
7003         while (parent.parent()) {
7004             parent = parent.parent();
7005             if (typeof(parent.labelAlign) !='undefined') {
7006                 return parent.labelAlign;
7007             }
7008         }
7009         return 'left';
7010         
7011     },
7012     
7013     getAutoCreate : function(){
7014         
7015         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7016         
7017         var id = Roo.id();
7018         
7019         var cfg = {};
7020         
7021         if(this.inputType != 'hidden'){
7022             cfg.cls = 'form-group' //input-group
7023         }
7024         
7025         var input =  {
7026             tag: 'input',
7027             id : id,
7028             type : this.inputType,
7029             value : this.value,
7030             cls : 'form-control',
7031             placeholder : this.placeholder || ''
7032             
7033         };
7034         
7035         if(this.align){
7036             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7037         }
7038         
7039         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7040             input.maxLength = this.maxLength;
7041         }
7042         
7043         if (this.disabled) {
7044             input.disabled=true;
7045         }
7046         
7047         if (this.readOnly) {
7048             input.readonly=true;
7049         }
7050         
7051         if (this.name) {
7052             input.name = this.name;
7053         }
7054         if (this.size) {
7055             input.cls += ' input-' + this.size;
7056         }
7057         var settings=this;
7058         ['xs','sm','md','lg'].map(function(size){
7059             if (settings[size]) {
7060                 cfg.cls += ' col-' + size + '-' + settings[size];
7061             }
7062         });
7063         
7064         var inputblock = input;
7065         
7066         if (this.before || this.after) {
7067             
7068             inputblock = {
7069                 cls : 'input-group',
7070                 cn :  [] 
7071             };
7072             if (this.before && typeof(this.before) == 'string') {
7073                 
7074                 inputblock.cn.push({
7075                     tag :'span',
7076                     cls : 'roo-input-before input-group-addon',
7077                     html : this.before
7078                 });
7079             }
7080             if (this.before && typeof(this.before) == 'object') {
7081                 this.before = Roo.factory(this.before);
7082                 Roo.log(this.before);
7083                 inputblock.cn.push({
7084                     tag :'span',
7085                     cls : 'roo-input-before input-group-' +
7086                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7087                 });
7088             }
7089             
7090             inputblock.cn.push(input);
7091             
7092             if (this.after && typeof(this.after) == 'string') {
7093                 inputblock.cn.push({
7094                     tag :'span',
7095                     cls : 'roo-input-after input-group-addon',
7096                     html : this.after
7097                 });
7098             }
7099             if (this.after && typeof(this.after) == 'object') {
7100                 this.after = Roo.factory(this.after);
7101                 Roo.log(this.after);
7102                 inputblock.cn.push({
7103                     tag :'span',
7104                     cls : 'roo-input-after input-group-' +
7105                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7106                 });
7107             }
7108         };
7109         
7110         if (align ==='left' && this.fieldLabel.length) {
7111                 Roo.log("left and has label");
7112                 cfg.cn = [
7113                     
7114                     {
7115                         tag: 'label',
7116                         'for' :  id,
7117                         cls : 'control-label col-sm-' + this.labelWidth,
7118                         html : this.fieldLabel
7119                         
7120                     },
7121                     {
7122                         cls : "col-sm-" + (12 - this.labelWidth), 
7123                         cn: [
7124                             inputblock
7125                         ]
7126                     }
7127                     
7128                 ];
7129         } else if ( this.fieldLabel.length) {
7130                 Roo.log(" label");
7131                  cfg.cn = [
7132                    
7133                     {
7134                         tag: 'label',
7135                         //cls : 'input-group-addon',
7136                         html : this.fieldLabel
7137                         
7138                     },
7139                     
7140                     inputblock
7141                     
7142                 ];
7143
7144         } else {
7145             
7146                 Roo.log(" no label && no align");
7147                 cfg.cn = [
7148                     
7149                         inputblock
7150                     
7151                 ];
7152                 
7153                 
7154         };
7155         Roo.log('input-parentType: ' + this.parentType);
7156         
7157         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7158            cfg.cls += ' navbar-form';
7159            Roo.log(cfg);
7160         }
7161         
7162         return cfg;
7163         
7164     },
7165     /**
7166      * return the real input element.
7167      */
7168     inputEl: function ()
7169     {
7170         return this.el.select('input.form-control',true).first();
7171     },
7172     setDisabled : function(v)
7173     {
7174         var i  = this.inputEl().dom;
7175         if (!v) {
7176             i.removeAttribute('disabled');
7177             return;
7178             
7179         }
7180         i.setAttribute('disabled','true');
7181     },
7182     initEvents : function()
7183     {
7184         
7185         this.inputEl().on("keydown" , this.fireKey,  this);
7186         this.inputEl().on("focus", this.onFocus,  this);
7187         this.inputEl().on("blur", this.onBlur,  this);
7188         
7189         this.inputEl().relayEvent('keyup', this);
7190
7191         // reference to original value for reset
7192         this.originalValue = this.getValue();
7193         //Roo.form.TextField.superclass.initEvents.call(this);
7194         if(this.validationEvent == 'keyup'){
7195             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7196             this.inputEl().on('keyup', this.filterValidation, this);
7197         }
7198         else if(this.validationEvent !== false){
7199             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7200         }
7201         
7202         if(this.selectOnFocus){
7203             this.on("focus", this.preFocus, this);
7204             
7205         }
7206         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7207             this.inputEl().on("keypress", this.filterKeys, this);
7208         }
7209        /* if(this.grow){
7210             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7211             this.el.on("click", this.autoSize,  this);
7212         }
7213         */
7214         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7215             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7216         }
7217         
7218         if (typeof(this.before) == 'object') {
7219             this.before.render(this.el.select('.roo-input-before',true).first());
7220         }
7221         if (typeof(this.after) == 'object') {
7222             this.after.render(this.el.select('.roo-input-after',true).first());
7223         }
7224         
7225         
7226     },
7227     filterValidation : function(e){
7228         if(!e.isNavKeyPress()){
7229             this.validationTask.delay(this.validationDelay);
7230         }
7231     },
7232      /**
7233      * Validates the field value
7234      * @return {Boolean} True if the value is valid, else false
7235      */
7236     validate : function(){
7237         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7238         if(this.disabled || this.validateValue(this.getRawValue())){
7239             this.clearInvalid();
7240             return true;
7241         }
7242         return false;
7243     },
7244     
7245     
7246     /**
7247      * Validates a value according to the field's validation rules and marks the field as invalid
7248      * if the validation fails
7249      * @param {Mixed} value The value to validate
7250      * @return {Boolean} True if the value is valid, else false
7251      */
7252     validateValue : function(value){
7253         if(value.length < 1)  { // if it's blank
7254              if(this.allowBlank){
7255                 this.clearInvalid();
7256                 return true;
7257              }else{
7258                 this.markInvalid(this.blankText);
7259                 return false;
7260              }
7261         }
7262         if(value.length < this.minLength){
7263             this.markInvalid(String.format(this.minLengthText, this.minLength));
7264             return false;
7265         }
7266         if(value.length > this.maxLength){
7267             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7268             return false;
7269         }
7270         if(this.vtype){
7271             var vt = Roo.form.VTypes;
7272             if(!vt[this.vtype](value, this)){
7273                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7274                 return false;
7275             }
7276         }
7277         if(typeof this.validator == "function"){
7278             var msg = this.validator(value);
7279             if(msg !== true){
7280                 this.markInvalid(msg);
7281                 return false;
7282             }
7283         }
7284         if(this.regex && !this.regex.test(value)){
7285             this.markInvalid(this.regexText);
7286             return false;
7287         }
7288         return true;
7289     },
7290
7291     
7292     
7293      // private
7294     fireKey : function(e){
7295         //Roo.log('field ' + e.getKey());
7296         if(e.isNavKeyPress()){
7297             this.fireEvent("specialkey", this, e);
7298         }
7299     },
7300     focus : function (selectText){
7301         if(this.rendered){
7302             this.inputEl().focus();
7303             if(selectText === true){
7304                 this.inputEl().dom.select();
7305             }
7306         }
7307         return this;
7308     } ,
7309     
7310     onFocus : function(){
7311         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7312            // this.el.addClass(this.focusClass);
7313         }
7314         if(!this.hasFocus){
7315             this.hasFocus = true;
7316             this.startValue = this.getValue();
7317             this.fireEvent("focus", this);
7318         }
7319     },
7320     
7321     beforeBlur : Roo.emptyFn,
7322
7323     
7324     // private
7325     onBlur : function(){
7326         this.beforeBlur();
7327         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7328             //this.el.removeClass(this.focusClass);
7329         }
7330         this.hasFocus = false;
7331         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7332             this.validate();
7333         }
7334         var v = this.getValue();
7335         if(String(v) !== String(this.startValue)){
7336             this.fireEvent('change', this, v, this.startValue);
7337         }
7338         this.fireEvent("blur", this);
7339     },
7340     
7341     /**
7342      * Resets the current field value to the originally loaded value and clears any validation messages
7343      */
7344     reset : function(){
7345         this.setValue(this.originalValue);
7346         this.clearInvalid();
7347     },
7348      /**
7349      * Returns the name of the field
7350      * @return {Mixed} name The name field
7351      */
7352     getName: function(){
7353         return this.name;
7354     },
7355      /**
7356      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7357      * @return {Mixed} value The field value
7358      */
7359     getValue : function(){
7360         
7361         var v = this.inputEl().getValue();
7362         
7363         return v;
7364     },
7365     /**
7366      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7367      * @return {Mixed} value The field value
7368      */
7369     getRawValue : function(){
7370         var v = this.inputEl().getValue();
7371         
7372         return v;
7373     },
7374     
7375     /**
7376      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7377      * @param {Mixed} value The value to set
7378      */
7379     setRawValue : function(v){
7380         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7381     },
7382     
7383     selectText : function(start, end){
7384         var v = this.getRawValue();
7385         if(v.length > 0){
7386             start = start === undefined ? 0 : start;
7387             end = end === undefined ? v.length : end;
7388             var d = this.inputEl().dom;
7389             if(d.setSelectionRange){
7390                 d.setSelectionRange(start, end);
7391             }else if(d.createTextRange){
7392                 var range = d.createTextRange();
7393                 range.moveStart("character", start);
7394                 range.moveEnd("character", v.length-end);
7395                 range.select();
7396             }
7397         }
7398     },
7399     
7400     /**
7401      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7402      * @param {Mixed} value The value to set
7403      */
7404     setValue : function(v){
7405         this.value = v;
7406         if(this.rendered){
7407             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7408             this.validate();
7409         }
7410     },
7411     
7412     /*
7413     processValue : function(value){
7414         if(this.stripCharsRe){
7415             var newValue = value.replace(this.stripCharsRe, '');
7416             if(newValue !== value){
7417                 this.setRawValue(newValue);
7418                 return newValue;
7419             }
7420         }
7421         return value;
7422     },
7423   */
7424     preFocus : function(){
7425         
7426         if(this.selectOnFocus){
7427             this.inputEl().dom.select();
7428         }
7429     },
7430     filterKeys : function(e){
7431         var k = e.getKey();
7432         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7433             return;
7434         }
7435         var c = e.getCharCode(), cc = String.fromCharCode(c);
7436         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7437             return;
7438         }
7439         if(!this.maskRe.test(cc)){
7440             e.stopEvent();
7441         }
7442     },
7443      /**
7444      * Clear any invalid styles/messages for this field
7445      */
7446     clearInvalid : function(){
7447         
7448         if(!this.el || this.preventMark){ // not rendered
7449             return;
7450         }
7451         this.el.removeClass(this.invalidClass);
7452         /*
7453         switch(this.msgTarget){
7454             case 'qtip':
7455                 this.el.dom.qtip = '';
7456                 break;
7457             case 'title':
7458                 this.el.dom.title = '';
7459                 break;
7460             case 'under':
7461                 if(this.errorEl){
7462                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7463                 }
7464                 break;
7465             case 'side':
7466                 if(this.errorIcon){
7467                     this.errorIcon.dom.qtip = '';
7468                     this.errorIcon.hide();
7469                     this.un('resize', this.alignErrorIcon, this);
7470                 }
7471                 break;
7472             default:
7473                 var t = Roo.getDom(this.msgTarget);
7474                 t.innerHTML = '';
7475                 t.style.display = 'none';
7476                 break;
7477         }
7478         */
7479         this.fireEvent('valid', this);
7480     },
7481      /**
7482      * Mark this field as invalid
7483      * @param {String} msg The validation message
7484      */
7485     markInvalid : function(msg){
7486         if(!this.el  || this.preventMark){ // not rendered
7487             return;
7488         }
7489         this.el.addClass(this.invalidClass);
7490         /*
7491         msg = msg || this.invalidText;
7492         switch(this.msgTarget){
7493             case 'qtip':
7494                 this.el.dom.qtip = msg;
7495                 this.el.dom.qclass = 'x-form-invalid-tip';
7496                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7497                     Roo.QuickTips.enable();
7498                 }
7499                 break;
7500             case 'title':
7501                 this.el.dom.title = msg;
7502                 break;
7503             case 'under':
7504                 if(!this.errorEl){
7505                     var elp = this.el.findParent('.x-form-element', 5, true);
7506                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7507                     this.errorEl.setWidth(elp.getWidth(true)-20);
7508                 }
7509                 this.errorEl.update(msg);
7510                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7511                 break;
7512             case 'side':
7513                 if(!this.errorIcon){
7514                     var elp = this.el.findParent('.x-form-element', 5, true);
7515                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7516                 }
7517                 this.alignErrorIcon();
7518                 this.errorIcon.dom.qtip = msg;
7519                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7520                 this.errorIcon.show();
7521                 this.on('resize', this.alignErrorIcon, this);
7522                 break;
7523             default:
7524                 var t = Roo.getDom(this.msgTarget);
7525                 t.innerHTML = msg;
7526                 t.style.display = this.msgDisplay;
7527                 break;
7528         }
7529         */
7530         this.fireEvent('invalid', this, msg);
7531     },
7532     // private
7533     SafariOnKeyDown : function(event)
7534     {
7535         // this is a workaround for a password hang bug on chrome/ webkit.
7536         
7537         var isSelectAll = false;
7538         
7539         if(this.inputEl().dom.selectionEnd > 0){
7540             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7541         }
7542         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7543             event.preventDefault();
7544             this.setValue('');
7545             return;
7546         }
7547         
7548         if(isSelectAll){ // backspace and delete key
7549             
7550             event.preventDefault();
7551             // this is very hacky as keydown always get's upper case.
7552             //
7553             var cc = String.fromCharCode(event.getCharCode());
7554             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7555             
7556         }
7557     },
7558     adjustWidth : function(tag, w){
7559         tag = tag.toLowerCase();
7560         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7561             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7562                 if(tag == 'input'){
7563                     return w + 2;
7564                 }
7565                 if(tag == 'textarea'){
7566                     return w-2;
7567                 }
7568             }else if(Roo.isOpera){
7569                 if(tag == 'input'){
7570                     return w + 2;
7571                 }
7572                 if(tag == 'textarea'){
7573                     return w-2;
7574                 }
7575             }
7576         }
7577         return w;
7578     }
7579     
7580 });
7581
7582  
7583 /*
7584  * - LGPL
7585  *
7586  * Input
7587  * 
7588  */
7589
7590 /**
7591  * @class Roo.bootstrap.TextArea
7592  * @extends Roo.bootstrap.Input
7593  * Bootstrap TextArea class
7594  * @cfg {Number} cols Specifies the visible width of a text area
7595  * @cfg {Number} rows Specifies the visible number of lines in a text area
7596  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7597  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7598  * @cfg {string} html text
7599  * 
7600  * @constructor
7601  * Create a new TextArea
7602  * @param {Object} config The config object
7603  */
7604
7605 Roo.bootstrap.TextArea = function(config){
7606     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7607    
7608 };
7609
7610 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7611      
7612     cols : false,
7613     rows : 5,
7614     readOnly : false,
7615     warp : 'soft',
7616     resize : false,
7617     value: false,
7618     html: false,
7619     
7620     getAutoCreate : function(){
7621         
7622         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7623         
7624         var id = Roo.id();
7625         
7626         var cfg = {};
7627         
7628         var input =  {
7629             tag: 'textarea',
7630             id : id,
7631             warp : this.warp,
7632             rows : this.rows,
7633             value : this.value || '',
7634             html: this.html || '',
7635             cls : 'form-control',
7636             placeholder : this.placeholder || '' 
7637             
7638         };
7639         
7640         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7641             input.maxLength = this.maxLength;
7642         }
7643         
7644         if(this.resize){
7645             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7646         }
7647         
7648         if(this.cols){
7649             input.cols = this.cols;
7650         }
7651         
7652         if (this.readOnly) {
7653             input.readonly = true;
7654         }
7655         
7656         if (this.name) {
7657             input.name = this.name;
7658         }
7659         
7660         if (this.size) {
7661             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7662         }
7663         
7664         var settings=this;
7665         ['xs','sm','md','lg'].map(function(size){
7666             if (settings[size]) {
7667                 cfg.cls += ' col-' + size + '-' + settings[size];
7668             }
7669         });
7670         
7671         var inputblock = input;
7672         
7673         if (this.before || this.after) {
7674             
7675             inputblock = {
7676                 cls : 'input-group',
7677                 cn :  [] 
7678             };
7679             if (this.before) {
7680                 inputblock.cn.push({
7681                     tag :'span',
7682                     cls : 'input-group-addon',
7683                     html : this.before
7684                 });
7685             }
7686             inputblock.cn.push(input);
7687             if (this.after) {
7688                 inputblock.cn.push({
7689                     tag :'span',
7690                     cls : 'input-group-addon',
7691                     html : this.after
7692                 });
7693             }
7694             
7695         }
7696         
7697         if (align ==='left' && this.fieldLabel.length) {
7698                 Roo.log("left and has label");
7699                 cfg.cn = [
7700                     
7701                     {
7702                         tag: 'label',
7703                         'for' :  id,
7704                         cls : 'control-label col-sm-' + this.labelWidth,
7705                         html : this.fieldLabel
7706                         
7707                     },
7708                     {
7709                         cls : "col-sm-" + (12 - this.labelWidth), 
7710                         cn: [
7711                             inputblock
7712                         ]
7713                     }
7714                     
7715                 ];
7716         } else if ( this.fieldLabel.length) {
7717                 Roo.log(" label");
7718                  cfg.cn = [
7719                    
7720                     {
7721                         tag: 'label',
7722                         //cls : 'input-group-addon',
7723                         html : this.fieldLabel
7724                         
7725                     },
7726                     
7727                     inputblock
7728                     
7729                 ];
7730
7731         } else {
7732             
7733                    Roo.log(" no label && no align");
7734                 cfg.cn = [
7735                     
7736                         inputblock
7737                     
7738                 ];
7739                 
7740                 
7741         }
7742         
7743         if (this.disabled) {
7744             input.disabled=true;
7745         }
7746         
7747         return cfg;
7748         
7749     },
7750     /**
7751      * return the real textarea element.
7752      */
7753     inputEl: function ()
7754     {
7755         return this.el.select('textarea.form-control',true).first();
7756     }
7757 });
7758
7759  
7760 /*
7761  * - LGPL
7762  *
7763  * trigger field - base class for combo..
7764  * 
7765  */
7766  
7767 /**
7768  * @class Roo.bootstrap.TriggerField
7769  * @extends Roo.bootstrap.Input
7770  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7771  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7772  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7773  * for which you can provide a custom implementation.  For example:
7774  * <pre><code>
7775 var trigger = new Roo.bootstrap.TriggerField();
7776 trigger.onTriggerClick = myTriggerFn;
7777 trigger.applyTo('my-field');
7778 </code></pre>
7779  *
7780  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7781  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7782  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7783  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7784  * @constructor
7785  * Create a new TriggerField.
7786  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7787  * to the base TextField)
7788  */
7789 Roo.bootstrap.TriggerField = function(config){
7790     this.mimicing = false;
7791     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7792 };
7793
7794 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7795     /**
7796      * @cfg {String} triggerClass A CSS class to apply to the trigger
7797      */
7798      /**
7799      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7800      */
7801     hideTrigger:false,
7802
7803     /** @cfg {Boolean} grow @hide */
7804     /** @cfg {Number} growMin @hide */
7805     /** @cfg {Number} growMax @hide */
7806
7807     /**
7808      * @hide 
7809      * @method
7810      */
7811     autoSize: Roo.emptyFn,
7812     // private
7813     monitorTab : true,
7814     // private
7815     deferHeight : true,
7816
7817     
7818     actionMode : 'wrap',
7819     
7820     
7821     
7822     getAutoCreate : function(){
7823        
7824         var align = this.labelAlign || this.parentLabelAlign();
7825         
7826         var id = Roo.id();
7827         
7828         var cfg = {
7829             cls: 'form-group' //input-group
7830         };
7831         
7832         
7833         var input =  {
7834             tag: 'input',
7835             id : id,
7836             type : this.inputType,
7837             cls : 'form-control',
7838             autocomplete: 'off',
7839             placeholder : this.placeholder || '' 
7840             
7841         };
7842         if (this.name) {
7843             input.name = this.name;
7844         }
7845         if (this.size) {
7846             input.cls += ' input-' + this.size;
7847         }
7848         
7849         if (this.disabled) {
7850             input.disabled=true;
7851         }
7852         
7853         var inputblock = input;
7854         
7855         if (this.before || this.after) {
7856             
7857             inputblock = {
7858                 cls : 'input-group',
7859                 cn :  [] 
7860             };
7861             if (this.before) {
7862                 inputblock.cn.push({
7863                     tag :'span',
7864                     cls : 'input-group-addon',
7865                     html : this.before
7866                 });
7867             }
7868             inputblock.cn.push(input);
7869             if (this.after) {
7870                 inputblock.cn.push({
7871                     tag :'span',
7872                     cls : 'input-group-addon',
7873                     html : this.after
7874                 });
7875             }
7876             
7877         };
7878         
7879         var box = {
7880             tag: 'div',
7881             cn: [
7882                 {
7883                     tag: 'input',
7884                     type : 'hidden',
7885                     cls: 'form-hidden-field'
7886                 },
7887                 inputblock
7888             ]
7889             
7890         };
7891         
7892         if(this.multiple){
7893             Roo.log('multiple');
7894             
7895             box = {
7896                 tag: 'div',
7897                 cn: [
7898                     {
7899                         tag: 'input',
7900                         type : 'hidden',
7901                         cls: 'form-hidden-field'
7902                     },
7903                     {
7904                         tag: 'ul',
7905                         cls: 'select2-choices',
7906                         cn:[
7907                             {
7908                                 tag: 'li',
7909                                 cls: 'select2-search-field',
7910                                 cn: [
7911
7912                                     inputblock
7913                                 ]
7914                             }
7915                         ]
7916                     }
7917                 ]
7918             }
7919         };
7920         
7921         var combobox = {
7922             cls: 'select2-container input-group',
7923             cn: [
7924                 box
7925 //                {
7926 //                    tag: 'ul',
7927 //                    cls: 'typeahead typeahead-long dropdown-menu',
7928 //                    style: 'display:none'
7929 //                }
7930             ]
7931         };
7932         
7933         if(!this.multiple && this.showToggleBtn){
7934             combobox.cn.push({
7935                 tag :'span',
7936                 cls : 'input-group-addon btn dropdown-toggle',
7937                 cn : [
7938                     {
7939                         tag: 'span',
7940                         cls: 'caret'
7941                     },
7942                     {
7943                         tag: 'span',
7944                         cls: 'combobox-clear',
7945                         cn  : [
7946                             {
7947                                 tag : 'i',
7948                                 cls: 'icon-remove'
7949                             }
7950                         ]
7951                     }
7952                 ]
7953
7954             })
7955         }
7956         
7957         if(this.multiple){
7958             combobox.cls += ' select2-container-multi';
7959         }
7960         
7961         if (align ==='left' && this.fieldLabel.length) {
7962             
7963                 Roo.log("left and has label");
7964                 cfg.cn = [
7965                     
7966                     {
7967                         tag: 'label',
7968                         'for' :  id,
7969                         cls : 'control-label col-sm-' + this.labelWidth,
7970                         html : this.fieldLabel
7971                         
7972                     },
7973                     {
7974                         cls : "col-sm-" + (12 - this.labelWidth), 
7975                         cn: [
7976                             combobox
7977                         ]
7978                     }
7979                     
7980                 ];
7981         } else if ( this.fieldLabel.length) {
7982                 Roo.log(" label");
7983                  cfg.cn = [
7984                    
7985                     {
7986                         tag: 'label',
7987                         //cls : 'input-group-addon',
7988                         html : this.fieldLabel
7989                         
7990                     },
7991                     
7992                     combobox
7993                     
7994                 ];
7995
7996         } else {
7997             
7998                 Roo.log(" no label && no align");
7999                 cfg = combobox
8000                      
8001                 
8002         }
8003          
8004         var settings=this;
8005         ['xs','sm','md','lg'].map(function(size){
8006             if (settings[size]) {
8007                 cfg.cls += ' col-' + size + '-' + settings[size];
8008             }
8009         });
8010         
8011         return cfg;
8012         
8013     },
8014     
8015     
8016     
8017     // private
8018     onResize : function(w, h){
8019 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8020 //        if(typeof w == 'number'){
8021 //            var x = w - this.trigger.getWidth();
8022 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8023 //            this.trigger.setStyle('left', x+'px');
8024 //        }
8025     },
8026
8027     // private
8028     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8029
8030     // private
8031     getResizeEl : function(){
8032         return this.inputEl();
8033     },
8034
8035     // private
8036     getPositionEl : function(){
8037         return this.inputEl();
8038     },
8039
8040     // private
8041     alignErrorIcon : function(){
8042         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8043     },
8044
8045     // private
8046     initEvents : function(){
8047         
8048         this.createList();
8049         
8050         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8051         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8052         if(!this.multiple && this.showToggleBtn){
8053             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8054             if(this.hideTrigger){
8055                 this.trigger.setDisplayed(false);
8056             }
8057             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8058         }
8059         
8060         if(this.multiple){
8061             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8062         }
8063         
8064         //this.trigger.addClassOnOver('x-form-trigger-over');
8065         //this.trigger.addClassOnClick('x-form-trigger-click');
8066         
8067         //if(!this.width){
8068         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8069         //}
8070     },
8071     
8072     createList : function()
8073     {
8074         this.list = Roo.get(document.body).createChild({
8075             tag: 'ul',
8076             cls: 'typeahead typeahead-long dropdown-menu',
8077             style: 'display:none'
8078         });
8079         
8080         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8081         
8082     },
8083
8084     // private
8085     initTrigger : function(){
8086        
8087     },
8088
8089     // private
8090     onDestroy : function(){
8091         if(this.trigger){
8092             this.trigger.removeAllListeners();
8093           //  this.trigger.remove();
8094         }
8095         //if(this.wrap){
8096         //    this.wrap.remove();
8097         //}
8098         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8099     },
8100
8101     // private
8102     onFocus : function(){
8103         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8104         /*
8105         if(!this.mimicing){
8106             this.wrap.addClass('x-trigger-wrap-focus');
8107             this.mimicing = true;
8108             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8109             if(this.monitorTab){
8110                 this.el.on("keydown", this.checkTab, this);
8111             }
8112         }
8113         */
8114     },
8115
8116     // private
8117     checkTab : function(e){
8118         if(e.getKey() == e.TAB){
8119             this.triggerBlur();
8120         }
8121     },
8122
8123     // private
8124     onBlur : function(){
8125         // do nothing
8126     },
8127
8128     // private
8129     mimicBlur : function(e, t){
8130         /*
8131         if(!this.wrap.contains(t) && this.validateBlur()){
8132             this.triggerBlur();
8133         }
8134         */
8135     },
8136
8137     // private
8138     triggerBlur : function(){
8139         this.mimicing = false;
8140         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8141         if(this.monitorTab){
8142             this.el.un("keydown", this.checkTab, this);
8143         }
8144         //this.wrap.removeClass('x-trigger-wrap-focus');
8145         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8146     },
8147
8148     // private
8149     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8150     validateBlur : function(e, t){
8151         return true;
8152     },
8153
8154     // private
8155     onDisable : function(){
8156         this.inputEl().dom.disabled = true;
8157         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8158         //if(this.wrap){
8159         //    this.wrap.addClass('x-item-disabled');
8160         //}
8161     },
8162
8163     // private
8164     onEnable : function(){
8165         this.inputEl().dom.disabled = false;
8166         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8167         //if(this.wrap){
8168         //    this.el.removeClass('x-item-disabled');
8169         //}
8170     },
8171
8172     // private
8173     onShow : function(){
8174         var ae = this.getActionEl();
8175         
8176         if(ae){
8177             ae.dom.style.display = '';
8178             ae.dom.style.visibility = 'visible';
8179         }
8180     },
8181
8182     // private
8183     
8184     onHide : function(){
8185         var ae = this.getActionEl();
8186         ae.dom.style.display = 'none';
8187     },
8188
8189     /**
8190      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8191      * by an implementing function.
8192      * @method
8193      * @param {EventObject} e
8194      */
8195     onTriggerClick : Roo.emptyFn
8196 });
8197  /*
8198  * Based on:
8199  * Ext JS Library 1.1.1
8200  * Copyright(c) 2006-2007, Ext JS, LLC.
8201  *
8202  * Originally Released Under LGPL - original licence link has changed is not relivant.
8203  *
8204  * Fork - LGPL
8205  * <script type="text/javascript">
8206  */
8207
8208
8209 /**
8210  * @class Roo.data.SortTypes
8211  * @singleton
8212  * Defines the default sorting (casting?) comparison functions used when sorting data.
8213  */
8214 Roo.data.SortTypes = {
8215     /**
8216      * Default sort that does nothing
8217      * @param {Mixed} s The value being converted
8218      * @return {Mixed} The comparison value
8219      */
8220     none : function(s){
8221         return s;
8222     },
8223     
8224     /**
8225      * The regular expression used to strip tags
8226      * @type {RegExp}
8227      * @property
8228      */
8229     stripTagsRE : /<\/?[^>]+>/gi,
8230     
8231     /**
8232      * Strips all HTML tags to sort on text only
8233      * @param {Mixed} s The value being converted
8234      * @return {String} The comparison value
8235      */
8236     asText : function(s){
8237         return String(s).replace(this.stripTagsRE, "");
8238     },
8239     
8240     /**
8241      * Strips all HTML tags to sort on text only - Case insensitive
8242      * @param {Mixed} s The value being converted
8243      * @return {String} The comparison value
8244      */
8245     asUCText : function(s){
8246         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8247     },
8248     
8249     /**
8250      * Case insensitive string
8251      * @param {Mixed} s The value being converted
8252      * @return {String} The comparison value
8253      */
8254     asUCString : function(s) {
8255         return String(s).toUpperCase();
8256     },
8257     
8258     /**
8259      * Date sorting
8260      * @param {Mixed} s The value being converted
8261      * @return {Number} The comparison value
8262      */
8263     asDate : function(s) {
8264         if(!s){
8265             return 0;
8266         }
8267         if(s instanceof Date){
8268             return s.getTime();
8269         }
8270         return Date.parse(String(s));
8271     },
8272     
8273     /**
8274      * Float sorting
8275      * @param {Mixed} s The value being converted
8276      * @return {Float} The comparison value
8277      */
8278     asFloat : function(s) {
8279         var val = parseFloat(String(s).replace(/,/g, ""));
8280         if(isNaN(val)) val = 0;
8281         return val;
8282     },
8283     
8284     /**
8285      * Integer sorting
8286      * @param {Mixed} s The value being converted
8287      * @return {Number} The comparison value
8288      */
8289     asInt : function(s) {
8290         var val = parseInt(String(s).replace(/,/g, ""));
8291         if(isNaN(val)) val = 0;
8292         return val;
8293     }
8294 };/*
8295  * Based on:
8296  * Ext JS Library 1.1.1
8297  * Copyright(c) 2006-2007, Ext JS, LLC.
8298  *
8299  * Originally Released Under LGPL - original licence link has changed is not relivant.
8300  *
8301  * Fork - LGPL
8302  * <script type="text/javascript">
8303  */
8304
8305 /**
8306 * @class Roo.data.Record
8307  * Instances of this class encapsulate both record <em>definition</em> information, and record
8308  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8309  * to access Records cached in an {@link Roo.data.Store} object.<br>
8310  * <p>
8311  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8312  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8313  * objects.<br>
8314  * <p>
8315  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8316  * @constructor
8317  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8318  * {@link #create}. The parameters are the same.
8319  * @param {Array} data An associative Array of data values keyed by the field name.
8320  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8321  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8322  * not specified an integer id is generated.
8323  */
8324 Roo.data.Record = function(data, id){
8325     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8326     this.data = data;
8327 };
8328
8329 /**
8330  * Generate a constructor for a specific record layout.
8331  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8332  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8333  * Each field definition object may contain the following properties: <ul>
8334  * <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,
8335  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8336  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8337  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8338  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8339  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8340  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8341  * this may be omitted.</p></li>
8342  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8343  * <ul><li>auto (Default, implies no conversion)</li>
8344  * <li>string</li>
8345  * <li>int</li>
8346  * <li>float</li>
8347  * <li>boolean</li>
8348  * <li>date</li></ul></p></li>
8349  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8350  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8351  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8352  * by the Reader into an object that will be stored in the Record. It is passed the
8353  * following parameters:<ul>
8354  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8355  * </ul></p></li>
8356  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8357  * </ul>
8358  * <br>usage:<br><pre><code>
8359 var TopicRecord = Roo.data.Record.create(
8360     {name: 'title', mapping: 'topic_title'},
8361     {name: 'author', mapping: 'username'},
8362     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8363     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8364     {name: 'lastPoster', mapping: 'user2'},
8365     {name: 'excerpt', mapping: 'post_text'}
8366 );
8367
8368 var myNewRecord = new TopicRecord({
8369     title: 'Do my job please',
8370     author: 'noobie',
8371     totalPosts: 1,
8372     lastPost: new Date(),
8373     lastPoster: 'Animal',
8374     excerpt: 'No way dude!'
8375 });
8376 myStore.add(myNewRecord);
8377 </code></pre>
8378  * @method create
8379  * @static
8380  */
8381 Roo.data.Record.create = function(o){
8382     var f = function(){
8383         f.superclass.constructor.apply(this, arguments);
8384     };
8385     Roo.extend(f, Roo.data.Record);
8386     var p = f.prototype;
8387     p.fields = new Roo.util.MixedCollection(false, function(field){
8388         return field.name;
8389     });
8390     for(var i = 0, len = o.length; i < len; i++){
8391         p.fields.add(new Roo.data.Field(o[i]));
8392     }
8393     f.getField = function(name){
8394         return p.fields.get(name);  
8395     };
8396     return f;
8397 };
8398
8399 Roo.data.Record.AUTO_ID = 1000;
8400 Roo.data.Record.EDIT = 'edit';
8401 Roo.data.Record.REJECT = 'reject';
8402 Roo.data.Record.COMMIT = 'commit';
8403
8404 Roo.data.Record.prototype = {
8405     /**
8406      * Readonly flag - true if this record has been modified.
8407      * @type Boolean
8408      */
8409     dirty : false,
8410     editing : false,
8411     error: null,
8412     modified: null,
8413
8414     // private
8415     join : function(store){
8416         this.store = store;
8417     },
8418
8419     /**
8420      * Set the named field to the specified value.
8421      * @param {String} name The name of the field to set.
8422      * @param {Object} value The value to set the field to.
8423      */
8424     set : function(name, value){
8425         if(this.data[name] == value){
8426             return;
8427         }
8428         this.dirty = true;
8429         if(!this.modified){
8430             this.modified = {};
8431         }
8432         if(typeof this.modified[name] == 'undefined'){
8433             this.modified[name] = this.data[name];
8434         }
8435         this.data[name] = value;
8436         if(!this.editing && this.store){
8437             this.store.afterEdit(this);
8438         }       
8439     },
8440
8441     /**
8442      * Get the value of the named field.
8443      * @param {String} name The name of the field to get the value of.
8444      * @return {Object} The value of the field.
8445      */
8446     get : function(name){
8447         return this.data[name]; 
8448     },
8449
8450     // private
8451     beginEdit : function(){
8452         this.editing = true;
8453         this.modified = {}; 
8454     },
8455
8456     // private
8457     cancelEdit : function(){
8458         this.editing = false;
8459         delete this.modified;
8460     },
8461
8462     // private
8463     endEdit : function(){
8464         this.editing = false;
8465         if(this.dirty && this.store){
8466             this.store.afterEdit(this);
8467         }
8468     },
8469
8470     /**
8471      * Usually called by the {@link Roo.data.Store} which owns the Record.
8472      * Rejects all changes made to the Record since either creation, or the last commit operation.
8473      * Modified fields are reverted to their original values.
8474      * <p>
8475      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8476      * of reject operations.
8477      */
8478     reject : function(){
8479         var m = this.modified;
8480         for(var n in m){
8481             if(typeof m[n] != "function"){
8482                 this.data[n] = m[n];
8483             }
8484         }
8485         this.dirty = false;
8486         delete this.modified;
8487         this.editing = false;
8488         if(this.store){
8489             this.store.afterReject(this);
8490         }
8491     },
8492
8493     /**
8494      * Usually called by the {@link Roo.data.Store} which owns the Record.
8495      * Commits all changes made to the Record since either creation, or the last commit operation.
8496      * <p>
8497      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8498      * of commit operations.
8499      */
8500     commit : function(){
8501         this.dirty = false;
8502         delete this.modified;
8503         this.editing = false;
8504         if(this.store){
8505             this.store.afterCommit(this);
8506         }
8507     },
8508
8509     // private
8510     hasError : function(){
8511         return this.error != null;
8512     },
8513
8514     // private
8515     clearError : function(){
8516         this.error = null;
8517     },
8518
8519     /**
8520      * Creates a copy of this record.
8521      * @param {String} id (optional) A new record id if you don't want to use this record's id
8522      * @return {Record}
8523      */
8524     copy : function(newId) {
8525         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8526     }
8527 };/*
8528  * Based on:
8529  * Ext JS Library 1.1.1
8530  * Copyright(c) 2006-2007, Ext JS, LLC.
8531  *
8532  * Originally Released Under LGPL - original licence link has changed is not relivant.
8533  *
8534  * Fork - LGPL
8535  * <script type="text/javascript">
8536  */
8537
8538
8539
8540 /**
8541  * @class Roo.data.Store
8542  * @extends Roo.util.Observable
8543  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8544  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8545  * <p>
8546  * 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
8547  * has no knowledge of the format of the data returned by the Proxy.<br>
8548  * <p>
8549  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8550  * instances from the data object. These records are cached and made available through accessor functions.
8551  * @constructor
8552  * Creates a new Store.
8553  * @param {Object} config A config object containing the objects needed for the Store to access data,
8554  * and read the data into Records.
8555  */
8556 Roo.data.Store = function(config){
8557     this.data = new Roo.util.MixedCollection(false);
8558     this.data.getKey = function(o){
8559         return o.id;
8560     };
8561     this.baseParams = {};
8562     // private
8563     this.paramNames = {
8564         "start" : "start",
8565         "limit" : "limit",
8566         "sort" : "sort",
8567         "dir" : "dir",
8568         "multisort" : "_multisort"
8569     };
8570
8571     if(config && config.data){
8572         this.inlineData = config.data;
8573         delete config.data;
8574     }
8575
8576     Roo.apply(this, config);
8577     
8578     if(this.reader){ // reader passed
8579         this.reader = Roo.factory(this.reader, Roo.data);
8580         this.reader.xmodule = this.xmodule || false;
8581         if(!this.recordType){
8582             this.recordType = this.reader.recordType;
8583         }
8584         if(this.reader.onMetaChange){
8585             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8586         }
8587     }
8588
8589     if(this.recordType){
8590         this.fields = this.recordType.prototype.fields;
8591     }
8592     this.modified = [];
8593
8594     this.addEvents({
8595         /**
8596          * @event datachanged
8597          * Fires when the data cache has changed, and a widget which is using this Store
8598          * as a Record cache should refresh its view.
8599          * @param {Store} this
8600          */
8601         datachanged : true,
8602         /**
8603          * @event metachange
8604          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8605          * @param {Store} this
8606          * @param {Object} meta The JSON metadata
8607          */
8608         metachange : true,
8609         /**
8610          * @event add
8611          * Fires when Records have been added to the Store
8612          * @param {Store} this
8613          * @param {Roo.data.Record[]} records The array of Records added
8614          * @param {Number} index The index at which the record(s) were added
8615          */
8616         add : true,
8617         /**
8618          * @event remove
8619          * Fires when a Record has been removed from the Store
8620          * @param {Store} this
8621          * @param {Roo.data.Record} record The Record that was removed
8622          * @param {Number} index The index at which the record was removed
8623          */
8624         remove : true,
8625         /**
8626          * @event update
8627          * Fires when a Record has been updated
8628          * @param {Store} this
8629          * @param {Roo.data.Record} record The Record that was updated
8630          * @param {String} operation The update operation being performed.  Value may be one of:
8631          * <pre><code>
8632  Roo.data.Record.EDIT
8633  Roo.data.Record.REJECT
8634  Roo.data.Record.COMMIT
8635          * </code></pre>
8636          */
8637         update : true,
8638         /**
8639          * @event clear
8640          * Fires when the data cache has been cleared.
8641          * @param {Store} this
8642          */
8643         clear : true,
8644         /**
8645          * @event beforeload
8646          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8647          * the load action will be canceled.
8648          * @param {Store} this
8649          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8650          */
8651         beforeload : true,
8652         /**
8653          * @event beforeloadadd
8654          * Fires after a new set of Records has been loaded.
8655          * @param {Store} this
8656          * @param {Roo.data.Record[]} records The Records that were loaded
8657          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8658          */
8659         beforeloadadd : true,
8660         /**
8661          * @event load
8662          * Fires after a new set of Records has been loaded, before they are added to the store.
8663          * @param {Store} this
8664          * @param {Roo.data.Record[]} records The Records that were loaded
8665          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8666          * @params {Object} return from reader
8667          */
8668         load : true,
8669         /**
8670          * @event loadexception
8671          * Fires if an exception occurs in the Proxy during loading.
8672          * Called with the signature of the Proxy's "loadexception" event.
8673          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8674          * 
8675          * @param {Proxy} 
8676          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8677          * @param {Object} load options 
8678          * @param {Object} jsonData from your request (normally this contains the Exception)
8679          */
8680         loadexception : true
8681     });
8682     
8683     if(this.proxy){
8684         this.proxy = Roo.factory(this.proxy, Roo.data);
8685         this.proxy.xmodule = this.xmodule || false;
8686         this.relayEvents(this.proxy,  ["loadexception"]);
8687     }
8688     this.sortToggle = {};
8689     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8690
8691     Roo.data.Store.superclass.constructor.call(this);
8692
8693     if(this.inlineData){
8694         this.loadData(this.inlineData);
8695         delete this.inlineData;
8696     }
8697 };
8698
8699 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8700      /**
8701     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8702     * without a remote query - used by combo/forms at present.
8703     */
8704     
8705     /**
8706     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8707     */
8708     /**
8709     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8710     */
8711     /**
8712     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8713     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8714     */
8715     /**
8716     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8717     * on any HTTP request
8718     */
8719     /**
8720     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8721     */
8722     /**
8723     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8724     */
8725     multiSort: false,
8726     /**
8727     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8728     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8729     */
8730     remoteSort : false,
8731
8732     /**
8733     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8734      * loaded or when a record is removed. (defaults to false).
8735     */
8736     pruneModifiedRecords : false,
8737
8738     // private
8739     lastOptions : null,
8740
8741     /**
8742      * Add Records to the Store and fires the add event.
8743      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8744      */
8745     add : function(records){
8746         records = [].concat(records);
8747         for(var i = 0, len = records.length; i < len; i++){
8748             records[i].join(this);
8749         }
8750         var index = this.data.length;
8751         this.data.addAll(records);
8752         this.fireEvent("add", this, records, index);
8753     },
8754
8755     /**
8756      * Remove a Record from the Store and fires the remove event.
8757      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8758      */
8759     remove : function(record){
8760         var index = this.data.indexOf(record);
8761         this.data.removeAt(index);
8762         if(this.pruneModifiedRecords){
8763             this.modified.remove(record);
8764         }
8765         this.fireEvent("remove", this, record, index);
8766     },
8767
8768     /**
8769      * Remove all Records from the Store and fires the clear event.
8770      */
8771     removeAll : function(){
8772         this.data.clear();
8773         if(this.pruneModifiedRecords){
8774             this.modified = [];
8775         }
8776         this.fireEvent("clear", this);
8777     },
8778
8779     /**
8780      * Inserts Records to the Store at the given index and fires the add event.
8781      * @param {Number} index The start index at which to insert the passed Records.
8782      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8783      */
8784     insert : function(index, records){
8785         records = [].concat(records);
8786         for(var i = 0, len = records.length; i < len; i++){
8787             this.data.insert(index, records[i]);
8788             records[i].join(this);
8789         }
8790         this.fireEvent("add", this, records, index);
8791     },
8792
8793     /**
8794      * Get the index within the cache of the passed Record.
8795      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8796      * @return {Number} The index of the passed Record. Returns -1 if not found.
8797      */
8798     indexOf : function(record){
8799         return this.data.indexOf(record);
8800     },
8801
8802     /**
8803      * Get the index within the cache of the Record with the passed id.
8804      * @param {String} id The id of the Record to find.
8805      * @return {Number} The index of the Record. Returns -1 if not found.
8806      */
8807     indexOfId : function(id){
8808         return this.data.indexOfKey(id);
8809     },
8810
8811     /**
8812      * Get the Record with the specified id.
8813      * @param {String} id The id of the Record to find.
8814      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8815      */
8816     getById : function(id){
8817         return this.data.key(id);
8818     },
8819
8820     /**
8821      * Get the Record at the specified index.
8822      * @param {Number} index The index of the Record to find.
8823      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8824      */
8825     getAt : function(index){
8826         return this.data.itemAt(index);
8827     },
8828
8829     /**
8830      * Returns a range of Records between specified indices.
8831      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8832      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8833      * @return {Roo.data.Record[]} An array of Records
8834      */
8835     getRange : function(start, end){
8836         return this.data.getRange(start, end);
8837     },
8838
8839     // private
8840     storeOptions : function(o){
8841         o = Roo.apply({}, o);
8842         delete o.callback;
8843         delete o.scope;
8844         this.lastOptions = o;
8845     },
8846
8847     /**
8848      * Loads the Record cache from the configured Proxy using the configured Reader.
8849      * <p>
8850      * If using remote paging, then the first load call must specify the <em>start</em>
8851      * and <em>limit</em> properties in the options.params property to establish the initial
8852      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8853      * <p>
8854      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8855      * and this call will return before the new data has been loaded. Perform any post-processing
8856      * in a callback function, or in a "load" event handler.</strong>
8857      * <p>
8858      * @param {Object} options An object containing properties which control loading options:<ul>
8859      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8860      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8861      * passed the following arguments:<ul>
8862      * <li>r : Roo.data.Record[]</li>
8863      * <li>options: Options object from the load call</li>
8864      * <li>success: Boolean success indicator</li></ul></li>
8865      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8866      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8867      * </ul>
8868      */
8869     load : function(options){
8870         options = options || {};
8871         if(this.fireEvent("beforeload", this, options) !== false){
8872             this.storeOptions(options);
8873             var p = Roo.apply(options.params || {}, this.baseParams);
8874             // if meta was not loaded from remote source.. try requesting it.
8875             if (!this.reader.metaFromRemote) {
8876                 p._requestMeta = 1;
8877             }
8878             if(this.sortInfo && this.remoteSort){
8879                 var pn = this.paramNames;
8880                 p[pn["sort"]] = this.sortInfo.field;
8881                 p[pn["dir"]] = this.sortInfo.direction;
8882             }
8883             if (this.multiSort) {
8884                 var pn = this.paramNames;
8885                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8886             }
8887             
8888             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8889         }
8890     },
8891
8892     /**
8893      * Reloads the Record cache from the configured Proxy using the configured Reader and
8894      * the options from the last load operation performed.
8895      * @param {Object} options (optional) An object containing properties which may override the options
8896      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8897      * the most recently used options are reused).
8898      */
8899     reload : function(options){
8900         this.load(Roo.applyIf(options||{}, this.lastOptions));
8901     },
8902
8903     // private
8904     // Called as a callback by the Reader during a load operation.
8905     loadRecords : function(o, options, success){
8906         if(!o || success === false){
8907             if(success !== false){
8908                 this.fireEvent("load", this, [], options, o);
8909             }
8910             if(options.callback){
8911                 options.callback.call(options.scope || this, [], options, false);
8912             }
8913             return;
8914         }
8915         // if data returned failure - throw an exception.
8916         if (o.success === false) {
8917             // show a message if no listener is registered.
8918             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8919                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8920             }
8921             // loadmask wil be hooked into this..
8922             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8923             return;
8924         }
8925         var r = o.records, t = o.totalRecords || r.length;
8926         
8927         this.fireEvent("beforeloadadd", this, r, options, o);
8928         
8929         if(!options || options.add !== true){
8930             if(this.pruneModifiedRecords){
8931                 this.modified = [];
8932             }
8933             for(var i = 0, len = r.length; i < len; i++){
8934                 r[i].join(this);
8935             }
8936             if(this.snapshot){
8937                 this.data = this.snapshot;
8938                 delete this.snapshot;
8939             }
8940             this.data.clear();
8941             this.data.addAll(r);
8942             this.totalLength = t;
8943             this.applySort();
8944             this.fireEvent("datachanged", this);
8945         }else{
8946             this.totalLength = Math.max(t, this.data.length+r.length);
8947             this.add(r);
8948         }
8949         this.fireEvent("load", this, r, options, o);
8950         if(options.callback){
8951             options.callback.call(options.scope || this, r, options, true);
8952         }
8953     },
8954
8955
8956     /**
8957      * Loads data from a passed data block. A Reader which understands the format of the data
8958      * must have been configured in the constructor.
8959      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8960      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8961      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8962      */
8963     loadData : function(o, append){
8964         var r = this.reader.readRecords(o);
8965         this.loadRecords(r, {add: append}, true);
8966     },
8967
8968     /**
8969      * Gets the number of cached records.
8970      * <p>
8971      * <em>If using paging, this may not be the total size of the dataset. If the data object
8972      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8973      * the data set size</em>
8974      */
8975     getCount : function(){
8976         return this.data.length || 0;
8977     },
8978
8979     /**
8980      * Gets the total number of records in the dataset as returned by the server.
8981      * <p>
8982      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8983      * the dataset size</em>
8984      */
8985     getTotalCount : function(){
8986         return this.totalLength || 0;
8987     },
8988
8989     /**
8990      * Returns the sort state of the Store as an object with two properties:
8991      * <pre><code>
8992  field {String} The name of the field by which the Records are sorted
8993  direction {String} The sort order, "ASC" or "DESC"
8994      * </code></pre>
8995      */
8996     getSortState : function(){
8997         return this.sortInfo;
8998     },
8999
9000     // private
9001     applySort : function(){
9002         if(this.sortInfo && !this.remoteSort){
9003             var s = this.sortInfo, f = s.field;
9004             var st = this.fields.get(f).sortType;
9005             var fn = function(r1, r2){
9006                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9007                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9008             };
9009             this.data.sort(s.direction, fn);
9010             if(this.snapshot && this.snapshot != this.data){
9011                 this.snapshot.sort(s.direction, fn);
9012             }
9013         }
9014     },
9015
9016     /**
9017      * Sets the default sort column and order to be used by the next load operation.
9018      * @param {String} fieldName The name of the field to sort by.
9019      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9020      */
9021     setDefaultSort : function(field, dir){
9022         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9023     },
9024
9025     /**
9026      * Sort the Records.
9027      * If remote sorting is used, the sort is performed on the server, and the cache is
9028      * reloaded. If local sorting is used, the cache is sorted internally.
9029      * @param {String} fieldName The name of the field to sort by.
9030      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9031      */
9032     sort : function(fieldName, dir){
9033         var f = this.fields.get(fieldName);
9034         if(!dir){
9035             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9036             
9037             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9038                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9039             }else{
9040                 dir = f.sortDir;
9041             }
9042         }
9043         this.sortToggle[f.name] = dir;
9044         this.sortInfo = {field: f.name, direction: dir};
9045         if(!this.remoteSort){
9046             this.applySort();
9047             this.fireEvent("datachanged", this);
9048         }else{
9049             this.load(this.lastOptions);
9050         }
9051     },
9052
9053     /**
9054      * Calls the specified function for each of the Records in the cache.
9055      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9056      * Returning <em>false</em> aborts and exits the iteration.
9057      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9058      */
9059     each : function(fn, scope){
9060         this.data.each(fn, scope);
9061     },
9062
9063     /**
9064      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9065      * (e.g., during paging).
9066      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9067      */
9068     getModifiedRecords : function(){
9069         return this.modified;
9070     },
9071
9072     // private
9073     createFilterFn : function(property, value, anyMatch){
9074         if(!value.exec){ // not a regex
9075             value = String(value);
9076             if(value.length == 0){
9077                 return false;
9078             }
9079             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9080         }
9081         return function(r){
9082             return value.test(r.data[property]);
9083         };
9084     },
9085
9086     /**
9087      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9088      * @param {String} property A field on your records
9089      * @param {Number} start The record index to start at (defaults to 0)
9090      * @param {Number} end The last record index to include (defaults to length - 1)
9091      * @return {Number} The sum
9092      */
9093     sum : function(property, start, end){
9094         var rs = this.data.items, v = 0;
9095         start = start || 0;
9096         end = (end || end === 0) ? end : rs.length-1;
9097
9098         for(var i = start; i <= end; i++){
9099             v += (rs[i].data[property] || 0);
9100         }
9101         return v;
9102     },
9103
9104     /**
9105      * Filter the records by a specified property.
9106      * @param {String} field A field on your records
9107      * @param {String/RegExp} value Either a string that the field
9108      * should start with or a RegExp to test against the field
9109      * @param {Boolean} anyMatch True to match any part not just the beginning
9110      */
9111     filter : function(property, value, anyMatch){
9112         var fn = this.createFilterFn(property, value, anyMatch);
9113         return fn ? this.filterBy(fn) : this.clearFilter();
9114     },
9115
9116     /**
9117      * Filter by a function. The specified function will be called with each
9118      * record in this data source. If the function returns true the record is included,
9119      * otherwise it is filtered.
9120      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9121      * @param {Object} scope (optional) The scope of the function (defaults to this)
9122      */
9123     filterBy : function(fn, scope){
9124         this.snapshot = this.snapshot || this.data;
9125         this.data = this.queryBy(fn, scope||this);
9126         this.fireEvent("datachanged", this);
9127     },
9128
9129     /**
9130      * Query the records by a specified property.
9131      * @param {String} field A field on your records
9132      * @param {String/RegExp} value Either a string that the field
9133      * should start with or a RegExp to test against the field
9134      * @param {Boolean} anyMatch True to match any part not just the beginning
9135      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9136      */
9137     query : function(property, value, anyMatch){
9138         var fn = this.createFilterFn(property, value, anyMatch);
9139         return fn ? this.queryBy(fn) : this.data.clone();
9140     },
9141
9142     /**
9143      * Query by a function. The specified function will be called with each
9144      * record in this data source. If the function returns true the record is included
9145      * in the results.
9146      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9147      * @param {Object} scope (optional) The scope of the function (defaults to this)
9148       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9149      **/
9150     queryBy : function(fn, scope){
9151         var data = this.snapshot || this.data;
9152         return data.filterBy(fn, scope||this);
9153     },
9154
9155     /**
9156      * Collects unique values for a particular dataIndex from this store.
9157      * @param {String} dataIndex The property to collect
9158      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9159      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9160      * @return {Array} An array of the unique values
9161      **/
9162     collect : function(dataIndex, allowNull, bypassFilter){
9163         var d = (bypassFilter === true && this.snapshot) ?
9164                 this.snapshot.items : this.data.items;
9165         var v, sv, r = [], l = {};
9166         for(var i = 0, len = d.length; i < len; i++){
9167             v = d[i].data[dataIndex];
9168             sv = String(v);
9169             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9170                 l[sv] = true;
9171                 r[r.length] = v;
9172             }
9173         }
9174         return r;
9175     },
9176
9177     /**
9178      * Revert to a view of the Record cache with no filtering applied.
9179      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9180      */
9181     clearFilter : function(suppressEvent){
9182         if(this.snapshot && this.snapshot != this.data){
9183             this.data = this.snapshot;
9184             delete this.snapshot;
9185             if(suppressEvent !== true){
9186                 this.fireEvent("datachanged", this);
9187             }
9188         }
9189     },
9190
9191     // private
9192     afterEdit : function(record){
9193         if(this.modified.indexOf(record) == -1){
9194             this.modified.push(record);
9195         }
9196         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9197     },
9198     
9199     // private
9200     afterReject : function(record){
9201         this.modified.remove(record);
9202         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9203     },
9204
9205     // private
9206     afterCommit : function(record){
9207         this.modified.remove(record);
9208         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9209     },
9210
9211     /**
9212      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9213      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9214      */
9215     commitChanges : function(){
9216         var m = this.modified.slice(0);
9217         this.modified = [];
9218         for(var i = 0, len = m.length; i < len; i++){
9219             m[i].commit();
9220         }
9221     },
9222
9223     /**
9224      * Cancel outstanding changes on all changed records.
9225      */
9226     rejectChanges : function(){
9227         var m = this.modified.slice(0);
9228         this.modified = [];
9229         for(var i = 0, len = m.length; i < len; i++){
9230             m[i].reject();
9231         }
9232     },
9233
9234     onMetaChange : function(meta, rtype, o){
9235         this.recordType = rtype;
9236         this.fields = rtype.prototype.fields;
9237         delete this.snapshot;
9238         this.sortInfo = meta.sortInfo || this.sortInfo;
9239         this.modified = [];
9240         this.fireEvent('metachange', this, this.reader.meta);
9241     },
9242     
9243     moveIndex : function(data, type)
9244     {
9245         var index = this.indexOf(data);
9246         
9247         var newIndex = index + type;
9248         
9249         this.remove(data);
9250         
9251         this.insert(newIndex, data);
9252         
9253     }
9254 });/*
9255  * Based on:
9256  * Ext JS Library 1.1.1
9257  * Copyright(c) 2006-2007, Ext JS, LLC.
9258  *
9259  * Originally Released Under LGPL - original licence link has changed is not relivant.
9260  *
9261  * Fork - LGPL
9262  * <script type="text/javascript">
9263  */
9264
9265 /**
9266  * @class Roo.data.SimpleStore
9267  * @extends Roo.data.Store
9268  * Small helper class to make creating Stores from Array data easier.
9269  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9270  * @cfg {Array} fields An array of field definition objects, or field name strings.
9271  * @cfg {Array} data The multi-dimensional array of data
9272  * @constructor
9273  * @param {Object} config
9274  */
9275 Roo.data.SimpleStore = function(config){
9276     Roo.data.SimpleStore.superclass.constructor.call(this, {
9277         isLocal : true,
9278         reader: new Roo.data.ArrayReader({
9279                 id: config.id
9280             },
9281             Roo.data.Record.create(config.fields)
9282         ),
9283         proxy : new Roo.data.MemoryProxy(config.data)
9284     });
9285     this.load();
9286 };
9287 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9288  * Based on:
9289  * Ext JS Library 1.1.1
9290  * Copyright(c) 2006-2007, Ext JS, LLC.
9291  *
9292  * Originally Released Under LGPL - original licence link has changed is not relivant.
9293  *
9294  * Fork - LGPL
9295  * <script type="text/javascript">
9296  */
9297
9298 /**
9299 /**
9300  * @extends Roo.data.Store
9301  * @class Roo.data.JsonStore
9302  * Small helper class to make creating Stores for JSON data easier. <br/>
9303 <pre><code>
9304 var store = new Roo.data.JsonStore({
9305     url: 'get-images.php',
9306     root: 'images',
9307     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9308 });
9309 </code></pre>
9310  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9311  * JsonReader and HttpProxy (unless inline data is provided).</b>
9312  * @cfg {Array} fields An array of field definition objects, or field name strings.
9313  * @constructor
9314  * @param {Object} config
9315  */
9316 Roo.data.JsonStore = function(c){
9317     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9318         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9319         reader: new Roo.data.JsonReader(c, c.fields)
9320     }));
9321 };
9322 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9323  * Based on:
9324  * Ext JS Library 1.1.1
9325  * Copyright(c) 2006-2007, Ext JS, LLC.
9326  *
9327  * Originally Released Under LGPL - original licence link has changed is not relivant.
9328  *
9329  * Fork - LGPL
9330  * <script type="text/javascript">
9331  */
9332
9333  
9334 Roo.data.Field = function(config){
9335     if(typeof config == "string"){
9336         config = {name: config};
9337     }
9338     Roo.apply(this, config);
9339     
9340     if(!this.type){
9341         this.type = "auto";
9342     }
9343     
9344     var st = Roo.data.SortTypes;
9345     // named sortTypes are supported, here we look them up
9346     if(typeof this.sortType == "string"){
9347         this.sortType = st[this.sortType];
9348     }
9349     
9350     // set default sortType for strings and dates
9351     if(!this.sortType){
9352         switch(this.type){
9353             case "string":
9354                 this.sortType = st.asUCString;
9355                 break;
9356             case "date":
9357                 this.sortType = st.asDate;
9358                 break;
9359             default:
9360                 this.sortType = st.none;
9361         }
9362     }
9363
9364     // define once
9365     var stripRe = /[\$,%]/g;
9366
9367     // prebuilt conversion function for this field, instead of
9368     // switching every time we're reading a value
9369     if(!this.convert){
9370         var cv, dateFormat = this.dateFormat;
9371         switch(this.type){
9372             case "":
9373             case "auto":
9374             case undefined:
9375                 cv = function(v){ return v; };
9376                 break;
9377             case "string":
9378                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9379                 break;
9380             case "int":
9381                 cv = function(v){
9382                     return v !== undefined && v !== null && v !== '' ?
9383                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9384                     };
9385                 break;
9386             case "float":
9387                 cv = function(v){
9388                     return v !== undefined && v !== null && v !== '' ?
9389                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9390                     };
9391                 break;
9392             case "bool":
9393             case "boolean":
9394                 cv = function(v){ return v === true || v === "true" || v == 1; };
9395                 break;
9396             case "date":
9397                 cv = function(v){
9398                     if(!v){
9399                         return '';
9400                     }
9401                     if(v instanceof Date){
9402                         return v;
9403                     }
9404                     if(dateFormat){
9405                         if(dateFormat == "timestamp"){
9406                             return new Date(v*1000);
9407                         }
9408                         return Date.parseDate(v, dateFormat);
9409                     }
9410                     var parsed = Date.parse(v);
9411                     return parsed ? new Date(parsed) : null;
9412                 };
9413              break;
9414             
9415         }
9416         this.convert = cv;
9417     }
9418 };
9419
9420 Roo.data.Field.prototype = {
9421     dateFormat: null,
9422     defaultValue: "",
9423     mapping: null,
9424     sortType : null,
9425     sortDir : "ASC"
9426 };/*
9427  * Based on:
9428  * Ext JS Library 1.1.1
9429  * Copyright(c) 2006-2007, Ext JS, LLC.
9430  *
9431  * Originally Released Under LGPL - original licence link has changed is not relivant.
9432  *
9433  * Fork - LGPL
9434  * <script type="text/javascript">
9435  */
9436  
9437 // Base class for reading structured data from a data source.  This class is intended to be
9438 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9439
9440 /**
9441  * @class Roo.data.DataReader
9442  * Base class for reading structured data from a data source.  This class is intended to be
9443  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9444  */
9445
9446 Roo.data.DataReader = function(meta, recordType){
9447     
9448     this.meta = meta;
9449     
9450     this.recordType = recordType instanceof Array ? 
9451         Roo.data.Record.create(recordType) : recordType;
9452 };
9453
9454 Roo.data.DataReader.prototype = {
9455      /**
9456      * Create an empty record
9457      * @param {Object} data (optional) - overlay some values
9458      * @return {Roo.data.Record} record created.
9459      */
9460     newRow :  function(d) {
9461         var da =  {};
9462         this.recordType.prototype.fields.each(function(c) {
9463             switch( c.type) {
9464                 case 'int' : da[c.name] = 0; break;
9465                 case 'date' : da[c.name] = new Date(); break;
9466                 case 'float' : da[c.name] = 0.0; break;
9467                 case 'boolean' : da[c.name] = false; break;
9468                 default : da[c.name] = ""; break;
9469             }
9470             
9471         });
9472         return new this.recordType(Roo.apply(da, d));
9473     }
9474     
9475 };/*
9476  * Based on:
9477  * Ext JS Library 1.1.1
9478  * Copyright(c) 2006-2007, Ext JS, LLC.
9479  *
9480  * Originally Released Under LGPL - original licence link has changed is not relivant.
9481  *
9482  * Fork - LGPL
9483  * <script type="text/javascript">
9484  */
9485
9486 /**
9487  * @class Roo.data.DataProxy
9488  * @extends Roo.data.Observable
9489  * This class is an abstract base class for implementations which provide retrieval of
9490  * unformatted data objects.<br>
9491  * <p>
9492  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9493  * (of the appropriate type which knows how to parse the data object) to provide a block of
9494  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9495  * <p>
9496  * Custom implementations must implement the load method as described in
9497  * {@link Roo.data.HttpProxy#load}.
9498  */
9499 Roo.data.DataProxy = function(){
9500     this.addEvents({
9501         /**
9502          * @event beforeload
9503          * Fires before a network request is made to retrieve a data object.
9504          * @param {Object} This DataProxy object.
9505          * @param {Object} params The params parameter to the load function.
9506          */
9507         beforeload : true,
9508         /**
9509          * @event load
9510          * Fires before the load method's callback is called.
9511          * @param {Object} This DataProxy object.
9512          * @param {Object} o The data object.
9513          * @param {Object} arg The callback argument object passed to the load function.
9514          */
9515         load : true,
9516         /**
9517          * @event loadexception
9518          * Fires if an Exception occurs during data retrieval.
9519          * @param {Object} This DataProxy object.
9520          * @param {Object} o The data object.
9521          * @param {Object} arg The callback argument object passed to the load function.
9522          * @param {Object} e The Exception.
9523          */
9524         loadexception : true
9525     });
9526     Roo.data.DataProxy.superclass.constructor.call(this);
9527 };
9528
9529 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9530
9531     /**
9532      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9533      */
9534 /*
9535  * Based on:
9536  * Ext JS Library 1.1.1
9537  * Copyright(c) 2006-2007, Ext JS, LLC.
9538  *
9539  * Originally Released Under LGPL - original licence link has changed is not relivant.
9540  *
9541  * Fork - LGPL
9542  * <script type="text/javascript">
9543  */
9544 /**
9545  * @class Roo.data.MemoryProxy
9546  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9547  * to the Reader when its load method is called.
9548  * @constructor
9549  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9550  */
9551 Roo.data.MemoryProxy = function(data){
9552     if (data.data) {
9553         data = data.data;
9554     }
9555     Roo.data.MemoryProxy.superclass.constructor.call(this);
9556     this.data = data;
9557 };
9558
9559 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9560     /**
9561      * Load data from the requested source (in this case an in-memory
9562      * data object passed to the constructor), read the data object into
9563      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9564      * process that block using the passed callback.
9565      * @param {Object} params This parameter is not used by the MemoryProxy class.
9566      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9567      * object into a block of Roo.data.Records.
9568      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9569      * The function must be passed <ul>
9570      * <li>The Record block object</li>
9571      * <li>The "arg" argument from the load function</li>
9572      * <li>A boolean success indicator</li>
9573      * </ul>
9574      * @param {Object} scope The scope in which to call the callback
9575      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9576      */
9577     load : function(params, reader, callback, scope, arg){
9578         params = params || {};
9579         var result;
9580         try {
9581             result = reader.readRecords(this.data);
9582         }catch(e){
9583             this.fireEvent("loadexception", this, arg, null, e);
9584             callback.call(scope, null, arg, false);
9585             return;
9586         }
9587         callback.call(scope, result, arg, true);
9588     },
9589     
9590     // private
9591     update : function(params, records){
9592         
9593     }
9594 });/*
9595  * Based on:
9596  * Ext JS Library 1.1.1
9597  * Copyright(c) 2006-2007, Ext JS, LLC.
9598  *
9599  * Originally Released Under LGPL - original licence link has changed is not relivant.
9600  *
9601  * Fork - LGPL
9602  * <script type="text/javascript">
9603  */
9604 /**
9605  * @class Roo.data.HttpProxy
9606  * @extends Roo.data.DataProxy
9607  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9608  * configured to reference a certain URL.<br><br>
9609  * <p>
9610  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9611  * from which the running page was served.<br><br>
9612  * <p>
9613  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9614  * <p>
9615  * Be aware that to enable the browser to parse an XML document, the server must set
9616  * the Content-Type header in the HTTP response to "text/xml".
9617  * @constructor
9618  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9619  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9620  * will be used to make the request.
9621  */
9622 Roo.data.HttpProxy = function(conn){
9623     Roo.data.HttpProxy.superclass.constructor.call(this);
9624     // is conn a conn config or a real conn?
9625     this.conn = conn;
9626     this.useAjax = !conn || !conn.events;
9627   
9628 };
9629
9630 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9631     // thse are take from connection...
9632     
9633     /**
9634      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9635      */
9636     /**
9637      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9638      * extra parameters to each request made by this object. (defaults to undefined)
9639      */
9640     /**
9641      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9642      *  to each request made by this object. (defaults to undefined)
9643      */
9644     /**
9645      * @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)
9646      */
9647     /**
9648      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9649      */
9650      /**
9651      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9652      * @type Boolean
9653      */
9654   
9655
9656     /**
9657      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9658      * @type Boolean
9659      */
9660     /**
9661      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9662      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9663      * a finer-grained basis than the DataProxy events.
9664      */
9665     getConnection : function(){
9666         return this.useAjax ? Roo.Ajax : this.conn;
9667     },
9668
9669     /**
9670      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9671      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9672      * process that block using the passed callback.
9673      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9674      * for the request to the remote server.
9675      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9676      * object into a block of Roo.data.Records.
9677      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9678      * The function must be passed <ul>
9679      * <li>The Record block object</li>
9680      * <li>The "arg" argument from the load function</li>
9681      * <li>A boolean success indicator</li>
9682      * </ul>
9683      * @param {Object} scope The scope in which to call the callback
9684      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9685      */
9686     load : function(params, reader, callback, scope, arg){
9687         if(this.fireEvent("beforeload", this, params) !== false){
9688             var  o = {
9689                 params : params || {},
9690                 request: {
9691                     callback : callback,
9692                     scope : scope,
9693                     arg : arg
9694                 },
9695                 reader: reader,
9696                 callback : this.loadResponse,
9697                 scope: this
9698             };
9699             if(this.useAjax){
9700                 Roo.applyIf(o, this.conn);
9701                 if(this.activeRequest){
9702                     Roo.Ajax.abort(this.activeRequest);
9703                 }
9704                 this.activeRequest = Roo.Ajax.request(o);
9705             }else{
9706                 this.conn.request(o);
9707             }
9708         }else{
9709             callback.call(scope||this, null, arg, false);
9710         }
9711     },
9712
9713     // private
9714     loadResponse : function(o, success, response){
9715         delete this.activeRequest;
9716         if(!success){
9717             this.fireEvent("loadexception", this, o, response);
9718             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9719             return;
9720         }
9721         var result;
9722         try {
9723             result = o.reader.read(response);
9724         }catch(e){
9725             this.fireEvent("loadexception", this, o, response, e);
9726             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9727             return;
9728         }
9729         
9730         this.fireEvent("load", this, o, o.request.arg);
9731         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9732     },
9733
9734     // private
9735     update : function(dataSet){
9736
9737     },
9738
9739     // private
9740     updateResponse : function(dataSet){
9741
9742     }
9743 });/*
9744  * Based on:
9745  * Ext JS Library 1.1.1
9746  * Copyright(c) 2006-2007, Ext JS, LLC.
9747  *
9748  * Originally Released Under LGPL - original licence link has changed is not relivant.
9749  *
9750  * Fork - LGPL
9751  * <script type="text/javascript">
9752  */
9753
9754 /**
9755  * @class Roo.data.ScriptTagProxy
9756  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9757  * other than the originating domain of the running page.<br><br>
9758  * <p>
9759  * <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
9760  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9761  * <p>
9762  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9763  * source code that is used as the source inside a &lt;script> tag.<br><br>
9764  * <p>
9765  * In order for the browser to process the returned data, the server must wrap the data object
9766  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9767  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9768  * depending on whether the callback name was passed:
9769  * <p>
9770  * <pre><code>
9771 boolean scriptTag = false;
9772 String cb = request.getParameter("callback");
9773 if (cb != null) {
9774     scriptTag = true;
9775     response.setContentType("text/javascript");
9776 } else {
9777     response.setContentType("application/x-json");
9778 }
9779 Writer out = response.getWriter();
9780 if (scriptTag) {
9781     out.write(cb + "(");
9782 }
9783 out.print(dataBlock.toJsonString());
9784 if (scriptTag) {
9785     out.write(");");
9786 }
9787 </pre></code>
9788  *
9789  * @constructor
9790  * @param {Object} config A configuration object.
9791  */
9792 Roo.data.ScriptTagProxy = function(config){
9793     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9794     Roo.apply(this, config);
9795     this.head = document.getElementsByTagName("head")[0];
9796 };
9797
9798 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9799
9800 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9801     /**
9802      * @cfg {String} url The URL from which to request the data object.
9803      */
9804     /**
9805      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9806      */
9807     timeout : 30000,
9808     /**
9809      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9810      * the server the name of the callback function set up by the load call to process the returned data object.
9811      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9812      * javascript output which calls this named function passing the data object as its only parameter.
9813      */
9814     callbackParam : "callback",
9815     /**
9816      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9817      * name to the request.
9818      */
9819     nocache : true,
9820
9821     /**
9822      * Load data from the configured URL, read the data object into
9823      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9824      * process that block using the passed callback.
9825      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9826      * for the request to the remote server.
9827      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9828      * object into a block of Roo.data.Records.
9829      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9830      * The function must be passed <ul>
9831      * <li>The Record block object</li>
9832      * <li>The "arg" argument from the load function</li>
9833      * <li>A boolean success indicator</li>
9834      * </ul>
9835      * @param {Object} scope The scope in which to call the callback
9836      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9837      */
9838     load : function(params, reader, callback, scope, arg){
9839         if(this.fireEvent("beforeload", this, params) !== false){
9840
9841             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9842
9843             var url = this.url;
9844             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9845             if(this.nocache){
9846                 url += "&_dc=" + (new Date().getTime());
9847             }
9848             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9849             var trans = {
9850                 id : transId,
9851                 cb : "stcCallback"+transId,
9852                 scriptId : "stcScript"+transId,
9853                 params : params,
9854                 arg : arg,
9855                 url : url,
9856                 callback : callback,
9857                 scope : scope,
9858                 reader : reader
9859             };
9860             var conn = this;
9861
9862             window[trans.cb] = function(o){
9863                 conn.handleResponse(o, trans);
9864             };
9865
9866             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9867
9868             if(this.autoAbort !== false){
9869                 this.abort();
9870             }
9871
9872             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9873
9874             var script = document.createElement("script");
9875             script.setAttribute("src", url);
9876             script.setAttribute("type", "text/javascript");
9877             script.setAttribute("id", trans.scriptId);
9878             this.head.appendChild(script);
9879
9880             this.trans = trans;
9881         }else{
9882             callback.call(scope||this, null, arg, false);
9883         }
9884     },
9885
9886     // private
9887     isLoading : function(){
9888         return this.trans ? true : false;
9889     },
9890
9891     /**
9892      * Abort the current server request.
9893      */
9894     abort : function(){
9895         if(this.isLoading()){
9896             this.destroyTrans(this.trans);
9897         }
9898     },
9899
9900     // private
9901     destroyTrans : function(trans, isLoaded){
9902         this.head.removeChild(document.getElementById(trans.scriptId));
9903         clearTimeout(trans.timeoutId);
9904         if(isLoaded){
9905             window[trans.cb] = undefined;
9906             try{
9907                 delete window[trans.cb];
9908             }catch(e){}
9909         }else{
9910             // if hasn't been loaded, wait for load to remove it to prevent script error
9911             window[trans.cb] = function(){
9912                 window[trans.cb] = undefined;
9913                 try{
9914                     delete window[trans.cb];
9915                 }catch(e){}
9916             };
9917         }
9918     },
9919
9920     // private
9921     handleResponse : function(o, trans){
9922         this.trans = false;
9923         this.destroyTrans(trans, true);
9924         var result;
9925         try {
9926             result = trans.reader.readRecords(o);
9927         }catch(e){
9928             this.fireEvent("loadexception", this, o, trans.arg, e);
9929             trans.callback.call(trans.scope||window, null, trans.arg, false);
9930             return;
9931         }
9932         this.fireEvent("load", this, o, trans.arg);
9933         trans.callback.call(trans.scope||window, result, trans.arg, true);
9934     },
9935
9936     // private
9937     handleFailure : function(trans){
9938         this.trans = false;
9939         this.destroyTrans(trans, false);
9940         this.fireEvent("loadexception", this, null, trans.arg);
9941         trans.callback.call(trans.scope||window, null, trans.arg, false);
9942     }
9943 });/*
9944  * Based on:
9945  * Ext JS Library 1.1.1
9946  * Copyright(c) 2006-2007, Ext JS, LLC.
9947  *
9948  * Originally Released Under LGPL - original licence link has changed is not relivant.
9949  *
9950  * Fork - LGPL
9951  * <script type="text/javascript">
9952  */
9953
9954 /**
9955  * @class Roo.data.JsonReader
9956  * @extends Roo.data.DataReader
9957  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9958  * based on mappings in a provided Roo.data.Record constructor.
9959  * 
9960  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9961  * in the reply previously. 
9962  * 
9963  * <p>
9964  * Example code:
9965  * <pre><code>
9966 var RecordDef = Roo.data.Record.create([
9967     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9968     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9969 ]);
9970 var myReader = new Roo.data.JsonReader({
9971     totalProperty: "results",    // The property which contains the total dataset size (optional)
9972     root: "rows",                // The property which contains an Array of row objects
9973     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9974 }, RecordDef);
9975 </code></pre>
9976  * <p>
9977  * This would consume a JSON file like this:
9978  * <pre><code>
9979 { 'results': 2, 'rows': [
9980     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9981     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9982 }
9983 </code></pre>
9984  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9985  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9986  * paged from the remote server.
9987  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9988  * @cfg {String} root name of the property which contains the Array of row objects.
9989  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9990  * @constructor
9991  * Create a new JsonReader
9992  * @param {Object} meta Metadata configuration options
9993  * @param {Object} recordType Either an Array of field definition objects,
9994  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9995  */
9996 Roo.data.JsonReader = function(meta, recordType){
9997     
9998     meta = meta || {};
9999     // set some defaults:
10000     Roo.applyIf(meta, {
10001         totalProperty: 'total',
10002         successProperty : 'success',
10003         root : 'data',
10004         id : 'id'
10005     });
10006     
10007     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10008 };
10009 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10010     
10011     /**
10012      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10013      * Used by Store query builder to append _requestMeta to params.
10014      * 
10015      */
10016     metaFromRemote : false,
10017     /**
10018      * This method is only used by a DataProxy which has retrieved data from a remote server.
10019      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10020      * @return {Object} data A data block which is used by an Roo.data.Store object as
10021      * a cache of Roo.data.Records.
10022      */
10023     read : function(response){
10024         var json = response.responseText;
10025        
10026         var o = /* eval:var:o */ eval("("+json+")");
10027         if(!o) {
10028             throw {message: "JsonReader.read: Json object not found"};
10029         }
10030         
10031         if(o.metaData){
10032             
10033             delete this.ef;
10034             this.metaFromRemote = true;
10035             this.meta = o.metaData;
10036             this.recordType = Roo.data.Record.create(o.metaData.fields);
10037             this.onMetaChange(this.meta, this.recordType, o);
10038         }
10039         return this.readRecords(o);
10040     },
10041
10042     // private function a store will implement
10043     onMetaChange : function(meta, recordType, o){
10044
10045     },
10046
10047     /**
10048          * @ignore
10049          */
10050     simpleAccess: function(obj, subsc) {
10051         return obj[subsc];
10052     },
10053
10054         /**
10055          * @ignore
10056          */
10057     getJsonAccessor: function(){
10058         var re = /[\[\.]/;
10059         return function(expr) {
10060             try {
10061                 return(re.test(expr))
10062                     ? new Function("obj", "return obj." + expr)
10063                     : function(obj){
10064                         return obj[expr];
10065                     };
10066             } catch(e){}
10067             return Roo.emptyFn;
10068         };
10069     }(),
10070
10071     /**
10072      * Create a data block containing Roo.data.Records from an XML document.
10073      * @param {Object} o An object which contains an Array of row objects in the property specified
10074      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10075      * which contains the total size of the dataset.
10076      * @return {Object} data A data block which is used by an Roo.data.Store object as
10077      * a cache of Roo.data.Records.
10078      */
10079     readRecords : function(o){
10080         /**
10081          * After any data loads, the raw JSON data is available for further custom processing.
10082          * @type Object
10083          */
10084         this.o = o;
10085         var s = this.meta, Record = this.recordType,
10086             f = Record.prototype.fields, fi = f.items, fl = f.length;
10087
10088 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10089         if (!this.ef) {
10090             if(s.totalProperty) {
10091                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10092                 }
10093                 if(s.successProperty) {
10094                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10095                 }
10096                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10097                 if (s.id) {
10098                         var g = this.getJsonAccessor(s.id);
10099                         this.getId = function(rec) {
10100                                 var r = g(rec);
10101                                 return (r === undefined || r === "") ? null : r;
10102                         };
10103                 } else {
10104                         this.getId = function(){return null;};
10105                 }
10106             this.ef = [];
10107             for(var jj = 0; jj < fl; jj++){
10108                 f = fi[jj];
10109                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10110                 this.ef[jj] = this.getJsonAccessor(map);
10111             }
10112         }
10113
10114         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10115         if(s.totalProperty){
10116             var vt = parseInt(this.getTotal(o), 10);
10117             if(!isNaN(vt)){
10118                 totalRecords = vt;
10119             }
10120         }
10121         if(s.successProperty){
10122             var vs = this.getSuccess(o);
10123             if(vs === false || vs === 'false'){
10124                 success = false;
10125             }
10126         }
10127         var records = [];
10128             for(var i = 0; i < c; i++){
10129                     var n = root[i];
10130                 var values = {};
10131                 var id = this.getId(n);
10132                 for(var j = 0; j < fl; j++){
10133                     f = fi[j];
10134                 var v = this.ef[j](n);
10135                 if (!f.convert) {
10136                     Roo.log('missing convert for ' + f.name);
10137                     Roo.log(f);
10138                     continue;
10139                 }
10140                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10141                 }
10142                 var record = new Record(values, id);
10143                 record.json = n;
10144                 records[i] = record;
10145             }
10146             return {
10147             raw : o,
10148                 success : success,
10149                 records : records,
10150                 totalRecords : totalRecords
10151             };
10152     }
10153 });/*
10154  * Based on:
10155  * Ext JS Library 1.1.1
10156  * Copyright(c) 2006-2007, Ext JS, LLC.
10157  *
10158  * Originally Released Under LGPL - original licence link has changed is not relivant.
10159  *
10160  * Fork - LGPL
10161  * <script type="text/javascript">
10162  */
10163
10164 /**
10165  * @class Roo.data.ArrayReader
10166  * @extends Roo.data.DataReader
10167  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10168  * Each element of that Array represents a row of data fields. The
10169  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10170  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10171  * <p>
10172  * Example code:.
10173  * <pre><code>
10174 var RecordDef = Roo.data.Record.create([
10175     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10176     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10177 ]);
10178 var myReader = new Roo.data.ArrayReader({
10179     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10180 }, RecordDef);
10181 </code></pre>
10182  * <p>
10183  * This would consume an Array like this:
10184  * <pre><code>
10185 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10186   </code></pre>
10187  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10188  * @constructor
10189  * Create a new JsonReader
10190  * @param {Object} meta Metadata configuration options.
10191  * @param {Object} recordType Either an Array of field definition objects
10192  * as specified to {@link Roo.data.Record#create},
10193  * or an {@link Roo.data.Record} object
10194  * created using {@link Roo.data.Record#create}.
10195  */
10196 Roo.data.ArrayReader = function(meta, recordType){
10197     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10198 };
10199
10200 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10201     /**
10202      * Create a data block containing Roo.data.Records from an XML document.
10203      * @param {Object} o An Array of row objects which represents the dataset.
10204      * @return {Object} data A data block which is used by an Roo.data.Store object as
10205      * a cache of Roo.data.Records.
10206      */
10207     readRecords : function(o){
10208         var sid = this.meta ? this.meta.id : null;
10209         var recordType = this.recordType, fields = recordType.prototype.fields;
10210         var records = [];
10211         var root = o;
10212             for(var i = 0; i < root.length; i++){
10213                     var n = root[i];
10214                 var values = {};
10215                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10216                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10217                 var f = fields.items[j];
10218                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10219                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10220                 v = f.convert(v);
10221                 values[f.name] = v;
10222             }
10223                 var record = new recordType(values, id);
10224                 record.json = n;
10225                 records[records.length] = record;
10226             }
10227             return {
10228                 records : records,
10229                 totalRecords : records.length
10230             };
10231     }
10232 });/*
10233  * - LGPL
10234  * * 
10235  */
10236
10237 /**
10238  * @class Roo.bootstrap.ComboBox
10239  * @extends Roo.bootstrap.TriggerField
10240  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10241  * @cfg {Boolean} append (true|false) default false
10242  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10243  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10244  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10245  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10246  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10247  * @constructor
10248  * Create a new ComboBox.
10249  * @param {Object} config Configuration options
10250  */
10251 Roo.bootstrap.ComboBox = function(config){
10252     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10253     this.addEvents({
10254         /**
10255          * @event expand
10256          * Fires when the dropdown list is expanded
10257              * @param {Roo.bootstrap.ComboBox} combo This combo box
10258              */
10259         'expand' : true,
10260         /**
10261          * @event collapse
10262          * Fires when the dropdown list is collapsed
10263              * @param {Roo.bootstrap.ComboBox} combo This combo box
10264              */
10265         'collapse' : true,
10266         /**
10267          * @event beforeselect
10268          * Fires before a list item is selected. Return false to cancel the selection.
10269              * @param {Roo.bootstrap.ComboBox} combo This combo box
10270              * @param {Roo.data.Record} record The data record returned from the underlying store
10271              * @param {Number} index The index of the selected item in the dropdown list
10272              */
10273         'beforeselect' : true,
10274         /**
10275          * @event select
10276          * Fires when a list item is selected
10277              * @param {Roo.bootstrap.ComboBox} combo This combo box
10278              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10279              * @param {Number} index The index of the selected item in the dropdown list
10280              */
10281         'select' : true,
10282         /**
10283          * @event beforequery
10284          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10285          * The event object passed has these properties:
10286              * @param {Roo.bootstrap.ComboBox} combo This combo box
10287              * @param {String} query The query
10288              * @param {Boolean} forceAll true to force "all" query
10289              * @param {Boolean} cancel true to cancel the query
10290              * @param {Object} e The query event object
10291              */
10292         'beforequery': true,
10293          /**
10294          * @event add
10295          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10296              * @param {Roo.bootstrap.ComboBox} combo This combo box
10297              */
10298         'add' : true,
10299         /**
10300          * @event edit
10301          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10302              * @param {Roo.bootstrap.ComboBox} combo This combo box
10303              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10304              */
10305         'edit' : true,
10306         /**
10307          * @event remove
10308          * Fires when the remove value from the combobox array
10309              * @param {Roo.bootstrap.ComboBox} combo This combo box
10310              */
10311         'remove' : true
10312         
10313     });
10314     
10315     this.item = [];
10316     this.tickItems = [];
10317     
10318     this.selectedIndex = -1;
10319     if(this.mode == 'local'){
10320         if(config.queryDelay === undefined){
10321             this.queryDelay = 10;
10322         }
10323         if(config.minChars === undefined){
10324             this.minChars = 0;
10325         }
10326     }
10327 };
10328
10329 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10330      
10331     /**
10332      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10333      * rendering into an Roo.Editor, defaults to false)
10334      */
10335     /**
10336      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10337      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10338      */
10339     /**
10340      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10341      */
10342     /**
10343      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10344      * the dropdown list (defaults to undefined, with no header element)
10345      */
10346
10347      /**
10348      * @cfg {String/Roo.Template} tpl The template to use to render the output
10349      */
10350      
10351      /**
10352      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10353      */
10354     listWidth: undefined,
10355     /**
10356      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10357      * mode = 'remote' or 'text' if mode = 'local')
10358      */
10359     displayField: undefined,
10360     /**
10361      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10362      * mode = 'remote' or 'value' if mode = 'local'). 
10363      * Note: use of a valueField requires the user make a selection
10364      * in order for a value to be mapped.
10365      */
10366     valueField: undefined,
10367     
10368     
10369     /**
10370      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10371      * field's data value (defaults to the underlying DOM element's name)
10372      */
10373     hiddenName: undefined,
10374     /**
10375      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10376      */
10377     listClass: '',
10378     /**
10379      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10380      */
10381     selectedClass: 'active',
10382     
10383     /**
10384      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10385      */
10386     shadow:'sides',
10387     /**
10388      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10389      * anchor positions (defaults to 'tl-bl')
10390      */
10391     listAlign: 'tl-bl?',
10392     /**
10393      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10394      */
10395     maxHeight: 300,
10396     /**
10397      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10398      * query specified by the allQuery config option (defaults to 'query')
10399      */
10400     triggerAction: 'query',
10401     /**
10402      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10403      * (defaults to 4, does not apply if editable = false)
10404      */
10405     minChars : 4,
10406     /**
10407      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10408      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10409      */
10410     typeAhead: false,
10411     /**
10412      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10413      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10414      */
10415     queryDelay: 500,
10416     /**
10417      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10418      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10419      */
10420     pageSize: 0,
10421     /**
10422      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10423      * when editable = true (defaults to false)
10424      */
10425     selectOnFocus:false,
10426     /**
10427      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10428      */
10429     queryParam: 'query',
10430     /**
10431      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10432      * when mode = 'remote' (defaults to 'Loading...')
10433      */
10434     loadingText: 'Loading...',
10435     /**
10436      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10437      */
10438     resizable: false,
10439     /**
10440      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10441      */
10442     handleHeight : 8,
10443     /**
10444      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10445      * traditional select (defaults to true)
10446      */
10447     editable: true,
10448     /**
10449      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10450      */
10451     allQuery: '',
10452     /**
10453      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10454      */
10455     mode: 'remote',
10456     /**
10457      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10458      * listWidth has a higher value)
10459      */
10460     minListWidth : 70,
10461     /**
10462      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10463      * allow the user to set arbitrary text into the field (defaults to false)
10464      */
10465     forceSelection:false,
10466     /**
10467      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10468      * if typeAhead = true (defaults to 250)
10469      */
10470     typeAheadDelay : 250,
10471     /**
10472      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10473      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10474      */
10475     valueNotFoundText : undefined,
10476     /**
10477      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10478      */
10479     blockFocus : false,
10480     
10481     /**
10482      * @cfg {Boolean} disableClear Disable showing of clear button.
10483      */
10484     disableClear : false,
10485     /**
10486      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10487      */
10488     alwaysQuery : false,
10489     
10490     /**
10491      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10492      */
10493     multiple : false,
10494     
10495     //private
10496     addicon : false,
10497     editicon: false,
10498     
10499     page: 0,
10500     hasQuery: false,
10501     append: false,
10502     loadNext: false,
10503     autoFocus : true,
10504     tickable : false,
10505     btnPosition : 'right',
10506     triggerList : true,
10507     showToggleBtn : true,
10508     // element that contains real text value.. (when hidden is used..)
10509     
10510     getAutoCreate : function()
10511     {
10512         var cfg = false;
10513         
10514         /*
10515          *  Normal ComboBox
10516          */
10517         if(!this.tickable){
10518             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10519             return cfg;
10520         }
10521         
10522         /*
10523          *  ComboBox with tickable selections
10524          */
10525              
10526         var align = this.labelAlign || this.parentLabelAlign();
10527         
10528         cfg = {
10529             cls : 'form-group roo-combobox-tickable' //input-group
10530         };
10531         
10532         
10533         var buttons = {
10534             tag : 'div',
10535             cls : 'tickable-buttons',
10536             cn : [
10537                 {
10538                     tag : 'button',
10539                     type : 'button',
10540                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10541                     html : 'Edit'
10542                 },
10543                 {
10544                     tag : 'button',
10545                     type : 'button',
10546                     name : 'ok',
10547                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10548                     html : 'Done'
10549                 },
10550                 {
10551                     tag : 'button',
10552                     type : 'button',
10553                     name : 'cancel',
10554                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10555                     html : 'Cancel'
10556                 }
10557             ]
10558         };
10559         
10560         var _this = this;
10561         Roo.each(buttons.cn, function(c){
10562             if (_this.size) {
10563                 c.cls += ' btn-' + _this.size;
10564             }
10565
10566             if (_this.disabled) {
10567                 c.disabled = true;
10568             }
10569         });
10570         
10571         var box = {
10572             tag: 'div',
10573             cn: [
10574                 {
10575                     tag: 'input',
10576                     type : 'hidden',
10577                     cls: 'form-hidden-field'
10578                 },
10579                 {
10580                     tag: 'ul',
10581                     cls: 'select2-choices',
10582                     cn:[
10583                         {
10584                             tag: 'li',
10585                             cls: 'select2-search-field',
10586                             cn: [
10587
10588                                 buttons
10589                             ]
10590                         }
10591                     ]
10592                 }
10593             ]
10594         }
10595         
10596         var combobox = {
10597             cls: 'select2-container input-group select2-container-multi',
10598             cn: [
10599                 box
10600 //                {
10601 //                    tag: 'ul',
10602 //                    cls: 'typeahead typeahead-long dropdown-menu',
10603 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10604 //                }
10605             ]
10606         };
10607         
10608         if (align ==='left' && this.fieldLabel.length) {
10609             
10610                 Roo.log("left and has label");
10611                 cfg.cn = [
10612                     
10613                     {
10614                         tag: 'label',
10615                         'for' :  id,
10616                         cls : 'control-label col-sm-' + this.labelWidth,
10617                         html : this.fieldLabel
10618                         
10619                     },
10620                     {
10621                         cls : "col-sm-" + (12 - this.labelWidth), 
10622                         cn: [
10623                             combobox
10624                         ]
10625                     }
10626                     
10627                 ];
10628         } else if ( this.fieldLabel.length) {
10629                 Roo.log(" label");
10630                  cfg.cn = [
10631                    
10632                     {
10633                         tag: 'label',
10634                         //cls : 'input-group-addon',
10635                         html : this.fieldLabel
10636                         
10637                     },
10638                     
10639                     combobox
10640                     
10641                 ];
10642
10643         } else {
10644             
10645                 Roo.log(" no label && no align");
10646                 cfg = combobox
10647                      
10648                 
10649         }
10650          
10651         var settings=this;
10652         ['xs','sm','md','lg'].map(function(size){
10653             if (settings[size]) {
10654                 cfg.cls += ' col-' + size + '-' + settings[size];
10655             }
10656         });
10657         
10658         return cfg;
10659         
10660     },
10661     
10662     // private
10663     initEvents: function()
10664     {
10665         
10666         if (!this.store) {
10667             throw "can not find store for combo";
10668         }
10669         this.store = Roo.factory(this.store, Roo.data);
10670         
10671         if(this.tickable){
10672             this.initTickableEvents();
10673             return;
10674         }
10675         
10676         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10677         
10678         if(this.hiddenName){
10679             
10680             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10681             
10682             this.hiddenField.dom.value =
10683                 this.hiddenValue !== undefined ? this.hiddenValue :
10684                 this.value !== undefined ? this.value : '';
10685
10686             // prevent input submission
10687             this.el.dom.removeAttribute('name');
10688             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10689              
10690              
10691         }
10692         //if(Roo.isGecko){
10693         //    this.el.dom.setAttribute('autocomplete', 'off');
10694         //}
10695         
10696         var cls = 'x-combo-list';
10697         
10698         //this.list = new Roo.Layer({
10699         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10700         //});
10701         
10702         var _this = this;
10703         
10704         (function(){
10705             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10706             _this.list.setWidth(lw);
10707         }).defer(100);
10708         
10709         this.list.on('mouseover', this.onViewOver, this);
10710         this.list.on('mousemove', this.onViewMove, this);
10711         
10712         this.list.on('scroll', this.onViewScroll, this);
10713         
10714         /*
10715         this.list.swallowEvent('mousewheel');
10716         this.assetHeight = 0;
10717
10718         if(this.title){
10719             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10720             this.assetHeight += this.header.getHeight();
10721         }
10722
10723         this.innerList = this.list.createChild({cls:cls+'-inner'});
10724         this.innerList.on('mouseover', this.onViewOver, this);
10725         this.innerList.on('mousemove', this.onViewMove, this);
10726         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10727         
10728         if(this.allowBlank && !this.pageSize && !this.disableClear){
10729             this.footer = this.list.createChild({cls:cls+'-ft'});
10730             this.pageTb = new Roo.Toolbar(this.footer);
10731            
10732         }
10733         if(this.pageSize){
10734             this.footer = this.list.createChild({cls:cls+'-ft'});
10735             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10736                     {pageSize: this.pageSize});
10737             
10738         }
10739         
10740         if (this.pageTb && this.allowBlank && !this.disableClear) {
10741             var _this = this;
10742             this.pageTb.add(new Roo.Toolbar.Fill(), {
10743                 cls: 'x-btn-icon x-btn-clear',
10744                 text: '&#160;',
10745                 handler: function()
10746                 {
10747                     _this.collapse();
10748                     _this.clearValue();
10749                     _this.onSelect(false, -1);
10750                 }
10751             });
10752         }
10753         if (this.footer) {
10754             this.assetHeight += this.footer.getHeight();
10755         }
10756         */
10757             
10758         if(!this.tpl){
10759             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10760         }
10761
10762         this.view = new Roo.View(this.list, this.tpl, {
10763             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10764         });
10765         //this.view.wrapEl.setDisplayed(false);
10766         this.view.on('click', this.onViewClick, this);
10767         
10768         
10769         
10770         this.store.on('beforeload', this.onBeforeLoad, this);
10771         this.store.on('load', this.onLoad, this);
10772         this.store.on('loadexception', this.onLoadException, this);
10773         /*
10774         if(this.resizable){
10775             this.resizer = new Roo.Resizable(this.list,  {
10776                pinned:true, handles:'se'
10777             });
10778             this.resizer.on('resize', function(r, w, h){
10779                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10780                 this.listWidth = w;
10781                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10782                 this.restrictHeight();
10783             }, this);
10784             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10785         }
10786         */
10787         if(!this.editable){
10788             this.editable = true;
10789             this.setEditable(false);
10790         }
10791         
10792         /*
10793         
10794         if (typeof(this.events.add.listeners) != 'undefined') {
10795             
10796             this.addicon = this.wrap.createChild(
10797                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10798        
10799             this.addicon.on('click', function(e) {
10800                 this.fireEvent('add', this);
10801             }, this);
10802         }
10803         if (typeof(this.events.edit.listeners) != 'undefined') {
10804             
10805             this.editicon = this.wrap.createChild(
10806                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10807             if (this.addicon) {
10808                 this.editicon.setStyle('margin-left', '40px');
10809             }
10810             this.editicon.on('click', function(e) {
10811                 
10812                 // we fire even  if inothing is selected..
10813                 this.fireEvent('edit', this, this.lastData );
10814                 
10815             }, this);
10816         }
10817         */
10818         
10819         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10820             "up" : function(e){
10821                 this.inKeyMode = true;
10822                 this.selectPrev();
10823             },
10824
10825             "down" : function(e){
10826                 if(!this.isExpanded()){
10827                     this.onTriggerClick();
10828                 }else{
10829                     this.inKeyMode = true;
10830                     this.selectNext();
10831                 }
10832             },
10833
10834             "enter" : function(e){
10835 //                this.onViewClick();
10836                 //return true;
10837                 this.collapse();
10838                 
10839                 if(this.fireEvent("specialkey", this, e)){
10840                     this.onViewClick(false);
10841                 }
10842                 
10843                 return true;
10844             },
10845
10846             "esc" : function(e){
10847                 this.collapse();
10848             },
10849
10850             "tab" : function(e){
10851                 this.collapse();
10852                 
10853                 if(this.fireEvent("specialkey", this, e)){
10854                     this.onViewClick(false);
10855                 }
10856                 
10857                 return true;
10858             },
10859
10860             scope : this,
10861
10862             doRelay : function(foo, bar, hname){
10863                 if(hname == 'down' || this.scope.isExpanded()){
10864                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10865                 }
10866                 return true;
10867             },
10868
10869             forceKeyDown: true
10870         });
10871         
10872         
10873         this.queryDelay = Math.max(this.queryDelay || 10,
10874                 this.mode == 'local' ? 10 : 250);
10875         
10876         
10877         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10878         
10879         if(this.typeAhead){
10880             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10881         }
10882         if(this.editable !== false){
10883             this.inputEl().on("keyup", this.onKeyUp, this);
10884         }
10885         if(this.forceSelection){
10886             this.inputEl().on('blur', this.doForce, this);
10887         }
10888         
10889         if(this.multiple){
10890             this.choices = this.el.select('ul.select2-choices', true).first();
10891             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10892         }
10893     },
10894     
10895     initTickableEvents: function()
10896     {   
10897         this.createList();
10898         
10899         if(this.hiddenName){
10900             
10901             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10902             
10903             this.hiddenField.dom.value =
10904                 this.hiddenValue !== undefined ? this.hiddenValue :
10905                 this.value !== undefined ? this.value : '';
10906
10907             // prevent input submission
10908             this.el.dom.removeAttribute('name');
10909             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10910              
10911              
10912         }
10913         
10914 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10915         
10916         this.choices = this.el.select('ul.select2-choices', true).first();
10917         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10918         if(this.triggerList){
10919             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10920         }
10921          
10922         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10923         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10924         
10925         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10926         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10927         
10928         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10929         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10930         
10931         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10932         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10933         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10934         
10935         this.okBtn.hide();
10936         this.cancelBtn.hide();
10937         
10938         var _this = this;
10939         
10940         (function(){
10941             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10942             _this.list.setWidth(lw);
10943         }).defer(100);
10944         
10945         this.list.on('mouseover', this.onViewOver, this);
10946         this.list.on('mousemove', this.onViewMove, this);
10947         
10948         this.list.on('scroll', this.onViewScroll, this);
10949         
10950         if(!this.tpl){
10951             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>';
10952         }
10953
10954         this.view = new Roo.View(this.list, this.tpl, {
10955             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10956         });
10957         
10958         //this.view.wrapEl.setDisplayed(false);
10959         this.view.on('click', this.onViewClick, this);
10960         
10961         
10962         
10963         this.store.on('beforeload', this.onBeforeLoad, this);
10964         this.store.on('load', this.onLoad, this);
10965         this.store.on('loadexception', this.onLoadException, this);
10966         
10967 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10968 //            "up" : function(e){
10969 //                this.inKeyMode = true;
10970 //                this.selectPrev();
10971 //            },
10972 //
10973 //            "down" : function(e){
10974 //                if(!this.isExpanded()){
10975 //                    this.onTriggerClick();
10976 //                }else{
10977 //                    this.inKeyMode = true;
10978 //                    this.selectNext();
10979 //                }
10980 //            },
10981 //
10982 //            "enter" : function(e){
10983 ////                this.onViewClick();
10984 //                //return true;
10985 //                this.collapse();
10986 //                
10987 //                if(this.fireEvent("specialkey", this, e)){
10988 //                    this.onViewClick(false);
10989 //                }
10990 //                
10991 //                return true;
10992 //            },
10993 //
10994 //            "esc" : function(e){
10995 //                this.collapse();
10996 //            },
10997 //
10998 //            "tab" : function(e){
10999 //                this.collapse();
11000 //                
11001 //                if(this.fireEvent("specialkey", this, e)){
11002 //                    this.onViewClick(false);
11003 //                }
11004 //                
11005 //                return true;
11006 //            },
11007 //
11008 //            scope : this,
11009 //
11010 //            doRelay : function(foo, bar, hname){
11011 //                if(hname == 'down' || this.scope.isExpanded()){
11012 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11013 //                }
11014 //                return true;
11015 //            },
11016 //
11017 //            forceKeyDown: true
11018 //        });
11019         
11020         
11021         this.queryDelay = Math.max(this.queryDelay || 10,
11022                 this.mode == 'local' ? 10 : 250);
11023         
11024         
11025         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11026         
11027         if(this.typeAhead){
11028             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11029         }
11030     },
11031
11032     onDestroy : function(){
11033         if(this.view){
11034             this.view.setStore(null);
11035             this.view.el.removeAllListeners();
11036             this.view.el.remove();
11037             this.view.purgeListeners();
11038         }
11039         if(this.list){
11040             this.list.dom.innerHTML  = '';
11041         }
11042         
11043         if(this.store){
11044             this.store.un('beforeload', this.onBeforeLoad, this);
11045             this.store.un('load', this.onLoad, this);
11046             this.store.un('loadexception', this.onLoadException, this);
11047         }
11048         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11049     },
11050
11051     // private
11052     fireKey : function(e){
11053         if(e.isNavKeyPress() && !this.list.isVisible()){
11054             this.fireEvent("specialkey", this, e);
11055         }
11056     },
11057
11058     // private
11059     onResize: function(w, h){
11060 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11061 //        
11062 //        if(typeof w != 'number'){
11063 //            // we do not handle it!?!?
11064 //            return;
11065 //        }
11066 //        var tw = this.trigger.getWidth();
11067 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11068 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11069 //        var x = w - tw;
11070 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11071 //            
11072 //        //this.trigger.setStyle('left', x+'px');
11073 //        
11074 //        if(this.list && this.listWidth === undefined){
11075 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11076 //            this.list.setWidth(lw);
11077 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11078 //        }
11079         
11080     
11081         
11082     },
11083
11084     /**
11085      * Allow or prevent the user from directly editing the field text.  If false is passed,
11086      * the user will only be able to select from the items defined in the dropdown list.  This method
11087      * is the runtime equivalent of setting the 'editable' config option at config time.
11088      * @param {Boolean} value True to allow the user to directly edit the field text
11089      */
11090     setEditable : function(value){
11091         if(value == this.editable){
11092             return;
11093         }
11094         this.editable = value;
11095         if(!value){
11096             this.inputEl().dom.setAttribute('readOnly', true);
11097             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11098             this.inputEl().addClass('x-combo-noedit');
11099         }else{
11100             this.inputEl().dom.setAttribute('readOnly', false);
11101             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11102             this.inputEl().removeClass('x-combo-noedit');
11103         }
11104     },
11105
11106     // private
11107     
11108     onBeforeLoad : function(combo,opts){
11109         if(!this.hasFocus){
11110             return;
11111         }
11112          if (!opts.add) {
11113             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11114          }
11115         this.restrictHeight();
11116         this.selectedIndex = -1;
11117     },
11118
11119     // private
11120     onLoad : function(){
11121         
11122         this.hasQuery = false;
11123         
11124         if(!this.hasFocus){
11125             return;
11126         }
11127         
11128         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11129             this.loading.hide();
11130         }
11131         
11132         if(this.store.getCount() > 0){
11133             this.expand();
11134 //            this.restrictHeight();
11135             if(this.lastQuery == this.allQuery){
11136                 if(this.editable && !this.tickable){
11137                     this.inputEl().dom.select();
11138                 }
11139                 
11140                 if(
11141                     !this.selectByValue(this.value, true) &&
11142                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11143                     this.store.lastOptions.add != true)
11144                 ){
11145                     this.select(0, true);
11146                 }
11147             }else{
11148                 if(this.autoFocus){
11149                     this.selectNext();
11150                 }
11151                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11152                     this.taTask.delay(this.typeAheadDelay);
11153                 }
11154             }
11155         }else{
11156             this.onEmptyResults();
11157         }
11158         
11159         //this.el.focus();
11160     },
11161     // private
11162     onLoadException : function()
11163     {
11164         this.hasQuery = false;
11165         
11166         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11167             this.loading.hide();
11168         }
11169         
11170         this.collapse();
11171         Roo.log(this.store.reader.jsonData);
11172         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11173             // fixme
11174             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11175         }
11176         
11177         
11178     },
11179     // private
11180     onTypeAhead : function(){
11181         if(this.store.getCount() > 0){
11182             var r = this.store.getAt(0);
11183             var newValue = r.data[this.displayField];
11184             var len = newValue.length;
11185             var selStart = this.getRawValue().length;
11186             
11187             if(selStart != len){
11188                 this.setRawValue(newValue);
11189                 this.selectText(selStart, newValue.length);
11190             }
11191         }
11192     },
11193
11194     // private
11195     onSelect : function(record, index){
11196         
11197         if(this.fireEvent('beforeselect', this, record, index) !== false){
11198         
11199             this.setFromData(index > -1 ? record.data : false);
11200             
11201             this.collapse();
11202             this.fireEvent('select', this, record, index);
11203         }
11204     },
11205
11206     /**
11207      * Returns the currently selected field value or empty string if no value is set.
11208      * @return {String} value The selected value
11209      */
11210     getValue : function(){
11211         
11212         if(this.multiple){
11213             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11214         }
11215         
11216         if(this.valueField){
11217             return typeof this.value != 'undefined' ? this.value : '';
11218         }else{
11219             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11220         }
11221     },
11222
11223     /**
11224      * Clears any text/value currently set in the field
11225      */
11226     clearValue : function(){
11227         if(this.hiddenField){
11228             this.hiddenField.dom.value = '';
11229         }
11230         this.value = '';
11231         this.setRawValue('');
11232         this.lastSelectionText = '';
11233         
11234     },
11235
11236     /**
11237      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11238      * will be displayed in the field.  If the value does not match the data value of an existing item,
11239      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11240      * Otherwise the field will be blank (although the value will still be set).
11241      * @param {String} value The value to match
11242      */
11243     setValue : function(v){
11244         if(this.multiple){
11245             this.syncValue();
11246             return;
11247         }
11248         
11249         var text = v;
11250         if(this.valueField){
11251             var r = this.findRecord(this.valueField, v);
11252             if(r){
11253                 text = r.data[this.displayField];
11254             }else if(this.valueNotFoundText !== undefined){
11255                 text = this.valueNotFoundText;
11256             }
11257         }
11258         this.lastSelectionText = text;
11259         if(this.hiddenField){
11260             this.hiddenField.dom.value = v;
11261         }
11262         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11263         this.value = v;
11264     },
11265     /**
11266      * @property {Object} the last set data for the element
11267      */
11268     
11269     lastData : false,
11270     /**
11271      * Sets the value of the field based on a object which is related to the record format for the store.
11272      * @param {Object} value the value to set as. or false on reset?
11273      */
11274     setFromData : function(o){
11275         
11276         if(this.multiple){
11277             if(typeof o.display_name !== 'string'){
11278                 for(var i=0;i<o.display_name.length;i++){
11279                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11280                 }
11281                 return;
11282             }
11283             this.addItem(o);
11284             return;
11285         }
11286             
11287         var dv = ''; // display value
11288         var vv = ''; // value value..
11289         this.lastData = o;
11290         if (this.displayField) {
11291             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11292         } else {
11293             // this is an error condition!!!
11294             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11295         }
11296         
11297         if(this.valueField){
11298             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11299         }
11300         
11301         if(this.hiddenField){
11302             this.hiddenField.dom.value = vv;
11303             
11304             this.lastSelectionText = dv;
11305             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11306             this.value = vv;
11307             return;
11308         }
11309         // no hidden field.. - we store the value in 'value', but still display
11310         // display field!!!!
11311         this.lastSelectionText = dv;
11312         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11313         this.value = vv;
11314         
11315         
11316     },
11317     // private
11318     reset : function(){
11319         // overridden so that last data is reset..
11320         this.setValue(this.originalValue);
11321         this.clearInvalid();
11322         this.lastData = false;
11323         if (this.view) {
11324             this.view.clearSelections();
11325         }
11326     },
11327     // private
11328     findRecord : function(prop, value){
11329         var record;
11330         if(this.store.getCount() > 0){
11331             this.store.each(function(r){
11332                 if(r.data[prop] == value){
11333                     record = r;
11334                     return false;
11335                 }
11336                 return true;
11337             });
11338         }
11339         return record;
11340     },
11341     
11342     getName: function()
11343     {
11344         // returns hidden if it's set..
11345         if (!this.rendered) {return ''};
11346         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11347         
11348     },
11349     // private
11350     onViewMove : function(e, t){
11351         this.inKeyMode = false;
11352     },
11353
11354     // private
11355     onViewOver : function(e, t){
11356         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11357             return;
11358         }
11359         var item = this.view.findItemFromChild(t);
11360         
11361         if(item){
11362             var index = this.view.indexOf(item);
11363             this.select(index, false);
11364         }
11365     },
11366
11367     // private
11368     onViewClick : function(view, doFocus, el, e)
11369     {
11370         var index = this.view.getSelectedIndexes()[0];
11371         
11372         var r = this.store.getAt(index);
11373         
11374         if(this.tickable){
11375             
11376             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11377                 return;
11378             }
11379             
11380             var rm = false;
11381             var _this = this;
11382             
11383             Roo.each(this.tickItems, function(v,k){
11384                 
11385                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11386                     _this.tickItems.splice(k, 1);
11387                     rm = true;
11388                     return;
11389                 }
11390             })
11391             
11392             if(rm){
11393                 return;
11394             }
11395             
11396             this.tickItems.push(r.data);
11397             return;
11398         }
11399         
11400         if(r){
11401             this.onSelect(r, index);
11402         }
11403         if(doFocus !== false && !this.blockFocus){
11404             this.inputEl().focus();
11405         }
11406     },
11407
11408     // private
11409     restrictHeight : function(){
11410         //this.innerList.dom.style.height = '';
11411         //var inner = this.innerList.dom;
11412         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11413         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11414         //this.list.beginUpdate();
11415         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11416         this.list.alignTo(this.inputEl(), this.listAlign);
11417         this.list.alignTo(this.inputEl(), this.listAlign);
11418         //this.list.endUpdate();
11419     },
11420
11421     // private
11422     onEmptyResults : function(){
11423         this.collapse();
11424     },
11425
11426     /**
11427      * Returns true if the dropdown list is expanded, else false.
11428      */
11429     isExpanded : function(){
11430         return this.list.isVisible();
11431     },
11432
11433     /**
11434      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11435      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11436      * @param {String} value The data value of the item to select
11437      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11438      * selected item if it is not currently in view (defaults to true)
11439      * @return {Boolean} True if the value matched an item in the list, else false
11440      */
11441     selectByValue : function(v, scrollIntoView){
11442         if(v !== undefined && v !== null){
11443             var r = this.findRecord(this.valueField || this.displayField, v);
11444             if(r){
11445                 this.select(this.store.indexOf(r), scrollIntoView);
11446                 return true;
11447             }
11448         }
11449         return false;
11450     },
11451
11452     /**
11453      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11454      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11455      * @param {Number} index The zero-based index of the list item to select
11456      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11457      * selected item if it is not currently in view (defaults to true)
11458      */
11459     select : function(index, scrollIntoView){
11460         this.selectedIndex = index;
11461         this.view.select(index);
11462         if(scrollIntoView !== false){
11463             var el = this.view.getNode(index);
11464             if(el && !this.multiple && !this.tickable){
11465                 this.list.scrollChildIntoView(el, false);
11466             }
11467         }
11468     },
11469
11470     // private
11471     selectNext : function(){
11472         var ct = this.store.getCount();
11473         if(ct > 0){
11474             if(this.selectedIndex == -1){
11475                 this.select(0);
11476             }else if(this.selectedIndex < ct-1){
11477                 this.select(this.selectedIndex+1);
11478             }
11479         }
11480     },
11481
11482     // private
11483     selectPrev : function(){
11484         var ct = this.store.getCount();
11485         if(ct > 0){
11486             if(this.selectedIndex == -1){
11487                 this.select(0);
11488             }else if(this.selectedIndex != 0){
11489                 this.select(this.selectedIndex-1);
11490             }
11491         }
11492     },
11493
11494     // private
11495     onKeyUp : function(e){
11496         if(this.editable !== false && !e.isSpecialKey()){
11497             this.lastKey = e.getKey();
11498             this.dqTask.delay(this.queryDelay);
11499         }
11500     },
11501
11502     // private
11503     validateBlur : function(){
11504         return !this.list || !this.list.isVisible();   
11505     },
11506
11507     // private
11508     initQuery : function(){
11509         this.doQuery(this.getRawValue());
11510     },
11511
11512     // private
11513     doForce : function(){
11514         if(this.inputEl().dom.value.length > 0){
11515             this.inputEl().dom.value =
11516                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11517              
11518         }
11519     },
11520
11521     /**
11522      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11523      * query allowing the query action to be canceled if needed.
11524      * @param {String} query The SQL query to execute
11525      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11526      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11527      * saved in the current store (defaults to false)
11528      */
11529     doQuery : function(q, forceAll){
11530         
11531         if(q === undefined || q === null){
11532             q = '';
11533         }
11534         var qe = {
11535             query: q,
11536             forceAll: forceAll,
11537             combo: this,
11538             cancel:false
11539         };
11540         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11541             return false;
11542         }
11543         q = qe.query;
11544         
11545         forceAll = qe.forceAll;
11546         if(forceAll === true || (q.length >= this.minChars)){
11547             
11548             this.hasQuery = true;
11549             
11550             if(this.lastQuery != q || this.alwaysQuery){
11551                 this.lastQuery = q;
11552                 if(this.mode == 'local'){
11553                     this.selectedIndex = -1;
11554                     if(forceAll){
11555                         this.store.clearFilter();
11556                     }else{
11557                         this.store.filter(this.displayField, q);
11558                     }
11559                     this.onLoad();
11560                 }else{
11561                     this.store.baseParams[this.queryParam] = q;
11562                     
11563                     var options = {params : this.getParams(q)};
11564                     
11565                     if(this.loadNext){
11566                         options.add = true;
11567                         options.params.start = this.page * this.pageSize;
11568                     }
11569                     
11570                     this.store.load(options);
11571                     /*
11572                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11573                      *  we should expand the list on onLoad
11574                      *  so command out it
11575                      */
11576 //                    this.expand();
11577                 }
11578             }else{
11579                 this.selectedIndex = -1;
11580                 this.onLoad();   
11581             }
11582         }
11583         
11584         this.loadNext = false;
11585     },
11586
11587     // private
11588     getParams : function(q){
11589         var p = {};
11590         //p[this.queryParam] = q;
11591         
11592         if(this.pageSize){
11593             p.start = 0;
11594             p.limit = this.pageSize;
11595         }
11596         return p;
11597     },
11598
11599     /**
11600      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11601      */
11602     collapse : function(){
11603         if(!this.isExpanded()){
11604             return;
11605         }
11606         
11607         this.list.hide();
11608         
11609         if(this.tickable){
11610             this.okBtn.hide();
11611             this.cancelBtn.hide();
11612             this.trigger.show();
11613         }
11614         
11615         Roo.get(document).un('mousedown', this.collapseIf, this);
11616         Roo.get(document).un('mousewheel', this.collapseIf, this);
11617         if (!this.editable) {
11618             Roo.get(document).un('keydown', this.listKeyPress, this);
11619         }
11620         this.fireEvent('collapse', this);
11621     },
11622
11623     // private
11624     collapseIf : function(e){
11625         var in_combo  = e.within(this.el);
11626         var in_list =  e.within(this.list);
11627         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11628         
11629         if (in_combo || in_list || is_list) {
11630             //e.stopPropagation();
11631             return;
11632         }
11633         
11634         if(this.tickable){
11635             this.onTickableFooterButtonClick(e, false, false);
11636         }
11637
11638         this.collapse();
11639         
11640     },
11641
11642     /**
11643      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11644      */
11645     expand : function(){
11646        
11647         if(this.isExpanded() || !this.hasFocus){
11648             return;
11649         }
11650         
11651         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11652         this.list.setWidth(lw);
11653         
11654         
11655          Roo.log('expand');
11656         
11657         this.list.show();
11658         
11659         this.restrictHeight();
11660         
11661         if(this.tickable){
11662             
11663             this.tickItems = Roo.apply([], this.item);
11664             
11665             this.okBtn.show();
11666             this.cancelBtn.show();
11667             this.trigger.hide();
11668             
11669         }
11670         
11671         Roo.get(document).on('mousedown', this.collapseIf, this);
11672         Roo.get(document).on('mousewheel', this.collapseIf, this);
11673         if (!this.editable) {
11674             Roo.get(document).on('keydown', this.listKeyPress, this);
11675         }
11676         
11677         this.fireEvent('expand', this);
11678     },
11679
11680     // private
11681     // Implements the default empty TriggerField.onTriggerClick function
11682     onTriggerClick : function(e)
11683     {
11684         Roo.log('trigger click');
11685         
11686         if(this.disabled || !this.triggerList){
11687             return;
11688         }
11689         
11690         this.page = 0;
11691         this.loadNext = false;
11692         
11693         if(this.isExpanded()){
11694             this.collapse();
11695             if (!this.blockFocus) {
11696                 this.inputEl().focus();
11697             }
11698             
11699         }else {
11700             this.hasFocus = true;
11701             if(this.triggerAction == 'all') {
11702                 this.doQuery(this.allQuery, true);
11703             } else {
11704                 this.doQuery(this.getRawValue());
11705             }
11706             if (!this.blockFocus) {
11707                 this.inputEl().focus();
11708             }
11709         }
11710     },
11711     
11712     onTickableTriggerClick : function(e)
11713     {
11714         if(this.disabled){
11715             return;
11716         }
11717         
11718         this.page = 0;
11719         this.loadNext = false;
11720         this.hasFocus = true;
11721         
11722         if(this.triggerAction == 'all') {
11723             this.doQuery(this.allQuery, true);
11724         } else {
11725             this.doQuery(this.getRawValue());
11726         }
11727     },
11728     
11729     onSearchFieldClick : function(e)
11730     {
11731         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11732             return;
11733         }
11734         
11735         this.page = 0;
11736         this.loadNext = false;
11737         this.hasFocus = true;
11738         
11739         if(this.triggerAction == 'all') {
11740             this.doQuery(this.allQuery, true);
11741         } else {
11742             this.doQuery(this.getRawValue());
11743         }
11744     },
11745     
11746     listKeyPress : function(e)
11747     {
11748         //Roo.log('listkeypress');
11749         // scroll to first matching element based on key pres..
11750         if (e.isSpecialKey()) {
11751             return false;
11752         }
11753         var k = String.fromCharCode(e.getKey()).toUpperCase();
11754         //Roo.log(k);
11755         var match  = false;
11756         var csel = this.view.getSelectedNodes();
11757         var cselitem = false;
11758         if (csel.length) {
11759             var ix = this.view.indexOf(csel[0]);
11760             cselitem  = this.store.getAt(ix);
11761             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11762                 cselitem = false;
11763             }
11764             
11765         }
11766         
11767         this.store.each(function(v) { 
11768             if (cselitem) {
11769                 // start at existing selection.
11770                 if (cselitem.id == v.id) {
11771                     cselitem = false;
11772                 }
11773                 return true;
11774             }
11775                 
11776             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11777                 match = this.store.indexOf(v);
11778                 return false;
11779             }
11780             return true;
11781         }, this);
11782         
11783         if (match === false) {
11784             return true; // no more action?
11785         }
11786         // scroll to?
11787         this.view.select(match);
11788         var sn = Roo.get(this.view.getSelectedNodes()[0])
11789         //sn.scrollIntoView(sn.dom.parentNode, false);
11790     },
11791     
11792     onViewScroll : function(e, t){
11793         
11794         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){
11795             return;
11796         }
11797         
11798         this.hasQuery = true;
11799         
11800         this.loading = this.list.select('.loading', true).first();
11801         
11802         if(this.loading === null){
11803             this.list.createChild({
11804                 tag: 'div',
11805                 cls: 'loading select2-more-results select2-active',
11806                 html: 'Loading more results...'
11807             })
11808             
11809             this.loading = this.list.select('.loading', true).first();
11810             
11811             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11812             
11813             this.loading.hide();
11814         }
11815         
11816         this.loading.show();
11817         
11818         var _combo = this;
11819         
11820         this.page++;
11821         this.loadNext = true;
11822         
11823         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11824         
11825         return;
11826     },
11827     
11828     addItem : function(o)
11829     {   
11830         var dv = ''; // display value
11831         
11832         if (this.displayField) {
11833             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11834         } else {
11835             // this is an error condition!!!
11836             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11837         }
11838         
11839         if(!dv.length){
11840             return;
11841         }
11842         
11843         var choice = this.choices.createChild({
11844             tag: 'li',
11845             cls: 'select2-search-choice',
11846             cn: [
11847                 {
11848                     tag: 'div',
11849                     html: dv
11850                 },
11851                 {
11852                     tag: 'a',
11853                     href: '#',
11854                     cls: 'select2-search-choice-close',
11855                     tabindex: '-1'
11856                 }
11857             ]
11858             
11859         }, this.searchField);
11860         
11861         var close = choice.select('a.select2-search-choice-close', true).first()
11862         
11863         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11864         
11865         this.item.push(o);
11866         
11867         this.lastData = o;
11868         
11869         this.syncValue();
11870         
11871         this.inputEl().dom.value = '';
11872         
11873     },
11874     
11875     onRemoveItem : function(e, _self, o)
11876     {
11877         e.preventDefault();
11878         var index = this.item.indexOf(o.data) * 1;
11879         
11880         if( index < 0){
11881             Roo.log('not this item?!');
11882             return;
11883         }
11884         
11885         this.item.splice(index, 1);
11886         o.item.remove();
11887         
11888         this.syncValue();
11889         
11890         this.fireEvent('remove', this, e);
11891         
11892     },
11893     
11894     syncValue : function()
11895     {
11896         if(!this.item.length){
11897             this.clearValue();
11898             return;
11899         }
11900             
11901         var value = [];
11902         var _this = this;
11903         Roo.each(this.item, function(i){
11904             if(_this.valueField){
11905                 value.push(i[_this.valueField]);
11906                 return;
11907             }
11908
11909             value.push(i);
11910         });
11911
11912         this.value = value.join(',');
11913
11914         if(this.hiddenField){
11915             this.hiddenField.dom.value = this.value;
11916         }
11917     },
11918     
11919     clearItem : function()
11920     {
11921         if(!this.multiple){
11922             return;
11923         }
11924         
11925         this.item = [];
11926         
11927         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11928            c.remove();
11929         });
11930         
11931         this.syncValue();
11932     },
11933     
11934     inputEl: function ()
11935     {
11936         if(this.tickable){
11937             return this.searchField;
11938         }
11939         return this.el.select('input.form-control',true).first();
11940     },
11941     
11942     
11943     onTickableFooterButtonClick : function(e, btn, el)
11944     {
11945         e.preventDefault();
11946         
11947         if(btn && btn.name == 'cancel'){
11948             this.tickItems = Roo.apply([], this.item);
11949             this.collapse();
11950             return;
11951         }
11952         
11953         this.clearItem();
11954         
11955         var _this = this;
11956         
11957         Roo.each(this.tickItems, function(o){
11958             _this.addItem(o);
11959         });
11960         
11961         this.collapse();
11962         
11963     }
11964     
11965     
11966
11967     /** 
11968     * @cfg {Boolean} grow 
11969     * @hide 
11970     */
11971     /** 
11972     * @cfg {Number} growMin 
11973     * @hide 
11974     */
11975     /** 
11976     * @cfg {Number} growMax 
11977     * @hide 
11978     */
11979     /**
11980      * @hide
11981      * @method autoSize
11982      */
11983 });
11984 /*
11985  * Based on:
11986  * Ext JS Library 1.1.1
11987  * Copyright(c) 2006-2007, Ext JS, LLC.
11988  *
11989  * Originally Released Under LGPL - original licence link has changed is not relivant.
11990  *
11991  * Fork - LGPL
11992  * <script type="text/javascript">
11993  */
11994
11995 /**
11996  * @class Roo.View
11997  * @extends Roo.util.Observable
11998  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11999  * This class also supports single and multi selection modes. <br>
12000  * Create a data model bound view:
12001  <pre><code>
12002  var store = new Roo.data.Store(...);
12003
12004  var view = new Roo.View({
12005     el : "my-element",
12006     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12007  
12008     singleSelect: true,
12009     selectedClass: "ydataview-selected",
12010     store: store
12011  });
12012
12013  // listen for node click?
12014  view.on("click", function(vw, index, node, e){
12015  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12016  });
12017
12018  // load XML data
12019  dataModel.load("foobar.xml");
12020  </code></pre>
12021  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12022  * <br><br>
12023  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12024  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12025  * 
12026  * Note: old style constructor is still suported (container, template, config)
12027  * 
12028  * @constructor
12029  * Create a new View
12030  * @param {Object} config The config object
12031  * 
12032  */
12033 Roo.View = function(config, depreciated_tpl, depreciated_config){
12034     
12035     this.parent = false;
12036     
12037     if (typeof(depreciated_tpl) == 'undefined') {
12038         // new way.. - universal constructor.
12039         Roo.apply(this, config);
12040         this.el  = Roo.get(this.el);
12041     } else {
12042         // old format..
12043         this.el  = Roo.get(config);
12044         this.tpl = depreciated_tpl;
12045         Roo.apply(this, depreciated_config);
12046     }
12047     this.wrapEl  = this.el.wrap().wrap();
12048     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12049     
12050     
12051     if(typeof(this.tpl) == "string"){
12052         this.tpl = new Roo.Template(this.tpl);
12053     } else {
12054         // support xtype ctors..
12055         this.tpl = new Roo.factory(this.tpl, Roo);
12056     }
12057     
12058     
12059     this.tpl.compile();
12060     
12061     /** @private */
12062     this.addEvents({
12063         /**
12064          * @event beforeclick
12065          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
12072         /**
12073          * @event click
12074          * Fires when a template node is 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             "click" : true,
12081         /**
12082          * @event dblclick
12083          * Fires when a template node is double 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             "dblclick" : true,
12090         /**
12091          * @event contextmenu
12092          * Fires when a template node is right clicked.
12093          * @param {Roo.View} this
12094          * @param {Number} index The index of the target node
12095          * @param {HTMLElement} node The target node
12096          * @param {Roo.EventObject} e The raw event object
12097          */
12098             "contextmenu" : true,
12099         /**
12100          * @event selectionchange
12101          * Fires when the selected nodes change.
12102          * @param {Roo.View} this
12103          * @param {Array} selections Array of the selected nodes
12104          */
12105             "selectionchange" : true,
12106     
12107         /**
12108          * @event beforeselect
12109          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12110          * @param {Roo.View} this
12111          * @param {HTMLElement} node The node to be selected
12112          * @param {Array} selections Array of currently selected nodes
12113          */
12114             "beforeselect" : true,
12115         /**
12116          * @event preparedata
12117          * Fires on every row to render, to allow you to change the data.
12118          * @param {Roo.View} this
12119          * @param {Object} data to be rendered (change this)
12120          */
12121           "preparedata" : true
12122           
12123           
12124         });
12125
12126
12127
12128     this.el.on({
12129         "click": this.onClick,
12130         "dblclick": this.onDblClick,
12131         "contextmenu": this.onContextMenu,
12132         scope:this
12133     });
12134
12135     this.selections = [];
12136     this.nodes = [];
12137     this.cmp = new Roo.CompositeElementLite([]);
12138     if(this.store){
12139         this.store = Roo.factory(this.store, Roo.data);
12140         this.setStore(this.store, true);
12141     }
12142     
12143     if ( this.footer && this.footer.xtype) {
12144            
12145          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12146         
12147         this.footer.dataSource = this.store
12148         this.footer.container = fctr;
12149         this.footer = Roo.factory(this.footer, Roo);
12150         fctr.insertFirst(this.el);
12151         
12152         // this is a bit insane - as the paging toolbar seems to detach the el..
12153 //        dom.parentNode.parentNode.parentNode
12154          // they get detached?
12155     }
12156     
12157     
12158     Roo.View.superclass.constructor.call(this);
12159     
12160     
12161 };
12162
12163 Roo.extend(Roo.View, Roo.util.Observable, {
12164     
12165      /**
12166      * @cfg {Roo.data.Store} store Data store to load data from.
12167      */
12168     store : false,
12169     
12170     /**
12171      * @cfg {String|Roo.Element} el The container element.
12172      */
12173     el : '',
12174     
12175     /**
12176      * @cfg {String|Roo.Template} tpl The template used by this View 
12177      */
12178     tpl : false,
12179     /**
12180      * @cfg {String} dataName the named area of the template to use as the data area
12181      *                          Works with domtemplates roo-name="name"
12182      */
12183     dataName: false,
12184     /**
12185      * @cfg {String} selectedClass The css class to add to selected nodes
12186      */
12187     selectedClass : "x-view-selected",
12188      /**
12189      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12190      */
12191     emptyText : "",
12192     
12193     /**
12194      * @cfg {String} text to display on mask (default Loading)
12195      */
12196     mask : false,
12197     /**
12198      * @cfg {Boolean} multiSelect Allow multiple selection
12199      */
12200     multiSelect : false,
12201     /**
12202      * @cfg {Boolean} singleSelect Allow single selection
12203      */
12204     singleSelect:  false,
12205     
12206     /**
12207      * @cfg {Boolean} toggleSelect - selecting 
12208      */
12209     toggleSelect : false,
12210     
12211     /**
12212      * @cfg {Boolean} tickable - selecting 
12213      */
12214     tickable : false,
12215     
12216     /**
12217      * Returns the element this view is bound to.
12218      * @return {Roo.Element}
12219      */
12220     getEl : function(){
12221         return this.wrapEl;
12222     },
12223     
12224     
12225
12226     /**
12227      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12228      */
12229     refresh : function(){
12230         Roo.log('refresh');
12231         var t = this.tpl;
12232         
12233         // if we are using something like 'domtemplate', then
12234         // the what gets used is:
12235         // t.applySubtemplate(NAME, data, wrapping data..)
12236         // the outer template then get' applied with
12237         //     the store 'extra data'
12238         // and the body get's added to the
12239         //      roo-name="data" node?
12240         //      <span class='roo-tpl-{name}'></span> ?????
12241         
12242         
12243         
12244         this.clearSelections();
12245         this.el.update("");
12246         var html = [];
12247         var records = this.store.getRange();
12248         if(records.length < 1) {
12249             
12250             // is this valid??  = should it render a template??
12251             
12252             this.el.update(this.emptyText);
12253             return;
12254         }
12255         var el = this.el;
12256         if (this.dataName) {
12257             this.el.update(t.apply(this.store.meta)); //????
12258             el = this.el.child('.roo-tpl-' + this.dataName);
12259         }
12260         
12261         for(var i = 0, len = records.length; i < len; i++){
12262             var data = this.prepareData(records[i].data, i, records[i]);
12263             this.fireEvent("preparedata", this, data, i, records[i]);
12264             
12265             var d = Roo.apply({}, data);
12266             
12267             if(this.tickable){
12268                 Roo.apply(d, {'roo-id' : Roo.id()});
12269                 
12270                 var _this = this;
12271             
12272                 Roo.each(this.parent.item, function(item){
12273                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12274                         return;
12275                     }
12276                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12277                 });
12278             }
12279             
12280             html[html.length] = Roo.util.Format.trim(
12281                 this.dataName ?
12282                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12283                     t.apply(d)
12284             );
12285         }
12286         
12287         
12288         
12289         el.update(html.join(""));
12290         this.nodes = el.dom.childNodes;
12291         this.updateIndexes(0);
12292     },
12293     
12294
12295     /**
12296      * Function to override to reformat the data that is sent to
12297      * the template for each node.
12298      * DEPRICATED - use the preparedata event handler.
12299      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12300      * a JSON object for an UpdateManager bound view).
12301      */
12302     prepareData : function(data, index, record)
12303     {
12304         this.fireEvent("preparedata", this, data, index, record);
12305         return data;
12306     },
12307
12308     onUpdate : function(ds, record){
12309          Roo.log('on update');   
12310         this.clearSelections();
12311         var index = this.store.indexOf(record);
12312         var n = this.nodes[index];
12313         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12314         n.parentNode.removeChild(n);
12315         this.updateIndexes(index, index);
12316     },
12317
12318     
12319     
12320 // --------- FIXME     
12321     onAdd : function(ds, records, index)
12322     {
12323         Roo.log(['on Add', ds, records, index] );        
12324         this.clearSelections();
12325         if(this.nodes.length == 0){
12326             this.refresh();
12327             return;
12328         }
12329         var n = this.nodes[index];
12330         for(var i = 0, len = records.length; i < len; i++){
12331             var d = this.prepareData(records[i].data, i, records[i]);
12332             if(n){
12333                 this.tpl.insertBefore(n, d);
12334             }else{
12335                 
12336                 this.tpl.append(this.el, d);
12337             }
12338         }
12339         this.updateIndexes(index);
12340     },
12341
12342     onRemove : function(ds, record, index){
12343         Roo.log('onRemove');
12344         this.clearSelections();
12345         var el = this.dataName  ?
12346             this.el.child('.roo-tpl-' + this.dataName) :
12347             this.el; 
12348         
12349         el.dom.removeChild(this.nodes[index]);
12350         this.updateIndexes(index);
12351     },
12352
12353     /**
12354      * Refresh an individual node.
12355      * @param {Number} index
12356      */
12357     refreshNode : function(index){
12358         this.onUpdate(this.store, this.store.getAt(index));
12359     },
12360
12361     updateIndexes : function(startIndex, endIndex){
12362         var ns = this.nodes;
12363         startIndex = startIndex || 0;
12364         endIndex = endIndex || ns.length - 1;
12365         for(var i = startIndex; i <= endIndex; i++){
12366             ns[i].nodeIndex = i;
12367         }
12368     },
12369
12370     /**
12371      * Changes the data store this view uses and refresh the view.
12372      * @param {Store} store
12373      */
12374     setStore : function(store, initial){
12375         if(!initial && this.store){
12376             this.store.un("datachanged", this.refresh);
12377             this.store.un("add", this.onAdd);
12378             this.store.un("remove", this.onRemove);
12379             this.store.un("update", this.onUpdate);
12380             this.store.un("clear", this.refresh);
12381             this.store.un("beforeload", this.onBeforeLoad);
12382             this.store.un("load", this.onLoad);
12383             this.store.un("loadexception", this.onLoad);
12384         }
12385         if(store){
12386           
12387             store.on("datachanged", this.refresh, this);
12388             store.on("add", this.onAdd, this);
12389             store.on("remove", this.onRemove, this);
12390             store.on("update", this.onUpdate, this);
12391             store.on("clear", this.refresh, this);
12392             store.on("beforeload", this.onBeforeLoad, this);
12393             store.on("load", this.onLoad, this);
12394             store.on("loadexception", this.onLoad, this);
12395         }
12396         
12397         if(store){
12398             this.refresh();
12399         }
12400     },
12401     /**
12402      * onbeforeLoad - masks the loading area.
12403      *
12404      */
12405     onBeforeLoad : function(store,opts)
12406     {
12407          Roo.log('onBeforeLoad');   
12408         if (!opts.add) {
12409             this.el.update("");
12410         }
12411         this.el.mask(this.mask ? this.mask : "Loading" ); 
12412     },
12413     onLoad : function ()
12414     {
12415         this.el.unmask();
12416     },
12417     
12418
12419     /**
12420      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12421      * @param {HTMLElement} node
12422      * @return {HTMLElement} The template node
12423      */
12424     findItemFromChild : function(node){
12425         var el = this.dataName  ?
12426             this.el.child('.roo-tpl-' + this.dataName,true) :
12427             this.el.dom; 
12428         
12429         if(!node || node.parentNode == el){
12430                     return node;
12431             }
12432             var p = node.parentNode;
12433             while(p && p != el){
12434             if(p.parentNode == el){
12435                 return p;
12436             }
12437             p = p.parentNode;
12438         }
12439             return null;
12440     },
12441
12442     /** @ignore */
12443     onClick : function(e){
12444         var item = this.findItemFromChild(e.getTarget());
12445         if(item){
12446             var index = this.indexOf(item);
12447             if(this.onItemClick(item, index, e) !== false){
12448                 this.fireEvent("click", this, index, item, e);
12449             }
12450         }else{
12451             this.clearSelections();
12452         }
12453     },
12454
12455     /** @ignore */
12456     onContextMenu : function(e){
12457         var item = this.findItemFromChild(e.getTarget());
12458         if(item){
12459             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12460         }
12461     },
12462
12463     /** @ignore */
12464     onDblClick : function(e){
12465         var item = this.findItemFromChild(e.getTarget());
12466         if(item){
12467             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12468         }
12469     },
12470
12471     onItemClick : function(item, index, e)
12472     {
12473         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12474             return false;
12475         }
12476         if (this.toggleSelect) {
12477             var m = this.isSelected(item) ? 'unselect' : 'select';
12478             Roo.log(m);
12479             var _t = this;
12480             _t[m](item, true, false);
12481             return true;
12482         }
12483         if(this.multiSelect || this.singleSelect){
12484             if(this.multiSelect && e.shiftKey && this.lastSelection){
12485                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12486             }else{
12487                 this.select(item, this.multiSelect && e.ctrlKey);
12488                 this.lastSelection = item;
12489             }
12490             
12491             if(!this.tickable){
12492                 e.preventDefault();
12493             }
12494             
12495         }
12496         return true;
12497     },
12498
12499     /**
12500      * Get the number of selected nodes.
12501      * @return {Number}
12502      */
12503     getSelectionCount : function(){
12504         return this.selections.length;
12505     },
12506
12507     /**
12508      * Get the currently selected nodes.
12509      * @return {Array} An array of HTMLElements
12510      */
12511     getSelectedNodes : function(){
12512         return this.selections;
12513     },
12514
12515     /**
12516      * Get the indexes of the selected nodes.
12517      * @return {Array}
12518      */
12519     getSelectedIndexes : function(){
12520         var indexes = [], s = this.selections;
12521         for(var i = 0, len = s.length; i < len; i++){
12522             indexes.push(s[i].nodeIndex);
12523         }
12524         return indexes;
12525     },
12526
12527     /**
12528      * Clear all selections
12529      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12530      */
12531     clearSelections : function(suppressEvent){
12532         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12533             this.cmp.elements = this.selections;
12534             this.cmp.removeClass(this.selectedClass);
12535             this.selections = [];
12536             if(!suppressEvent){
12537                 this.fireEvent("selectionchange", this, this.selections);
12538             }
12539         }
12540     },
12541
12542     /**
12543      * Returns true if the passed node is selected
12544      * @param {HTMLElement/Number} node The node or node index
12545      * @return {Boolean}
12546      */
12547     isSelected : function(node){
12548         var s = this.selections;
12549         if(s.length < 1){
12550             return false;
12551         }
12552         node = this.getNode(node);
12553         return s.indexOf(node) !== -1;
12554     },
12555
12556     /**
12557      * Selects nodes.
12558      * @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
12559      * @param {Boolean} keepExisting (optional) true to keep existing selections
12560      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12561      */
12562     select : function(nodeInfo, keepExisting, suppressEvent){
12563         if(nodeInfo instanceof Array){
12564             if(!keepExisting){
12565                 this.clearSelections(true);
12566             }
12567             for(var i = 0, len = nodeInfo.length; i < len; i++){
12568                 this.select(nodeInfo[i], true, true);
12569             }
12570             return;
12571         } 
12572         var node = this.getNode(nodeInfo);
12573         if(!node || this.isSelected(node)){
12574             return; // already selected.
12575         }
12576         if(!keepExisting){
12577             this.clearSelections(true);
12578         }
12579         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12580             Roo.fly(node).addClass(this.selectedClass);
12581             this.selections.push(node);
12582             if(!suppressEvent){
12583                 this.fireEvent("selectionchange", this, this.selections);
12584             }
12585         }
12586         
12587         
12588     },
12589       /**
12590      * Unselects nodes.
12591      * @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
12592      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12593      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12594      */
12595     unselect : function(nodeInfo, keepExisting, suppressEvent)
12596     {
12597         if(nodeInfo instanceof Array){
12598             Roo.each(this.selections, function(s) {
12599                 this.unselect(s, nodeInfo);
12600             }, this);
12601             return;
12602         }
12603         var node = this.getNode(nodeInfo);
12604         if(!node || !this.isSelected(node)){
12605             Roo.log("not selected");
12606             return; // not selected.
12607         }
12608         // fireevent???
12609         var ns = [];
12610         Roo.each(this.selections, function(s) {
12611             if (s == node ) {
12612                 Roo.fly(node).removeClass(this.selectedClass);
12613
12614                 return;
12615             }
12616             ns.push(s);
12617         },this);
12618         
12619         this.selections= ns;
12620         this.fireEvent("selectionchange", this, this.selections);
12621     },
12622
12623     /**
12624      * Gets a template node.
12625      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12626      * @return {HTMLElement} The node or null if it wasn't found
12627      */
12628     getNode : function(nodeInfo){
12629         if(typeof nodeInfo == "string"){
12630             return document.getElementById(nodeInfo);
12631         }else if(typeof nodeInfo == "number"){
12632             return this.nodes[nodeInfo];
12633         }
12634         return nodeInfo;
12635     },
12636
12637     /**
12638      * Gets a range template nodes.
12639      * @param {Number} startIndex
12640      * @param {Number} endIndex
12641      * @return {Array} An array of nodes
12642      */
12643     getNodes : function(start, end){
12644         var ns = this.nodes;
12645         start = start || 0;
12646         end = typeof end == "undefined" ? ns.length - 1 : end;
12647         var nodes = [];
12648         if(start <= end){
12649             for(var i = start; i <= end; i++){
12650                 nodes.push(ns[i]);
12651             }
12652         } else{
12653             for(var i = start; i >= end; i--){
12654                 nodes.push(ns[i]);
12655             }
12656         }
12657         return nodes;
12658     },
12659
12660     /**
12661      * Finds the index of the passed node
12662      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12663      * @return {Number} The index of the node or -1
12664      */
12665     indexOf : function(node){
12666         node = this.getNode(node);
12667         if(typeof node.nodeIndex == "number"){
12668             return node.nodeIndex;
12669         }
12670         var ns = this.nodes;
12671         for(var i = 0, len = ns.length; i < len; i++){
12672             if(ns[i] == node){
12673                 return i;
12674             }
12675         }
12676         return -1;
12677     }
12678 });
12679 /*
12680  * - LGPL
12681  *
12682  * based on jquery fullcalendar
12683  * 
12684  */
12685
12686 Roo.bootstrap = Roo.bootstrap || {};
12687 /**
12688  * @class Roo.bootstrap.Calendar
12689  * @extends Roo.bootstrap.Component
12690  * Bootstrap Calendar class
12691  * @cfg {Boolean} loadMask (true|false) default false
12692  * @cfg {Object} header generate the user specific header of the calendar, default false
12693
12694  * @constructor
12695  * Create a new Container
12696  * @param {Object} config The config object
12697  */
12698
12699
12700
12701 Roo.bootstrap.Calendar = function(config){
12702     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12703      this.addEvents({
12704         /**
12705              * @event select
12706              * Fires when a date is selected
12707              * @param {DatePicker} this
12708              * @param {Date} date The selected date
12709              */
12710         'select': true,
12711         /**
12712              * @event monthchange
12713              * Fires when the displayed month changes 
12714              * @param {DatePicker} this
12715              * @param {Date} date The selected month
12716              */
12717         'monthchange': true,
12718         /**
12719              * @event evententer
12720              * Fires when mouse over an event
12721              * @param {Calendar} this
12722              * @param {event} Event
12723              */
12724         'evententer': true,
12725         /**
12726              * @event eventleave
12727              * Fires when the mouse leaves an
12728              * @param {Calendar} this
12729              * @param {event}
12730              */
12731         'eventleave': true,
12732         /**
12733              * @event eventclick
12734              * Fires when the mouse click an
12735              * @param {Calendar} this
12736              * @param {event}
12737              */
12738         'eventclick': true
12739         
12740     });
12741
12742 };
12743
12744 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12745     
12746      /**
12747      * @cfg {Number} startDay
12748      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12749      */
12750     startDay : 0,
12751     
12752     loadMask : false,
12753     
12754     header : false,
12755       
12756     getAutoCreate : function(){
12757         
12758         
12759         var fc_button = function(name, corner, style, content ) {
12760             return Roo.apply({},{
12761                 tag : 'span',
12762                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12763                          (corner.length ?
12764                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12765                             ''
12766                         ),
12767                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12768                 unselectable: 'on'
12769             });
12770         };
12771         
12772         var header = {};
12773         
12774         if(!this.header){
12775             header = {
12776                 tag : 'table',
12777                 cls : 'fc-header',
12778                 style : 'width:100%',
12779                 cn : [
12780                     {
12781                         tag: 'tr',
12782                         cn : [
12783                             {
12784                                 tag : 'td',
12785                                 cls : 'fc-header-left',
12786                                 cn : [
12787                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12788                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12789                                     { tag: 'span', cls: 'fc-header-space' },
12790                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12791
12792
12793                                 ]
12794                             },
12795
12796                             {
12797                                 tag : 'td',
12798                                 cls : 'fc-header-center',
12799                                 cn : [
12800                                     {
12801                                         tag: 'span',
12802                                         cls: 'fc-header-title',
12803                                         cn : {
12804                                             tag: 'H2',
12805                                             html : 'month / year'
12806                                         }
12807                                     }
12808
12809                                 ]
12810                             },
12811                             {
12812                                 tag : 'td',
12813                                 cls : 'fc-header-right',
12814                                 cn : [
12815                               /*      fc_button('month', 'left', '', 'month' ),
12816                                     fc_button('week', '', '', 'week' ),
12817                                     fc_button('day', 'right', '', 'day' )
12818                                 */    
12819
12820                                 ]
12821                             }
12822
12823                         ]
12824                     }
12825                 ]
12826             };
12827         }
12828         
12829         header = this.header;
12830         
12831        
12832         var cal_heads = function() {
12833             var ret = [];
12834             // fixme - handle this.
12835             
12836             for (var i =0; i < Date.dayNames.length; i++) {
12837                 var d = Date.dayNames[i];
12838                 ret.push({
12839                     tag: 'th',
12840                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12841                     html : d.substring(0,3)
12842                 });
12843                 
12844             }
12845             ret[0].cls += ' fc-first';
12846             ret[6].cls += ' fc-last';
12847             return ret;
12848         };
12849         var cal_cell = function(n) {
12850             return  {
12851                 tag: 'td',
12852                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12853                 cn : [
12854                     {
12855                         cn : [
12856                             {
12857                                 cls: 'fc-day-number',
12858                                 html: 'D'
12859                             },
12860                             {
12861                                 cls: 'fc-day-content',
12862                              
12863                                 cn : [
12864                                      {
12865                                         style: 'position: relative;' // height: 17px;
12866                                     }
12867                                 ]
12868                             }
12869                             
12870                             
12871                         ]
12872                     }
12873                 ]
12874                 
12875             }
12876         };
12877         var cal_rows = function() {
12878             
12879             var ret = []
12880             for (var r = 0; r < 6; r++) {
12881                 var row= {
12882                     tag : 'tr',
12883                     cls : 'fc-week',
12884                     cn : []
12885                 };
12886                 
12887                 for (var i =0; i < Date.dayNames.length; i++) {
12888                     var d = Date.dayNames[i];
12889                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12890
12891                 }
12892                 row.cn[0].cls+=' fc-first';
12893                 row.cn[0].cn[0].style = 'min-height:90px';
12894                 row.cn[6].cls+=' fc-last';
12895                 ret.push(row);
12896                 
12897             }
12898             ret[0].cls += ' fc-first';
12899             ret[4].cls += ' fc-prev-last';
12900             ret[5].cls += ' fc-last';
12901             return ret;
12902             
12903         };
12904         
12905         var cal_table = {
12906             tag: 'table',
12907             cls: 'fc-border-separate',
12908             style : 'width:100%',
12909             cellspacing  : 0,
12910             cn : [
12911                 { 
12912                     tag: 'thead',
12913                     cn : [
12914                         { 
12915                             tag: 'tr',
12916                             cls : 'fc-first fc-last',
12917                             cn : cal_heads()
12918                         }
12919                     ]
12920                 },
12921                 { 
12922                     tag: 'tbody',
12923                     cn : cal_rows()
12924                 }
12925                   
12926             ]
12927         };
12928          
12929          var cfg = {
12930             cls : 'fc fc-ltr',
12931             cn : [
12932                 header,
12933                 {
12934                     cls : 'fc-content',
12935                     style : "position: relative;",
12936                     cn : [
12937                         {
12938                             cls : 'fc-view fc-view-month fc-grid',
12939                             style : 'position: relative',
12940                             unselectable : 'on',
12941                             cn : [
12942                                 {
12943                                     cls : 'fc-event-container',
12944                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12945                                 },
12946                                 cal_table
12947                             ]
12948                         }
12949                     ]
12950     
12951                 }
12952            ] 
12953             
12954         };
12955         
12956          
12957         
12958         return cfg;
12959     },
12960     
12961     
12962     initEvents : function()
12963     {
12964         if(!this.store){
12965             throw "can not find store for calendar";
12966         }
12967         
12968         var mark = {
12969             tag: "div",
12970             cls:"x-dlg-mask",
12971             style: "text-align:center",
12972             cn: [
12973                 {
12974                     tag: "div",
12975                     style: "background-color:white;width:50%;margin:250 auto",
12976                     cn: [
12977                         {
12978                             tag: "img",
12979                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12980                         },
12981                         {
12982                             tag: "span",
12983                             html: "Loading"
12984                         }
12985                         
12986                     ]
12987                 }
12988             ]
12989         }
12990         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12991         
12992         var size = this.el.select('.fc-content', true).first().getSize();
12993         this.maskEl.setSize(size.width, size.height);
12994         this.maskEl.enableDisplayMode("block");
12995         if(!this.loadMask){
12996             this.maskEl.hide();
12997         }
12998         
12999         this.store = Roo.factory(this.store, Roo.data);
13000         this.store.on('load', this.onLoad, this);
13001         this.store.on('beforeload', this.onBeforeLoad, this);
13002         
13003         this.resize();
13004         
13005         this.cells = this.el.select('.fc-day',true);
13006         //Roo.log(this.cells);
13007         this.textNodes = this.el.query('.fc-day-number');
13008         this.cells.addClassOnOver('fc-state-hover');
13009         
13010         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13011         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13012         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13013         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13014         
13015         this.on('monthchange', this.onMonthChange, this);
13016         
13017         this.update(new Date().clearTime());
13018     },
13019     
13020     resize : function() {
13021         var sz  = this.el.getSize();
13022         
13023         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13024         this.el.select('.fc-day-content div',true).setHeight(34);
13025     },
13026     
13027     
13028     // private
13029     showPrevMonth : function(e){
13030         this.update(this.activeDate.add("mo", -1));
13031     },
13032     showToday : function(e){
13033         this.update(new Date().clearTime());
13034     },
13035     // private
13036     showNextMonth : function(e){
13037         this.update(this.activeDate.add("mo", 1));
13038     },
13039
13040     // private
13041     showPrevYear : function(){
13042         this.update(this.activeDate.add("y", -1));
13043     },
13044
13045     // private
13046     showNextYear : function(){
13047         this.update(this.activeDate.add("y", 1));
13048     },
13049
13050     
13051    // private
13052     update : function(date)
13053     {
13054         var vd = this.activeDate;
13055         this.activeDate = date;
13056 //        if(vd && this.el){
13057 //            var t = date.getTime();
13058 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13059 //                Roo.log('using add remove');
13060 //                
13061 //                this.fireEvent('monthchange', this, date);
13062 //                
13063 //                this.cells.removeClass("fc-state-highlight");
13064 //                this.cells.each(function(c){
13065 //                   if(c.dateValue == t){
13066 //                       c.addClass("fc-state-highlight");
13067 //                       setTimeout(function(){
13068 //                            try{c.dom.firstChild.focus();}catch(e){}
13069 //                       }, 50);
13070 //                       return false;
13071 //                   }
13072 //                   return true;
13073 //                });
13074 //                return;
13075 //            }
13076 //        }
13077         
13078         var days = date.getDaysInMonth();
13079         
13080         var firstOfMonth = date.getFirstDateOfMonth();
13081         var startingPos = firstOfMonth.getDay()-this.startDay;
13082         
13083         if(startingPos < this.startDay){
13084             startingPos += 7;
13085         }
13086         
13087         var pm = date.add(Date.MONTH, -1);
13088         var prevStart = pm.getDaysInMonth()-startingPos;
13089 //        
13090         this.cells = this.el.select('.fc-day',true);
13091         this.textNodes = this.el.query('.fc-day-number');
13092         this.cells.addClassOnOver('fc-state-hover');
13093         
13094         var cells = this.cells.elements;
13095         var textEls = this.textNodes;
13096         
13097         Roo.each(cells, function(cell){
13098             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13099         });
13100         
13101         days += startingPos;
13102
13103         // convert everything to numbers so it's fast
13104         var day = 86400000;
13105         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13106         //Roo.log(d);
13107         //Roo.log(pm);
13108         //Roo.log(prevStart);
13109         
13110         var today = new Date().clearTime().getTime();
13111         var sel = date.clearTime().getTime();
13112         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13113         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13114         var ddMatch = this.disabledDatesRE;
13115         var ddText = this.disabledDatesText;
13116         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13117         var ddaysText = this.disabledDaysText;
13118         var format = this.format;
13119         
13120         var setCellClass = function(cal, cell){
13121             cell.row = 0;
13122             cell.events = [];
13123             cell.more = [];
13124             //Roo.log('set Cell Class');
13125             cell.title = "";
13126             var t = d.getTime();
13127             
13128             //Roo.log(d);
13129             
13130             cell.dateValue = t;
13131             if(t == today){
13132                 cell.className += " fc-today";
13133                 cell.className += " fc-state-highlight";
13134                 cell.title = cal.todayText;
13135             }
13136             if(t == sel){
13137                 // disable highlight in other month..
13138                 //cell.className += " fc-state-highlight";
13139                 
13140             }
13141             // disabling
13142             if(t < min) {
13143                 cell.className = " fc-state-disabled";
13144                 cell.title = cal.minText;
13145                 return;
13146             }
13147             if(t > max) {
13148                 cell.className = " fc-state-disabled";
13149                 cell.title = cal.maxText;
13150                 return;
13151             }
13152             if(ddays){
13153                 if(ddays.indexOf(d.getDay()) != -1){
13154                     cell.title = ddaysText;
13155                     cell.className = " fc-state-disabled";
13156                 }
13157             }
13158             if(ddMatch && format){
13159                 var fvalue = d.dateFormat(format);
13160                 if(ddMatch.test(fvalue)){
13161                     cell.title = ddText.replace("%0", fvalue);
13162                     cell.className = " fc-state-disabled";
13163                 }
13164             }
13165             
13166             if (!cell.initialClassName) {
13167                 cell.initialClassName = cell.dom.className;
13168             }
13169             
13170             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13171         };
13172
13173         var i = 0;
13174         
13175         for(; i < startingPos; i++) {
13176             textEls[i].innerHTML = (++prevStart);
13177             d.setDate(d.getDate()+1);
13178             
13179             cells[i].className = "fc-past fc-other-month";
13180             setCellClass(this, cells[i]);
13181         }
13182         
13183         var intDay = 0;
13184         
13185         for(; i < days; i++){
13186             intDay = i - startingPos + 1;
13187             textEls[i].innerHTML = (intDay);
13188             d.setDate(d.getDate()+1);
13189             
13190             cells[i].className = ''; // "x-date-active";
13191             setCellClass(this, cells[i]);
13192         }
13193         var extraDays = 0;
13194         
13195         for(; i < 42; i++) {
13196             textEls[i].innerHTML = (++extraDays);
13197             d.setDate(d.getDate()+1);
13198             
13199             cells[i].className = "fc-future fc-other-month";
13200             setCellClass(this, cells[i]);
13201         }
13202         
13203         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13204         
13205         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13206         
13207         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13208         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13209         
13210         if(totalRows != 6){
13211             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13212             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13213         }
13214         
13215         this.fireEvent('monthchange', this, date);
13216         
13217         
13218         /*
13219         if(!this.internalRender){
13220             var main = this.el.dom.firstChild;
13221             var w = main.offsetWidth;
13222             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13223             Roo.fly(main).setWidth(w);
13224             this.internalRender = true;
13225             // opera does not respect the auto grow header center column
13226             // then, after it gets a width opera refuses to recalculate
13227             // without a second pass
13228             if(Roo.isOpera && !this.secondPass){
13229                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13230                 this.secondPass = true;
13231                 this.update.defer(10, this, [date]);
13232             }
13233         }
13234         */
13235         
13236     },
13237     
13238     findCell : function(dt) {
13239         dt = dt.clearTime().getTime();
13240         var ret = false;
13241         this.cells.each(function(c){
13242             //Roo.log("check " +c.dateValue + '?=' + dt);
13243             if(c.dateValue == dt){
13244                 ret = c;
13245                 return false;
13246             }
13247             return true;
13248         });
13249         
13250         return ret;
13251     },
13252     
13253     findCells : function(ev) {
13254         var s = ev.start.clone().clearTime().getTime();
13255        // Roo.log(s);
13256         var e= ev.end.clone().clearTime().getTime();
13257        // Roo.log(e);
13258         var ret = [];
13259         this.cells.each(function(c){
13260              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13261             
13262             if(c.dateValue > e){
13263                 return ;
13264             }
13265             if(c.dateValue < s){
13266                 return ;
13267             }
13268             ret.push(c);
13269         });
13270         
13271         return ret;    
13272     },
13273     
13274 //    findBestRow: function(cells)
13275 //    {
13276 //        var ret = 0;
13277 //        
13278 //        for (var i =0 ; i < cells.length;i++) {
13279 //            ret  = Math.max(cells[i].rows || 0,ret);
13280 //        }
13281 //        return ret;
13282 //        
13283 //    },
13284     
13285     
13286     addItem : function(ev)
13287     {
13288         // look for vertical location slot in
13289         var cells = this.findCells(ev);
13290         
13291 //        ev.row = this.findBestRow(cells);
13292         
13293         // work out the location.
13294         
13295         var crow = false;
13296         var rows = [];
13297         for(var i =0; i < cells.length; i++) {
13298             
13299             cells[i].row = cells[0].row;
13300             
13301             if(i == 0){
13302                 cells[i].row = cells[i].row + 1;
13303             }
13304             
13305             if (!crow) {
13306                 crow = {
13307                     start : cells[i],
13308                     end :  cells[i]
13309                 };
13310                 continue;
13311             }
13312             if (crow.start.getY() == cells[i].getY()) {
13313                 // on same row.
13314                 crow.end = cells[i];
13315                 continue;
13316             }
13317             // different row.
13318             rows.push(crow);
13319             crow = {
13320                 start: cells[i],
13321                 end : cells[i]
13322             };
13323             
13324         }
13325         
13326         rows.push(crow);
13327         ev.els = [];
13328         ev.rows = rows;
13329         ev.cells = cells;
13330         
13331         cells[0].events.push(ev);
13332         
13333         this.calevents.push(ev);
13334     },
13335     
13336     clearEvents: function() {
13337         
13338         if(!this.calevents){
13339             return;
13340         }
13341         
13342         Roo.each(this.cells.elements, function(c){
13343             c.row = 0;
13344             c.events = [];
13345             c.more = [];
13346         });
13347         
13348         Roo.each(this.calevents, function(e) {
13349             Roo.each(e.els, function(el) {
13350                 el.un('mouseenter' ,this.onEventEnter, this);
13351                 el.un('mouseleave' ,this.onEventLeave, this);
13352                 el.remove();
13353             },this);
13354         },this);
13355         
13356         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13357             e.remove();
13358         });
13359         
13360     },
13361     
13362     renderEvents: function()
13363     {   
13364         var _this = this;
13365         
13366         this.cells.each(function(c) {
13367             
13368             if(c.row < 5){
13369                 return;
13370             }
13371             
13372             var ev = c.events;
13373             
13374             var r = 4;
13375             if(c.row != c.events.length){
13376                 r = 4 - (4 - (c.row - c.events.length));
13377             }
13378             
13379             c.events = ev.slice(0, r);
13380             c.more = ev.slice(r);
13381             
13382             if(c.more.length && c.more.length == 1){
13383                 c.events.push(c.more.pop());
13384             }
13385             
13386             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13387             
13388         });
13389             
13390         this.cells.each(function(c) {
13391             
13392             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13393             
13394             
13395             for (var e = 0; e < c.events.length; e++){
13396                 var ev = c.events[e];
13397                 var rows = ev.rows;
13398                 
13399                 for(var i = 0; i < rows.length; i++) {
13400                 
13401                     // how many rows should it span..
13402
13403                     var  cfg = {
13404                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13405                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13406
13407                         unselectable : "on",
13408                         cn : [
13409                             {
13410                                 cls: 'fc-event-inner',
13411                                 cn : [
13412     //                                {
13413     //                                  tag:'span',
13414     //                                  cls: 'fc-event-time',
13415     //                                  html : cells.length > 1 ? '' : ev.time
13416     //                                },
13417                                     {
13418                                       tag:'span',
13419                                       cls: 'fc-event-title',
13420                                       html : String.format('{0}', ev.title)
13421                                     }
13422
13423
13424                                 ]
13425                             },
13426                             {
13427                                 cls: 'ui-resizable-handle ui-resizable-e',
13428                                 html : '&nbsp;&nbsp;&nbsp'
13429                             }
13430
13431                         ]
13432                     };
13433
13434                     if (i == 0) {
13435                         cfg.cls += ' fc-event-start';
13436                     }
13437                     if ((i+1) == rows.length) {
13438                         cfg.cls += ' fc-event-end';
13439                     }
13440
13441                     var ctr = _this.el.select('.fc-event-container',true).first();
13442                     var cg = ctr.createChild(cfg);
13443
13444                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13445                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13446
13447                     var r = (c.more.length) ? 1 : 0;
13448                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13449                     cg.setWidth(ebox.right - sbox.x -2);
13450
13451                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13452                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13453                     cg.on('click', _this.onEventClick, _this, ev);
13454
13455                     ev.els.push(cg);
13456                     
13457                 }
13458                 
13459             }
13460             
13461             
13462             if(c.more.length){
13463                 var  cfg = {
13464                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13465                     style : 'position: absolute',
13466                     unselectable : "on",
13467                     cn : [
13468                         {
13469                             cls: 'fc-event-inner',
13470                             cn : [
13471                                 {
13472                                   tag:'span',
13473                                   cls: 'fc-event-title',
13474                                   html : 'More'
13475                                 }
13476
13477
13478                             ]
13479                         },
13480                         {
13481                             cls: 'ui-resizable-handle ui-resizable-e',
13482                             html : '&nbsp;&nbsp;&nbsp'
13483                         }
13484
13485                     ]
13486                 };
13487
13488                 var ctr = _this.el.select('.fc-event-container',true).first();
13489                 var cg = ctr.createChild(cfg);
13490
13491                 var sbox = c.select('.fc-day-content',true).first().getBox();
13492                 var ebox = c.select('.fc-day-content',true).first().getBox();
13493                 //Roo.log(cg);
13494                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13495                 cg.setWidth(ebox.right - sbox.x -2);
13496
13497                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13498                 
13499             }
13500             
13501         });
13502         
13503         
13504         
13505     },
13506     
13507     onEventEnter: function (e, el,event,d) {
13508         this.fireEvent('evententer', this, el, event);
13509     },
13510     
13511     onEventLeave: function (e, el,event,d) {
13512         this.fireEvent('eventleave', this, el, event);
13513     },
13514     
13515     onEventClick: function (e, el,event,d) {
13516         this.fireEvent('eventclick', this, el, event);
13517     },
13518     
13519     onMonthChange: function () {
13520         this.store.load();
13521     },
13522     
13523     onMoreEventClick: function(e, el, more)
13524     {
13525         var _this = this;
13526         
13527         this.calpopover.placement = 'right';
13528         this.calpopover.setTitle('More');
13529         
13530         this.calpopover.setContent('');
13531         
13532         var ctr = this.calpopover.el.select('.popover-content', true).first();
13533         
13534         Roo.each(more, function(m){
13535             var cfg = {
13536                 cls : 'fc-event-hori fc-event-draggable',
13537                 html : m.title
13538             }
13539             var cg = ctr.createChild(cfg);
13540             
13541             cg.on('click', _this.onEventClick, _this, m);
13542         });
13543         
13544         this.calpopover.show(el);
13545         
13546         
13547     },
13548     
13549     onLoad: function () 
13550     {   
13551         this.calevents = [];
13552         var cal = this;
13553         
13554         if(this.store.getCount() > 0){
13555             this.store.data.each(function(d){
13556                cal.addItem({
13557                     id : d.data.id,
13558                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13559                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13560                     time : d.data.start_time,
13561                     title : d.data.title,
13562                     description : d.data.description,
13563                     venue : d.data.venue
13564                 });
13565             });
13566         }
13567         
13568         this.renderEvents();
13569         
13570         if(this.calevents.length && this.loadMask){
13571             this.maskEl.hide();
13572         }
13573     },
13574     
13575     onBeforeLoad: function()
13576     {
13577         this.clearEvents();
13578         if(this.loadMask){
13579             this.maskEl.show();
13580         }
13581     }
13582 });
13583
13584  
13585  /*
13586  * - LGPL
13587  *
13588  * element
13589  * 
13590  */
13591
13592 /**
13593  * @class Roo.bootstrap.Popover
13594  * @extends Roo.bootstrap.Component
13595  * Bootstrap Popover class
13596  * @cfg {String} html contents of the popover   (or false to use children..)
13597  * @cfg {String} title of popover (or false to hide)
13598  * @cfg {String} placement how it is placed
13599  * @cfg {String} trigger click || hover (or false to trigger manually)
13600  * @cfg {String} over what (parent or false to trigger manually.)
13601  * 
13602  * @constructor
13603  * Create a new Popover
13604  * @param {Object} config The config object
13605  */
13606
13607 Roo.bootstrap.Popover = function(config){
13608     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13609 };
13610
13611 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13612     
13613     title: 'Fill in a title',
13614     html: false,
13615     
13616     placement : 'right',
13617     trigger : 'hover', // hover
13618     
13619     over: 'parent',
13620     
13621     can_build_overlaid : false,
13622     
13623     getChildContainer : function()
13624     {
13625         return this.el.select('.popover-content',true).first();
13626     },
13627     
13628     getAutoCreate : function(){
13629          Roo.log('make popover?');
13630         var cfg = {
13631            cls : 'popover roo-dynamic',
13632            style: 'display:block',
13633            cn : [
13634                 {
13635                     cls : 'arrow'
13636                 },
13637                 {
13638                     cls : 'popover-inner',
13639                     cn : [
13640                         {
13641                             tag: 'h3',
13642                             cls: 'popover-title',
13643                             html : this.title
13644                         },
13645                         {
13646                             cls : 'popover-content',
13647                             html : this.html
13648                         }
13649                     ]
13650                     
13651                 }
13652            ]
13653         };
13654         
13655         return cfg;
13656     },
13657     setTitle: function(str)
13658     {
13659         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13660     },
13661     setContent: function(str)
13662     {
13663         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13664     },
13665     // as it get's added to the bottom of the page.
13666     onRender : function(ct, position)
13667     {
13668         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13669         if(!this.el){
13670             var cfg = Roo.apply({},  this.getAutoCreate());
13671             cfg.id = Roo.id();
13672             
13673             if (this.cls) {
13674                 cfg.cls += ' ' + this.cls;
13675             }
13676             if (this.style) {
13677                 cfg.style = this.style;
13678             }
13679             Roo.log("adding to ")
13680             this.el = Roo.get(document.body).createChild(cfg, position);
13681             Roo.log(this.el);
13682         }
13683         this.initEvents();
13684     },
13685     
13686     initEvents : function()
13687     {
13688         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13689         this.el.enableDisplayMode('block');
13690         this.el.hide();
13691         if (this.over === false) {
13692             return; 
13693         }
13694         if (this.triggers === false) {
13695             return;
13696         }
13697         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13698         var triggers = this.trigger ? this.trigger.split(' ') : [];
13699         Roo.each(triggers, function(trigger) {
13700         
13701             if (trigger == 'click') {
13702                 on_el.on('click', this.toggle, this);
13703             } else if (trigger != 'manual') {
13704                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13705                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13706       
13707                 on_el.on(eventIn  ,this.enter, this);
13708                 on_el.on(eventOut, this.leave, this);
13709             }
13710         }, this);
13711         
13712     },
13713     
13714     
13715     // private
13716     timeout : null,
13717     hoverState : null,
13718     
13719     toggle : function () {
13720         this.hoverState == 'in' ? this.leave() : this.enter();
13721     },
13722     
13723     enter : function () {
13724        
13725     
13726         clearTimeout(this.timeout);
13727     
13728         this.hoverState = 'in'
13729     
13730         if (!this.delay || !this.delay.show) {
13731             this.show();
13732             return 
13733         }
13734         var _t = this;
13735         this.timeout = setTimeout(function () {
13736             if (_t.hoverState == 'in') {
13737                 _t.show();
13738             }
13739         }, this.delay.show)
13740     },
13741     leave : function() {
13742         clearTimeout(this.timeout);
13743     
13744         this.hoverState = 'out'
13745     
13746         if (!this.delay || !this.delay.hide) {
13747             this.hide();
13748             return 
13749         }
13750         var _t = this;
13751         this.timeout = setTimeout(function () {
13752             if (_t.hoverState == 'out') {
13753                 _t.hide();
13754             }
13755         }, this.delay.hide)
13756     },
13757     
13758     show : function (on_el)
13759     {
13760         if (!on_el) {
13761             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13762         }
13763         // set content.
13764         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13765         if (this.html !== false) {
13766             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13767         }
13768         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13769         if (!this.title.length) {
13770             this.el.select('.popover-title',true).hide();
13771         }
13772         
13773         var placement = typeof this.placement == 'function' ?
13774             this.placement.call(this, this.el, on_el) :
13775             this.placement;
13776             
13777         var autoToken = /\s?auto?\s?/i;
13778         var autoPlace = autoToken.test(placement);
13779         if (autoPlace) {
13780             placement = placement.replace(autoToken, '') || 'top';
13781         }
13782         
13783         //this.el.detach()
13784         //this.el.setXY([0,0]);
13785         this.el.show();
13786         this.el.dom.style.display='block';
13787         this.el.addClass(placement);
13788         
13789         //this.el.appendTo(on_el);
13790         
13791         var p = this.getPosition();
13792         var box = this.el.getBox();
13793         
13794         if (autoPlace) {
13795             // fixme..
13796         }
13797         var align = Roo.bootstrap.Popover.alignment[placement]
13798         this.el.alignTo(on_el, align[0],align[1]);
13799         //var arrow = this.el.select('.arrow',true).first();
13800         //arrow.set(align[2], 
13801         
13802         this.el.addClass('in');
13803         this.hoverState = null;
13804         
13805         if (this.el.hasClass('fade')) {
13806             // fade it?
13807         }
13808         
13809     },
13810     hide : function()
13811     {
13812         this.el.setXY([0,0]);
13813         this.el.removeClass('in');
13814         this.el.hide();
13815         
13816     }
13817     
13818 });
13819
13820 Roo.bootstrap.Popover.alignment = {
13821     'left' : ['r-l', [-10,0], 'right'],
13822     'right' : ['l-r', [10,0], 'left'],
13823     'bottom' : ['t-b', [0,10], 'top'],
13824     'top' : [ 'b-t', [0,-10], 'bottom']
13825 };
13826
13827  /*
13828  * - LGPL
13829  *
13830  * Progress
13831  * 
13832  */
13833
13834 /**
13835  * @class Roo.bootstrap.Progress
13836  * @extends Roo.bootstrap.Component
13837  * Bootstrap Progress class
13838  * @cfg {Boolean} striped striped of the progress bar
13839  * @cfg {Boolean} active animated of the progress bar
13840  * 
13841  * 
13842  * @constructor
13843  * Create a new Progress
13844  * @param {Object} config The config object
13845  */
13846
13847 Roo.bootstrap.Progress = function(config){
13848     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13849 };
13850
13851 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13852     
13853     striped : false,
13854     active: false,
13855     
13856     getAutoCreate : function(){
13857         var cfg = {
13858             tag: 'div',
13859             cls: 'progress'
13860         };
13861         
13862         
13863         if(this.striped){
13864             cfg.cls += ' progress-striped';
13865         }
13866       
13867         if(this.active){
13868             cfg.cls += ' active';
13869         }
13870         
13871         
13872         return cfg;
13873     }
13874    
13875 });
13876
13877  
13878
13879  /*
13880  * - LGPL
13881  *
13882  * ProgressBar
13883  * 
13884  */
13885
13886 /**
13887  * @class Roo.bootstrap.ProgressBar
13888  * @extends Roo.bootstrap.Component
13889  * Bootstrap ProgressBar class
13890  * @cfg {Number} aria_valuenow aria-value now
13891  * @cfg {Number} aria_valuemin aria-value min
13892  * @cfg {Number} aria_valuemax aria-value max
13893  * @cfg {String} label label for the progress bar
13894  * @cfg {String} panel (success | info | warning | danger )
13895  * @cfg {String} role role of the progress bar
13896  * @cfg {String} sr_only text
13897  * 
13898  * 
13899  * @constructor
13900  * Create a new ProgressBar
13901  * @param {Object} config The config object
13902  */
13903
13904 Roo.bootstrap.ProgressBar = function(config){
13905     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13906 };
13907
13908 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13909     
13910     aria_valuenow : 0,
13911     aria_valuemin : 0,
13912     aria_valuemax : 100,
13913     label : false,
13914     panel : false,
13915     role : false,
13916     sr_only: false,
13917     
13918     getAutoCreate : function()
13919     {
13920         
13921         var cfg = {
13922             tag: 'div',
13923             cls: 'progress-bar',
13924             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13925         };
13926         
13927         if(this.sr_only){
13928             cfg.cn = {
13929                 tag: 'span',
13930                 cls: 'sr-only',
13931                 html: this.sr_only
13932             }
13933         }
13934         
13935         if(this.role){
13936             cfg.role = this.role;
13937         }
13938         
13939         if(this.aria_valuenow){
13940             cfg['aria-valuenow'] = this.aria_valuenow;
13941         }
13942         
13943         if(this.aria_valuemin){
13944             cfg['aria-valuemin'] = this.aria_valuemin;
13945         }
13946         
13947         if(this.aria_valuemax){
13948             cfg['aria-valuemax'] = this.aria_valuemax;
13949         }
13950         
13951         if(this.label && !this.sr_only){
13952             cfg.html = this.label;
13953         }
13954         
13955         if(this.panel){
13956             cfg.cls += ' progress-bar-' + this.panel;
13957         }
13958         
13959         return cfg;
13960     },
13961     
13962     update : function(aria_valuenow)
13963     {
13964         this.aria_valuenow = aria_valuenow;
13965         
13966         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13967     }
13968    
13969 });
13970
13971  
13972
13973  /*
13974  * - LGPL
13975  *
13976  * column
13977  * 
13978  */
13979
13980 /**
13981  * @class Roo.bootstrap.TabGroup
13982  * @extends Roo.bootstrap.Column
13983  * Bootstrap Column class
13984  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13985  * @cfg {Boolean} carousel true to make the group behave like a carousel
13986  * 
13987  * @constructor
13988  * Create a new TabGroup
13989  * @param {Object} config The config object
13990  */
13991
13992 Roo.bootstrap.TabGroup = function(config){
13993     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13994     if (!this.navId) {
13995         this.navId = Roo.id();
13996     }
13997     this.tabs = [];
13998     Roo.bootstrap.TabGroup.register(this);
13999     
14000 };
14001
14002 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14003     
14004     carousel : false,
14005     transition : false,
14006      
14007     getAutoCreate : function()
14008     {
14009         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14010         
14011         cfg.cls += ' tab-content';
14012         
14013         if (this.carousel) {
14014             cfg.cls += ' carousel slide';
14015             cfg.cn = [{
14016                cls : 'carousel-inner'
14017             }]
14018         }
14019         
14020         
14021         return cfg;
14022     },
14023     getChildContainer : function()
14024     {
14025         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14026     },
14027     
14028     /**
14029     * register a Navigation item
14030     * @param {Roo.bootstrap.NavItem} the navitem to add
14031     */
14032     register : function(item)
14033     {
14034         this.tabs.push( item);
14035         item.navId = this.navId; // not really needed..
14036     
14037     },
14038     
14039     getActivePanel : function()
14040     {
14041         var r = false;
14042         Roo.each(this.tabs, function(t) {
14043             if (t.active) {
14044                 r = t;
14045                 return false;
14046             }
14047             return null;
14048         });
14049         return r;
14050         
14051     },
14052     getPanelByName : function(n)
14053     {
14054         var r = false;
14055         Roo.each(this.tabs, function(t) {
14056             if (t.tabId == n) {
14057                 r = t;
14058                 return false;
14059             }
14060             return null;
14061         });
14062         return r;
14063     },
14064     indexOfPanel : function(p)
14065     {
14066         var r = false;
14067         Roo.each(this.tabs, function(t,i) {
14068             if (t.tabId == p.tabId) {
14069                 r = i;
14070                 return false;
14071             }
14072             return null;
14073         });
14074         return r;
14075     },
14076     /**
14077      * show a specific panel
14078      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14079      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14080      */
14081     showPanel : function (pan)
14082     {
14083         
14084         if (typeof(pan) == 'number') {
14085             pan = this.tabs[pan];
14086         }
14087         if (typeof(pan) == 'string') {
14088             pan = this.getPanelByName(pan);
14089         }
14090         if (pan.tabId == this.getActivePanel().tabId) {
14091             return true;
14092         }
14093         var cur = this.getActivePanel();
14094         
14095         if (false === cur.fireEvent('beforedeactivate')) {
14096             return false;
14097         }
14098         
14099         if (this.carousel) {
14100             this.transition = true;
14101             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14102             var lr = dir == 'next' ? 'left' : 'right';
14103             pan.el.addClass(dir); // or prev
14104             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14105             cur.el.addClass(lr); // or right
14106             pan.el.addClass(lr);
14107             
14108             var _this = this;
14109             cur.el.on('transitionend', function() {
14110                 Roo.log("trans end?");
14111                 
14112                 pan.el.removeClass([lr,dir]);
14113                 pan.setActive(true);
14114                 
14115                 cur.el.removeClass([lr]);
14116                 cur.setActive(false);
14117                 
14118                 _this.transition = false;
14119                 
14120             }, this, { single:  true } );
14121             return true;
14122         }
14123         
14124         cur.setActive(false);
14125         pan.setActive(true);
14126         return true;
14127         
14128     },
14129     showPanelNext : function()
14130     {
14131         var i = this.indexOfPanel(this.getActivePanel());
14132         if (i > this.tabs.length) {
14133             return;
14134         }
14135         this.showPanel(this.tabs[i+1]);
14136     },
14137     showPanelPrev : function()
14138     {
14139         var i = this.indexOfPanel(this.getActivePanel());
14140         if (i  < 1) {
14141             return;
14142         }
14143         this.showPanel(this.tabs[i-1]);
14144     }
14145     
14146     
14147   
14148 });
14149
14150  
14151
14152  
14153  
14154 Roo.apply(Roo.bootstrap.TabGroup, {
14155     
14156     groups: {},
14157      /**
14158     * register a Navigation Group
14159     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14160     */
14161     register : function(navgrp)
14162     {
14163         this.groups[navgrp.navId] = navgrp;
14164         
14165     },
14166     /**
14167     * fetch a Navigation Group based on the navigation ID
14168     * if one does not exist , it will get created.
14169     * @param {string} the navgroup to add
14170     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14171     */
14172     get: function(navId) {
14173         if (typeof(this.groups[navId]) == 'undefined') {
14174             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14175         }
14176         return this.groups[navId] ;
14177     }
14178     
14179     
14180     
14181 });
14182
14183  /*
14184  * - LGPL
14185  *
14186  * TabPanel
14187  * 
14188  */
14189
14190 /**
14191  * @class Roo.bootstrap.TabPanel
14192  * @extends Roo.bootstrap.Component
14193  * Bootstrap TabPanel class
14194  * @cfg {Boolean} active panel active
14195  * @cfg {String} html panel content
14196  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14197  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14198  * 
14199  * 
14200  * @constructor
14201  * Create a new TabPanel
14202  * @param {Object} config The config object
14203  */
14204
14205 Roo.bootstrap.TabPanel = function(config){
14206     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14207     this.addEvents({
14208         /**
14209              * @event changed
14210              * Fires when the active status changes
14211              * @param {Roo.bootstrap.TabPanel} this
14212              * @param {Boolean} state the new state
14213             
14214          */
14215         'changed': true,
14216         /**
14217              * @event beforedeactivate
14218              * Fires before a tab is de-activated - can be used to do validation on a form.
14219              * @param {Roo.bootstrap.TabPanel} this
14220              * @return {Boolean} false if there is an error
14221             
14222          */
14223         'beforedeactivate': true
14224      });
14225     
14226     this.tabId = this.tabId || Roo.id();
14227   
14228 };
14229
14230 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14231     
14232     active: false,
14233     html: false,
14234     tabId: false,
14235     navId : false,
14236     
14237     getAutoCreate : function(){
14238         var cfg = {
14239             tag: 'div',
14240             // item is needed for carousel - not sure if it has any effect otherwise
14241             cls: 'tab-pane item',
14242             html: this.html || ''
14243         };
14244         
14245         if(this.active){
14246             cfg.cls += ' active';
14247         }
14248         
14249         if(this.tabId){
14250             cfg.tabId = this.tabId;
14251         }
14252         
14253         
14254         return cfg;
14255     },
14256     
14257     initEvents:  function()
14258     {
14259         Roo.log('-------- init events on tab panel ---------');
14260         
14261         var p = this.parent();
14262         this.navId = this.navId || p.navId;
14263         
14264         if (typeof(this.navId) != 'undefined') {
14265             // not really needed.. but just in case.. parent should be a NavGroup.
14266             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14267             Roo.log(['register', tg, this]);
14268             tg.register(this);
14269         }
14270     },
14271     
14272     
14273     onRender : function(ct, position)
14274     {
14275        // Roo.log("Call onRender: " + this.xtype);
14276         
14277         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14278         
14279         
14280         
14281         
14282         
14283     },
14284     
14285     setActive: function(state)
14286     {
14287         Roo.log("panel - set active " + this.tabId + "=" + state);
14288         
14289         this.active = state;
14290         if (!state) {
14291             this.el.removeClass('active');
14292             
14293         } else  if (!this.el.hasClass('active')) {
14294             this.el.addClass('active');
14295         }
14296         this.fireEvent('changed', this, state);
14297     }
14298     
14299     
14300 });
14301  
14302
14303  
14304
14305  /*
14306  * - LGPL
14307  *
14308  * DateField
14309  * 
14310  */
14311
14312 /**
14313  * @class Roo.bootstrap.DateField
14314  * @extends Roo.bootstrap.Input
14315  * Bootstrap DateField class
14316  * @cfg {Number} weekStart default 0
14317  * @cfg {Number} weekStart default 0
14318  * @cfg {Number} viewMode default empty, (months|years)
14319  * @cfg {Number} minViewMode default empty, (months|years)
14320  * @cfg {Number} startDate default -Infinity
14321  * @cfg {Number} endDate default Infinity
14322  * @cfg {Boolean} todayHighlight default false
14323  * @cfg {Boolean} todayBtn default false
14324  * @cfg {Boolean} calendarWeeks default false
14325  * @cfg {Object} daysOfWeekDisabled default empty
14326  * 
14327  * @cfg {Boolean} keyboardNavigation default true
14328  * @cfg {String} language default en
14329  * 
14330  * @constructor
14331  * Create a new DateField
14332  * @param {Object} config The config object
14333  */
14334
14335 Roo.bootstrap.DateField = function(config){
14336     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14337      this.addEvents({
14338             /**
14339              * @event show
14340              * Fires when this field show.
14341              * @param {Roo.bootstrap.DateField} this
14342              * @param {Mixed} date The date value
14343              */
14344             show : true,
14345             /**
14346              * @event show
14347              * Fires when this field hide.
14348              * @param {Roo.bootstrap.DateField} this
14349              * @param {Mixed} date The date value
14350              */
14351             hide : true,
14352             /**
14353              * @event select
14354              * Fires when select a date.
14355              * @param {Roo.bootstrap.DateField} this
14356              * @param {Mixed} date The date value
14357              */
14358             select : true
14359         });
14360 };
14361
14362 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14363     
14364     /**
14365      * @cfg {String} format
14366      * The default date format string which can be overriden for localization support.  The format must be
14367      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14368      */
14369     format : "m/d/y",
14370     /**
14371      * @cfg {String} altFormats
14372      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14373      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14374      */
14375     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14376     
14377     weekStart : 0,
14378     
14379     viewMode : '',
14380     
14381     minViewMode : '',
14382     
14383     todayHighlight : false,
14384     
14385     todayBtn: false,
14386     
14387     language: 'en',
14388     
14389     keyboardNavigation: true,
14390     
14391     calendarWeeks: false,
14392     
14393     startDate: -Infinity,
14394     
14395     endDate: Infinity,
14396     
14397     daysOfWeekDisabled: [],
14398     
14399     _events: [],
14400     
14401     UTCDate: function()
14402     {
14403         return new Date(Date.UTC.apply(Date, arguments));
14404     },
14405     
14406     UTCToday: function()
14407     {
14408         var today = new Date();
14409         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14410     },
14411     
14412     getDate: function() {
14413             var d = this.getUTCDate();
14414             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14415     },
14416     
14417     getUTCDate: function() {
14418             return this.date;
14419     },
14420     
14421     setDate: function(d) {
14422             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14423     },
14424     
14425     setUTCDate: function(d) {
14426             this.date = d;
14427             this.setValue(this.formatDate(this.date));
14428     },
14429         
14430     onRender: function(ct, position)
14431     {
14432         
14433         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14434         
14435         this.language = this.language || 'en';
14436         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14437         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14438         
14439         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14440         this.format = this.format || 'm/d/y';
14441         this.isInline = false;
14442         this.isInput = true;
14443         this.component = this.el.select('.add-on', true).first() || false;
14444         this.component = (this.component && this.component.length === 0) ? false : this.component;
14445         this.hasInput = this.component && this.inputEL().length;
14446         
14447         if (typeof(this.minViewMode === 'string')) {
14448             switch (this.minViewMode) {
14449                 case 'months':
14450                     this.minViewMode = 1;
14451                     break;
14452                 case 'years':
14453                     this.minViewMode = 2;
14454                     break;
14455                 default:
14456                     this.minViewMode = 0;
14457                     break;
14458             }
14459         }
14460         
14461         if (typeof(this.viewMode === 'string')) {
14462             switch (this.viewMode) {
14463                 case 'months':
14464                     this.viewMode = 1;
14465                     break;
14466                 case 'years':
14467                     this.viewMode = 2;
14468                     break;
14469                 default:
14470                     this.viewMode = 0;
14471                     break;
14472             }
14473         }
14474                 
14475         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14476         
14477 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14478         
14479         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14480         
14481         this.picker().on('mousedown', this.onMousedown, this);
14482         this.picker().on('click', this.onClick, this);
14483         
14484         this.picker().addClass('datepicker-dropdown');
14485         
14486         this.startViewMode = this.viewMode;
14487         
14488         
14489         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14490             if(!this.calendarWeeks){
14491                 v.remove();
14492                 return;
14493             };
14494             
14495             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14496             v.attr('colspan', function(i, val){
14497                 return parseInt(val) + 1;
14498             });
14499         })
14500                         
14501         
14502         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14503         
14504         this.setStartDate(this.startDate);
14505         this.setEndDate(this.endDate);
14506         
14507         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14508         
14509         this.fillDow();
14510         this.fillMonths();
14511         this.update();
14512         this.showMode();
14513         
14514         if(this.isInline) {
14515             this.show();
14516         }
14517     },
14518     
14519     picker : function()
14520     {
14521         return this.pickerEl;
14522 //        return this.el.select('.datepicker', true).first();
14523     },
14524     
14525     fillDow: function()
14526     {
14527         var dowCnt = this.weekStart;
14528         
14529         var dow = {
14530             tag: 'tr',
14531             cn: [
14532                 
14533             ]
14534         };
14535         
14536         if(this.calendarWeeks){
14537             dow.cn.push({
14538                 tag: 'th',
14539                 cls: 'cw',
14540                 html: '&nbsp;'
14541             })
14542         }
14543         
14544         while (dowCnt < this.weekStart + 7) {
14545             dow.cn.push({
14546                 tag: 'th',
14547                 cls: 'dow',
14548                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14549             });
14550         }
14551         
14552         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14553     },
14554     
14555     fillMonths: function()
14556     {    
14557         var i = 0
14558         var months = this.picker().select('>.datepicker-months td', true).first();
14559         
14560         months.dom.innerHTML = '';
14561         
14562         while (i < 12) {
14563             var month = {
14564                 tag: 'span',
14565                 cls: 'month',
14566                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14567             }
14568             
14569             months.createChild(month);
14570         }
14571         
14572     },
14573     
14574     update: function()
14575     {
14576         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;
14577         
14578         if (this.date < this.startDate) {
14579             this.viewDate = new Date(this.startDate);
14580         } else if (this.date > this.endDate) {
14581             this.viewDate = new Date(this.endDate);
14582         } else {
14583             this.viewDate = new Date(this.date);
14584         }
14585         
14586         this.fill();
14587     },
14588     
14589     fill: function() 
14590     {
14591         var d = new Date(this.viewDate),
14592                 year = d.getUTCFullYear(),
14593                 month = d.getUTCMonth(),
14594                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14595                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14596                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14597                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14598                 currentDate = this.date && this.date.valueOf(),
14599                 today = this.UTCToday();
14600         
14601         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14602         
14603 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14604         
14605 //        this.picker.select('>tfoot th.today').
14606 //                                              .text(dates[this.language].today)
14607 //                                              .toggle(this.todayBtn !== false);
14608     
14609         this.updateNavArrows();
14610         this.fillMonths();
14611                                                 
14612         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14613         
14614         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14615          
14616         prevMonth.setUTCDate(day);
14617         
14618         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14619         
14620         var nextMonth = new Date(prevMonth);
14621         
14622         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14623         
14624         nextMonth = nextMonth.valueOf();
14625         
14626         var fillMonths = false;
14627         
14628         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14629         
14630         while(prevMonth.valueOf() < nextMonth) {
14631             var clsName = '';
14632             
14633             if (prevMonth.getUTCDay() === this.weekStart) {
14634                 if(fillMonths){
14635                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14636                 }
14637                     
14638                 fillMonths = {
14639                     tag: 'tr',
14640                     cn: []
14641                 };
14642                 
14643                 if(this.calendarWeeks){
14644                     // ISO 8601: First week contains first thursday.
14645                     // ISO also states week starts on Monday, but we can be more abstract here.
14646                     var
14647                     // Start of current week: based on weekstart/current date
14648                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14649                     // Thursday of this week
14650                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14651                     // First Thursday of year, year from thursday
14652                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14653                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14654                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14655                     
14656                     fillMonths.cn.push({
14657                         tag: 'td',
14658                         cls: 'cw',
14659                         html: calWeek
14660                     });
14661                 }
14662             }
14663             
14664             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14665                 clsName += ' old';
14666             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14667                 clsName += ' new';
14668             }
14669             if (this.todayHighlight &&
14670                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14671                 prevMonth.getUTCMonth() == today.getMonth() &&
14672                 prevMonth.getUTCDate() == today.getDate()) {
14673                 clsName += ' today';
14674             }
14675             
14676             if (currentDate && prevMonth.valueOf() === currentDate) {
14677                 clsName += ' active';
14678             }
14679             
14680             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14681                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14682                     clsName += ' disabled';
14683             }
14684             
14685             fillMonths.cn.push({
14686                 tag: 'td',
14687                 cls: 'day ' + clsName,
14688                 html: prevMonth.getDate()
14689             })
14690             
14691             prevMonth.setDate(prevMonth.getDate()+1);
14692         }
14693           
14694         var currentYear = this.date && this.date.getUTCFullYear();
14695         var currentMonth = this.date && this.date.getUTCMonth();
14696         
14697         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14698         
14699         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14700             v.removeClass('active');
14701             
14702             if(currentYear === year && k === currentMonth){
14703                 v.addClass('active');
14704             }
14705             
14706             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14707                 v.addClass('disabled');
14708             }
14709             
14710         });
14711         
14712         
14713         year = parseInt(year/10, 10) * 10;
14714         
14715         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14716         
14717         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14718         
14719         year -= 1;
14720         for (var i = -1; i < 11; i++) {
14721             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14722                 tag: 'span',
14723                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14724                 html: year
14725             })
14726             
14727             year += 1;
14728         }
14729     },
14730     
14731     showMode: function(dir) 
14732     {
14733         if (dir) {
14734             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14735         }
14736         Roo.each(this.picker().select('>div',true).elements, function(v){
14737             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14738             v.hide();
14739         });
14740         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14741     },
14742     
14743     place: function()
14744     {
14745         if(this.isInline) return;
14746         
14747         this.picker().removeClass(['bottom', 'top']);
14748         
14749         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14750             /*
14751              * place to the top of element!
14752              *
14753              */
14754             
14755             this.picker().addClass('top');
14756             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14757             
14758             return;
14759         }
14760         
14761         this.picker().addClass('bottom');
14762         
14763         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14764     },
14765     
14766     parseDate : function(value)
14767     {
14768         if(!value || value instanceof Date){
14769             return value;
14770         }
14771         var v = Date.parseDate(value, this.format);
14772         if (!v && this.useIso) {
14773             v = Date.parseDate(value, 'Y-m-d');
14774         }
14775         if(!v && this.altFormats){
14776             if(!this.altFormatsArray){
14777                 this.altFormatsArray = this.altFormats.split("|");
14778             }
14779             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14780                 v = Date.parseDate(value, this.altFormatsArray[i]);
14781             }
14782         }
14783         return v;
14784     },
14785     
14786     formatDate : function(date, fmt)
14787     {
14788         return (!date || !(date instanceof Date)) ?
14789         date : date.dateFormat(fmt || this.format);
14790     },
14791     
14792     onFocus : function()
14793     {
14794         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14795         this.show();
14796     },
14797     
14798     onBlur : function()
14799     {
14800         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14801         
14802         var d = this.inputEl().getValue();
14803         
14804         this.setValue(d);
14805                 
14806         this.hide();
14807     },
14808     
14809     show : function()
14810     {
14811         this.picker().show();
14812         this.update();
14813         this.place();
14814         
14815         this.fireEvent('show', this, this.date);
14816     },
14817     
14818     hide : function()
14819     {
14820         if(this.isInline) return;
14821         this.picker().hide();
14822         this.viewMode = this.startViewMode;
14823         this.showMode();
14824         
14825         this.fireEvent('hide', this, this.date);
14826         
14827     },
14828     
14829     onMousedown: function(e)
14830     {
14831         e.stopPropagation();
14832         e.preventDefault();
14833     },
14834     
14835     keyup: function(e)
14836     {
14837         Roo.bootstrap.DateField.superclass.keyup.call(this);
14838         this.update();
14839     },
14840
14841     setValue: function(v)
14842     {
14843         var d = new Date(v).clearTime();
14844         
14845         if(isNaN(d.getTime())){
14846             this.date = this.viewDate = '';
14847             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14848             return;
14849         }
14850         
14851         v = this.formatDate(d);
14852         
14853         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14854         
14855         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14856      
14857         this.update();
14858
14859         this.fireEvent('select', this, this.date);
14860         
14861     },
14862     
14863     getValue: function()
14864     {
14865         return this.formatDate(this.date);
14866     },
14867     
14868     fireKey: function(e)
14869     {
14870         if (!this.picker().isVisible()){
14871             if (e.keyCode == 27) // allow escape to hide and re-show picker
14872                 this.show();
14873             return;
14874         }
14875         
14876         var dateChanged = false,
14877         dir, day, month,
14878         newDate, newViewDate;
14879         
14880         switch(e.keyCode){
14881             case 27: // escape
14882                 this.hide();
14883                 e.preventDefault();
14884                 break;
14885             case 37: // left
14886             case 39: // right
14887                 if (!this.keyboardNavigation) break;
14888                 dir = e.keyCode == 37 ? -1 : 1;
14889                 
14890                 if (e.ctrlKey){
14891                     newDate = this.moveYear(this.date, dir);
14892                     newViewDate = this.moveYear(this.viewDate, dir);
14893                 } else if (e.shiftKey){
14894                     newDate = this.moveMonth(this.date, dir);
14895                     newViewDate = this.moveMonth(this.viewDate, dir);
14896                 } else {
14897                     newDate = new Date(this.date);
14898                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14899                     newViewDate = new Date(this.viewDate);
14900                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14901                 }
14902                 if (this.dateWithinRange(newDate)){
14903                     this.date = newDate;
14904                     this.viewDate = newViewDate;
14905                     this.setValue(this.formatDate(this.date));
14906 //                    this.update();
14907                     e.preventDefault();
14908                     dateChanged = true;
14909                 }
14910                 break;
14911             case 38: // up
14912             case 40: // down
14913                 if (!this.keyboardNavigation) break;
14914                 dir = e.keyCode == 38 ? -1 : 1;
14915                 if (e.ctrlKey){
14916                     newDate = this.moveYear(this.date, dir);
14917                     newViewDate = this.moveYear(this.viewDate, dir);
14918                 } else if (e.shiftKey){
14919                     newDate = this.moveMonth(this.date, dir);
14920                     newViewDate = this.moveMonth(this.viewDate, dir);
14921                 } else {
14922                     newDate = new Date(this.date);
14923                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14924                     newViewDate = new Date(this.viewDate);
14925                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14926                 }
14927                 if (this.dateWithinRange(newDate)){
14928                     this.date = newDate;
14929                     this.viewDate = newViewDate;
14930                     this.setValue(this.formatDate(this.date));
14931 //                    this.update();
14932                     e.preventDefault();
14933                     dateChanged = true;
14934                 }
14935                 break;
14936             case 13: // enter
14937                 this.setValue(this.formatDate(this.date));
14938                 this.hide();
14939                 e.preventDefault();
14940                 break;
14941             case 9: // tab
14942                 this.setValue(this.formatDate(this.date));
14943                 this.hide();
14944                 break;
14945             case 16: // shift
14946             case 17: // ctrl
14947             case 18: // alt
14948                 break;
14949             default :
14950                 this.hide();
14951                 
14952         }
14953     },
14954     
14955     
14956     onClick: function(e) 
14957     {
14958         e.stopPropagation();
14959         e.preventDefault();
14960         
14961         var target = e.getTarget();
14962         
14963         if(target.nodeName.toLowerCase() === 'i'){
14964             target = Roo.get(target).dom.parentNode;
14965         }
14966         
14967         var nodeName = target.nodeName;
14968         var className = target.className;
14969         var html = target.innerHTML;
14970         
14971         switch(nodeName.toLowerCase()) {
14972             case 'th':
14973                 switch(className) {
14974                     case 'switch':
14975                         this.showMode(1);
14976                         break;
14977                     case 'prev':
14978                     case 'next':
14979                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14980                         switch(this.viewMode){
14981                                 case 0:
14982                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14983                                         break;
14984                                 case 1:
14985                                 case 2:
14986                                         this.viewDate = this.moveYear(this.viewDate, dir);
14987                                         break;
14988                         }
14989                         this.fill();
14990                         break;
14991                     case 'today':
14992                         var date = new Date();
14993                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14994 //                        this.fill()
14995                         this.setValue(this.formatDate(this.date));
14996                         
14997                         this.hide();
14998                         break;
14999                 }
15000                 break;
15001             case 'span':
15002                 if (className.indexOf('disabled') === -1) {
15003                     this.viewDate.setUTCDate(1);
15004                     if (className.indexOf('month') !== -1) {
15005                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15006                     } else {
15007                         var year = parseInt(html, 10) || 0;
15008                         this.viewDate.setUTCFullYear(year);
15009                         
15010                     }
15011                     this.showMode(-1);
15012                     this.fill();
15013                 }
15014                 break;
15015                 
15016             case 'td':
15017                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15018                     var day = parseInt(html, 10) || 1;
15019                     var year = this.viewDate.getUTCFullYear(),
15020                         month = this.viewDate.getUTCMonth();
15021
15022                     if (className.indexOf('old') !== -1) {
15023                         if(month === 0 ){
15024                             month = 11;
15025                             year -= 1;
15026                         }else{
15027                             month -= 1;
15028                         }
15029                     } else if (className.indexOf('new') !== -1) {
15030                         if (month == 11) {
15031                             month = 0;
15032                             year += 1;
15033                         } else {
15034                             month += 1;
15035                         }
15036                     }
15037                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15038                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15039 //                    this.fill();
15040                     this.setValue(this.formatDate(this.date));
15041                     this.hide();
15042                 }
15043                 break;
15044         }
15045     },
15046     
15047     setStartDate: function(startDate)
15048     {
15049         this.startDate = startDate || -Infinity;
15050         if (this.startDate !== -Infinity) {
15051             this.startDate = this.parseDate(this.startDate);
15052         }
15053         this.update();
15054         this.updateNavArrows();
15055     },
15056
15057     setEndDate: function(endDate)
15058     {
15059         this.endDate = endDate || Infinity;
15060         if (this.endDate !== Infinity) {
15061             this.endDate = this.parseDate(this.endDate);
15062         }
15063         this.update();
15064         this.updateNavArrows();
15065     },
15066     
15067     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15068     {
15069         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15070         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15071             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15072         }
15073         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15074             return parseInt(d, 10);
15075         });
15076         this.update();
15077         this.updateNavArrows();
15078     },
15079     
15080     updateNavArrows: function() 
15081     {
15082         var d = new Date(this.viewDate),
15083         year = d.getUTCFullYear(),
15084         month = d.getUTCMonth();
15085         
15086         Roo.each(this.picker().select('.prev', true).elements, function(v){
15087             v.show();
15088             switch (this.viewMode) {
15089                 case 0:
15090
15091                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15092                         v.hide();
15093                     }
15094                     break;
15095                 case 1:
15096                 case 2:
15097                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15098                         v.hide();
15099                     }
15100                     break;
15101             }
15102         });
15103         
15104         Roo.each(this.picker().select('.next', true).elements, function(v){
15105             v.show();
15106             switch (this.viewMode) {
15107                 case 0:
15108
15109                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15110                         v.hide();
15111                     }
15112                     break;
15113                 case 1:
15114                 case 2:
15115                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15116                         v.hide();
15117                     }
15118                     break;
15119             }
15120         })
15121     },
15122     
15123     moveMonth: function(date, dir)
15124     {
15125         if (!dir) return date;
15126         var new_date = new Date(date.valueOf()),
15127         day = new_date.getUTCDate(),
15128         month = new_date.getUTCMonth(),
15129         mag = Math.abs(dir),
15130         new_month, test;
15131         dir = dir > 0 ? 1 : -1;
15132         if (mag == 1){
15133             test = dir == -1
15134             // If going back one month, make sure month is not current month
15135             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15136             ? function(){
15137                 return new_date.getUTCMonth() == month;
15138             }
15139             // If going forward one month, make sure month is as expected
15140             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15141             : function(){
15142                 return new_date.getUTCMonth() != new_month;
15143             };
15144             new_month = month + dir;
15145             new_date.setUTCMonth(new_month);
15146             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15147             if (new_month < 0 || new_month > 11)
15148                 new_month = (new_month + 12) % 12;
15149         } else {
15150             // For magnitudes >1, move one month at a time...
15151             for (var i=0; i<mag; i++)
15152                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15153                 new_date = this.moveMonth(new_date, dir);
15154             // ...then reset the day, keeping it in the new month
15155             new_month = new_date.getUTCMonth();
15156             new_date.setUTCDate(day);
15157             test = function(){
15158                 return new_month != new_date.getUTCMonth();
15159             };
15160         }
15161         // Common date-resetting loop -- if date is beyond end of month, make it
15162         // end of month
15163         while (test()){
15164             new_date.setUTCDate(--day);
15165             new_date.setUTCMonth(new_month);
15166         }
15167         return new_date;
15168     },
15169
15170     moveYear: function(date, dir)
15171     {
15172         return this.moveMonth(date, dir*12);
15173     },
15174
15175     dateWithinRange: function(date)
15176     {
15177         return date >= this.startDate && date <= this.endDate;
15178     },
15179
15180     
15181     remove: function() 
15182     {
15183         this.picker().remove();
15184     }
15185    
15186 });
15187
15188 Roo.apply(Roo.bootstrap.DateField,  {
15189     
15190     head : {
15191         tag: 'thead',
15192         cn: [
15193         {
15194             tag: 'tr',
15195             cn: [
15196             {
15197                 tag: 'th',
15198                 cls: 'prev',
15199                 html: '<i class="fa fa-arrow-left"/>'
15200             },
15201             {
15202                 tag: 'th',
15203                 cls: 'switch',
15204                 colspan: '5'
15205             },
15206             {
15207                 tag: 'th',
15208                 cls: 'next',
15209                 html: '<i class="fa fa-arrow-right"/>'
15210             }
15211
15212             ]
15213         }
15214         ]
15215     },
15216     
15217     content : {
15218         tag: 'tbody',
15219         cn: [
15220         {
15221             tag: 'tr',
15222             cn: [
15223             {
15224                 tag: 'td',
15225                 colspan: '7'
15226             }
15227             ]
15228         }
15229         ]
15230     },
15231     
15232     footer : {
15233         tag: 'tfoot',
15234         cn: [
15235         {
15236             tag: 'tr',
15237             cn: [
15238             {
15239                 tag: 'th',
15240                 colspan: '7',
15241                 cls: 'today'
15242             }
15243                     
15244             ]
15245         }
15246         ]
15247     },
15248     
15249     dates:{
15250         en: {
15251             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15252             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15253             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15254             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15255             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15256             today: "Today"
15257         }
15258     },
15259     
15260     modes: [
15261     {
15262         clsName: 'days',
15263         navFnc: 'Month',
15264         navStep: 1
15265     },
15266     {
15267         clsName: 'months',
15268         navFnc: 'FullYear',
15269         navStep: 1
15270     },
15271     {
15272         clsName: 'years',
15273         navFnc: 'FullYear',
15274         navStep: 10
15275     }]
15276 });
15277
15278 Roo.apply(Roo.bootstrap.DateField,  {
15279   
15280     template : {
15281         tag: 'div',
15282         cls: 'datepicker dropdown-menu',
15283         cn: [
15284         {
15285             tag: 'div',
15286             cls: 'datepicker-days',
15287             cn: [
15288             {
15289                 tag: 'table',
15290                 cls: 'table-condensed',
15291                 cn:[
15292                 Roo.bootstrap.DateField.head,
15293                 {
15294                     tag: 'tbody'
15295                 },
15296                 Roo.bootstrap.DateField.footer
15297                 ]
15298             }
15299             ]
15300         },
15301         {
15302             tag: 'div',
15303             cls: 'datepicker-months',
15304             cn: [
15305             {
15306                 tag: 'table',
15307                 cls: 'table-condensed',
15308                 cn:[
15309                 Roo.bootstrap.DateField.head,
15310                 Roo.bootstrap.DateField.content,
15311                 Roo.bootstrap.DateField.footer
15312                 ]
15313             }
15314             ]
15315         },
15316         {
15317             tag: 'div',
15318             cls: 'datepicker-years',
15319             cn: [
15320             {
15321                 tag: 'table',
15322                 cls: 'table-condensed',
15323                 cn:[
15324                 Roo.bootstrap.DateField.head,
15325                 Roo.bootstrap.DateField.content,
15326                 Roo.bootstrap.DateField.footer
15327                 ]
15328             }
15329             ]
15330         }
15331         ]
15332     }
15333 });
15334
15335  
15336
15337  /*
15338  * - LGPL
15339  *
15340  * TimeField
15341  * 
15342  */
15343
15344 /**
15345  * @class Roo.bootstrap.TimeField
15346  * @extends Roo.bootstrap.Input
15347  * Bootstrap DateField class
15348  * 
15349  * 
15350  * @constructor
15351  * Create a new TimeField
15352  * @param {Object} config The config object
15353  */
15354
15355 Roo.bootstrap.TimeField = function(config){
15356     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15357     this.addEvents({
15358             /**
15359              * @event show
15360              * Fires when this field show.
15361              * @param {Roo.bootstrap.DateField} this
15362              * @param {Mixed} date The date value
15363              */
15364             show : true,
15365             /**
15366              * @event show
15367              * Fires when this field hide.
15368              * @param {Roo.bootstrap.DateField} this
15369              * @param {Mixed} date The date value
15370              */
15371             hide : true,
15372             /**
15373              * @event select
15374              * Fires when select a date.
15375              * @param {Roo.bootstrap.DateField} this
15376              * @param {Mixed} date The date value
15377              */
15378             select : true
15379         });
15380 };
15381
15382 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15383     
15384     /**
15385      * @cfg {String} format
15386      * The default time format string which can be overriden for localization support.  The format must be
15387      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15388      */
15389     format : "H:i",
15390        
15391     onRender: function(ct, position)
15392     {
15393         
15394         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15395                 
15396         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15397         
15398         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15399         
15400         this.pop = this.picker().select('>.datepicker-time',true).first();
15401         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15402         
15403         this.picker().on('mousedown', this.onMousedown, this);
15404         this.picker().on('click', this.onClick, this);
15405         
15406         this.picker().addClass('datepicker-dropdown');
15407     
15408         this.fillTime();
15409         this.update();
15410             
15411         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15412         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15413         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15414         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15415         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15416         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15417
15418     },
15419     
15420     fireKey: function(e){
15421         if (!this.picker().isVisible()){
15422             if (e.keyCode == 27) // allow escape to hide and re-show picker
15423                 this.show();
15424             return;
15425         }
15426
15427         e.preventDefault();
15428         
15429         switch(e.keyCode){
15430             case 27: // escape
15431                 this.hide();
15432                 break;
15433             case 37: // left
15434             case 39: // right
15435                 this.onTogglePeriod();
15436                 break;
15437             case 38: // up
15438                 this.onIncrementMinutes();
15439                 break;
15440             case 40: // down
15441                 this.onDecrementMinutes();
15442                 break;
15443             case 13: // enter
15444             case 9: // tab
15445                 this.setTime();
15446                 break;
15447         }
15448     },
15449     
15450     onClick: function(e) {
15451         e.stopPropagation();
15452         e.preventDefault();
15453     },
15454     
15455     picker : function()
15456     {
15457         return this.el.select('.datepicker', true).first();
15458     },
15459     
15460     fillTime: function()
15461     {    
15462         var time = this.pop.select('tbody', true).first();
15463         
15464         time.dom.innerHTML = '';
15465         
15466         time.createChild({
15467             tag: 'tr',
15468             cn: [
15469                 {
15470                     tag: 'td',
15471                     cn: [
15472                         {
15473                             tag: 'a',
15474                             href: '#',
15475                             cls: 'btn',
15476                             cn: [
15477                                 {
15478                                     tag: 'span',
15479                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15480                                 }
15481                             ]
15482                         } 
15483                     ]
15484                 },
15485                 {
15486                     tag: 'td',
15487                     cls: 'separator'
15488                 },
15489                 {
15490                     tag: 'td',
15491                     cn: [
15492                         {
15493                             tag: 'a',
15494                             href: '#',
15495                             cls: 'btn',
15496                             cn: [
15497                                 {
15498                                     tag: 'span',
15499                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15500                                 }
15501                             ]
15502                         }
15503                     ]
15504                 },
15505                 {
15506                     tag: 'td',
15507                     cls: 'separator'
15508                 }
15509             ]
15510         });
15511         
15512         time.createChild({
15513             tag: 'tr',
15514             cn: [
15515                 {
15516                     tag: 'td',
15517                     cn: [
15518                         {
15519                             tag: 'span',
15520                             cls: 'timepicker-hour',
15521                             html: '00'
15522                         }  
15523                     ]
15524                 },
15525                 {
15526                     tag: 'td',
15527                     cls: 'separator',
15528                     html: ':'
15529                 },
15530                 {
15531                     tag: 'td',
15532                     cn: [
15533                         {
15534                             tag: 'span',
15535                             cls: 'timepicker-minute',
15536                             html: '00'
15537                         }  
15538                     ]
15539                 },
15540                 {
15541                     tag: 'td',
15542                     cls: 'separator'
15543                 },
15544                 {
15545                     tag: 'td',
15546                     cn: [
15547                         {
15548                             tag: 'button',
15549                             type: 'button',
15550                             cls: 'btn btn-primary period',
15551                             html: 'AM'
15552                             
15553                         }
15554                     ]
15555                 }
15556             ]
15557         });
15558         
15559         time.createChild({
15560             tag: 'tr',
15561             cn: [
15562                 {
15563                     tag: 'td',
15564                     cn: [
15565                         {
15566                             tag: 'a',
15567                             href: '#',
15568                             cls: 'btn',
15569                             cn: [
15570                                 {
15571                                     tag: 'span',
15572                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15573                                 }
15574                             ]
15575                         }
15576                     ]
15577                 },
15578                 {
15579                     tag: 'td',
15580                     cls: 'separator'
15581                 },
15582                 {
15583                     tag: 'td',
15584                     cn: [
15585                         {
15586                             tag: 'a',
15587                             href: '#',
15588                             cls: 'btn',
15589                             cn: [
15590                                 {
15591                                     tag: 'span',
15592                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15593                                 }
15594                             ]
15595                         }
15596                     ]
15597                 },
15598                 {
15599                     tag: 'td',
15600                     cls: 'separator'
15601                 }
15602             ]
15603         });
15604         
15605     },
15606     
15607     update: function()
15608     {
15609         
15610         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15611         
15612         this.fill();
15613     },
15614     
15615     fill: function() 
15616     {
15617         var hours = this.time.getHours();
15618         var minutes = this.time.getMinutes();
15619         var period = 'AM';
15620         
15621         if(hours > 11){
15622             period = 'PM';
15623         }
15624         
15625         if(hours == 0){
15626             hours = 12;
15627         }
15628         
15629         
15630         if(hours > 12){
15631             hours = hours - 12;
15632         }
15633         
15634         if(hours < 10){
15635             hours = '0' + hours;
15636         }
15637         
15638         if(minutes < 10){
15639             minutes = '0' + minutes;
15640         }
15641         
15642         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15643         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15644         this.pop.select('button', true).first().dom.innerHTML = period;
15645         
15646     },
15647     
15648     place: function()
15649     {   
15650         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15651         
15652         var cls = ['bottom'];
15653         
15654         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15655             cls.pop();
15656             cls.push('top');
15657         }
15658         
15659         cls.push('right');
15660         
15661         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15662             cls.pop();
15663             cls.push('left');
15664         }
15665         
15666         this.picker().addClass(cls.join('-'));
15667         
15668         var _this = this;
15669         
15670         Roo.each(cls, function(c){
15671             if(c == 'bottom'){
15672                 _this.picker().setTop(_this.inputEl().getHeight());
15673                 return;
15674             }
15675             if(c == 'top'){
15676                 _this.picker().setTop(0 - _this.picker().getHeight());
15677                 return;
15678             }
15679             
15680             if(c == 'left'){
15681                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15682                 return;
15683             }
15684             if(c == 'right'){
15685                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15686                 return;
15687             }
15688         });
15689         
15690     },
15691   
15692     onFocus : function()
15693     {
15694         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15695         this.show();
15696     },
15697     
15698     onBlur : function()
15699     {
15700         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15701         this.hide();
15702     },
15703     
15704     show : function()
15705     {
15706         this.picker().show();
15707         this.pop.show();
15708         this.update();
15709         this.place();
15710         
15711         this.fireEvent('show', this, this.date);
15712     },
15713     
15714     hide : function()
15715     {
15716         this.picker().hide();
15717         this.pop.hide();
15718         
15719         this.fireEvent('hide', this, this.date);
15720     },
15721     
15722     setTime : function()
15723     {
15724         this.hide();
15725         this.setValue(this.time.format(this.format));
15726         
15727         this.fireEvent('select', this, this.date);
15728         
15729         
15730     },
15731     
15732     onMousedown: function(e){
15733         e.stopPropagation();
15734         e.preventDefault();
15735     },
15736     
15737     onIncrementHours: function()
15738     {
15739         Roo.log('onIncrementHours');
15740         this.time = this.time.add(Date.HOUR, 1);
15741         this.update();
15742         
15743     },
15744     
15745     onDecrementHours: function()
15746     {
15747         Roo.log('onDecrementHours');
15748         this.time = this.time.add(Date.HOUR, -1);
15749         this.update();
15750     },
15751     
15752     onIncrementMinutes: function()
15753     {
15754         Roo.log('onIncrementMinutes');
15755         this.time = this.time.add(Date.MINUTE, 1);
15756         this.update();
15757     },
15758     
15759     onDecrementMinutes: function()
15760     {
15761         Roo.log('onDecrementMinutes');
15762         this.time = this.time.add(Date.MINUTE, -1);
15763         this.update();
15764     },
15765     
15766     onTogglePeriod: function()
15767     {
15768         Roo.log('onTogglePeriod');
15769         this.time = this.time.add(Date.HOUR, 12);
15770         this.update();
15771     }
15772     
15773    
15774 });
15775
15776 Roo.apply(Roo.bootstrap.TimeField,  {
15777     
15778     content : {
15779         tag: 'tbody',
15780         cn: [
15781             {
15782                 tag: 'tr',
15783                 cn: [
15784                 {
15785                     tag: 'td',
15786                     colspan: '7'
15787                 }
15788                 ]
15789             }
15790         ]
15791     },
15792     
15793     footer : {
15794         tag: 'tfoot',
15795         cn: [
15796             {
15797                 tag: 'tr',
15798                 cn: [
15799                 {
15800                     tag: 'th',
15801                     colspan: '7',
15802                     cls: '',
15803                     cn: [
15804                         {
15805                             tag: 'button',
15806                             cls: 'btn btn-info ok',
15807                             html: 'OK'
15808                         }
15809                     ]
15810                 }
15811
15812                 ]
15813             }
15814         ]
15815     }
15816 });
15817
15818 Roo.apply(Roo.bootstrap.TimeField,  {
15819   
15820     template : {
15821         tag: 'div',
15822         cls: 'datepicker dropdown-menu',
15823         cn: [
15824             {
15825                 tag: 'div',
15826                 cls: 'datepicker-time',
15827                 cn: [
15828                 {
15829                     tag: 'table',
15830                     cls: 'table-condensed',
15831                     cn:[
15832                     Roo.bootstrap.TimeField.content,
15833                     Roo.bootstrap.TimeField.footer
15834                     ]
15835                 }
15836                 ]
15837             }
15838         ]
15839     }
15840 });
15841
15842  
15843
15844  /*
15845  * - LGPL
15846  *
15847  * CheckBox
15848  * 
15849  */
15850
15851 /**
15852  * @class Roo.bootstrap.CheckBox
15853  * @extends Roo.bootstrap.Input
15854  * Bootstrap CheckBox class
15855  * 
15856  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15857  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15858  * @cfg {String} boxLabel The text that appears beside the checkbox
15859  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15860  * @cfg {Boolean} checked initnal the element
15861  * 
15862  * 
15863  * @constructor
15864  * Create a new CheckBox
15865  * @param {Object} config The config object
15866  */
15867
15868 Roo.bootstrap.CheckBox = function(config){
15869     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15870    
15871         this.addEvents({
15872             /**
15873             * @event check
15874             * Fires when the element is checked or unchecked.
15875             * @param {Roo.bootstrap.CheckBox} this This input
15876             * @param {Boolean} checked The new checked value
15877             */
15878            check : true
15879         });
15880 };
15881
15882 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15883     
15884     inputType: 'checkbox',
15885     inputValue: 1,
15886     valueOff: 0,
15887     boxLabel: false,
15888     checked: false,
15889     weight : false,
15890     
15891     getAutoCreate : function()
15892     {
15893         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15894         
15895         var id = Roo.id();
15896         
15897         var cfg = {};
15898         
15899         cfg.cls = 'form-group checkbox' //input-group
15900         
15901         
15902         
15903         
15904         var input =  {
15905             tag: 'input',
15906             id : id,
15907             type : this.inputType,
15908             value : (!this.checked) ? this.valueOff : this.inputValue,
15909             cls : 'roo-checkbox', //'form-box',
15910             placeholder : this.placeholder || ''
15911             
15912         };
15913         
15914         if (this.weight) { // Validity check?
15915             cfg.cls += " checkbox-" + this.weight;
15916         }
15917         
15918         if (this.disabled) {
15919             input.disabled=true;
15920         }
15921         
15922         if(this.checked){
15923             input.checked = this.checked;
15924         }
15925         
15926         if (this.name) {
15927             input.name = this.name;
15928         }
15929         
15930         if (this.size) {
15931             input.cls += ' input-' + this.size;
15932         }
15933         
15934         var settings=this;
15935         ['xs','sm','md','lg'].map(function(size){
15936             if (settings[size]) {
15937                 cfg.cls += ' col-' + size + '-' + settings[size];
15938             }
15939         });
15940         
15941        
15942         
15943         var inputblock = input;
15944         
15945         
15946         
15947         
15948         if (this.before || this.after) {
15949             
15950             inputblock = {
15951                 cls : 'input-group',
15952                 cn :  [] 
15953             };
15954             if (this.before) {
15955                 inputblock.cn.push({
15956                     tag :'span',
15957                     cls : 'input-group-addon',
15958                     html : this.before
15959                 });
15960             }
15961             inputblock.cn.push(input);
15962             if (this.after) {
15963                 inputblock.cn.push({
15964                     tag :'span',
15965                     cls : 'input-group-addon',
15966                     html : this.after
15967                 });
15968             }
15969             
15970         };
15971         
15972         if (align ==='left' && this.fieldLabel.length) {
15973                 Roo.log("left and has label");
15974                 cfg.cn = [
15975                     
15976                     {
15977                         tag: 'label',
15978                         'for' :  id,
15979                         cls : 'control-label col-md-' + this.labelWidth,
15980                         html : this.fieldLabel
15981                         
15982                     },
15983                     {
15984                         cls : "col-md-" + (12 - this.labelWidth), 
15985                         cn: [
15986                             inputblock
15987                         ]
15988                     }
15989                     
15990                 ];
15991         } else if ( this.fieldLabel.length) {
15992                 Roo.log(" label");
15993                 cfg.cn = [
15994                    
15995                     {
15996                         tag: this.boxLabel ? 'span' : 'label',
15997                         'for': id,
15998                         cls: 'control-label box-input-label',
15999                         //cls : 'input-group-addon',
16000                         html : this.fieldLabel
16001                         
16002                     },
16003                     
16004                     inputblock
16005                     
16006                 ];
16007
16008         } else {
16009             
16010                 Roo.log(" no label && no align");
16011                 cfg.cn = [  inputblock ] ;
16012                 
16013                 
16014         };
16015          if(this.boxLabel){
16016             cfg.cn.push( {
16017                 tag: 'label',
16018                 'for': id,
16019                 cls: 'box-label',
16020                 html: this.boxLabel
16021                 
16022             });
16023         }
16024         
16025         
16026        
16027         return cfg;
16028         
16029     },
16030     
16031     /**
16032      * return the real input element.
16033      */
16034     inputEl: function ()
16035     {
16036         return this.el.select('input.roo-checkbox',true).first();
16037     },
16038     
16039     label: function()
16040     {
16041         return this.el.select('label.control-label',true).first();
16042     },
16043     
16044     initEvents : function()
16045     {
16046 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16047         
16048         this.inputEl().on('click', this.onClick,  this);
16049         
16050     },
16051     
16052     onClick : function()
16053     {   
16054         this.setChecked(!this.checked);
16055     },
16056     
16057     setChecked : function(state,suppressEvent)
16058     {
16059         this.checked = state;
16060         
16061         this.inputEl().dom.checked = state;
16062         
16063         if(suppressEvent !== true){
16064             this.fireEvent('check', this, state);
16065         }
16066         
16067         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16068         
16069     },
16070     
16071     setValue : function(v,suppressEvent)
16072     {
16073         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16074     }
16075     
16076 });
16077
16078  
16079 /*
16080  * - LGPL
16081  *
16082  * Radio
16083  * 
16084  */
16085
16086 /**
16087  * @class Roo.bootstrap.Radio
16088  * @extends Roo.bootstrap.CheckBox
16089  * Bootstrap Radio class
16090
16091  * @constructor
16092  * Create a new Radio
16093  * @param {Object} config The config object
16094  */
16095
16096 Roo.bootstrap.Radio = function(config){
16097     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16098    
16099 };
16100
16101 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16102     
16103     inputType: 'radio',
16104     inputValue: '',
16105     valueOff: '',
16106     
16107     getAutoCreate : function()
16108     {
16109         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16110         
16111         var id = Roo.id();
16112         
16113         var cfg = {};
16114         
16115         cfg.cls = 'form-group radio' //input-group
16116         
16117         var input =  {
16118             tag: 'input',
16119             id : id,
16120             type : this.inputType,
16121             value : (!this.checked) ? this.valueOff : this.inputValue,
16122             cls : 'roo-radio',
16123             placeholder : this.placeholder || ''
16124             
16125         };
16126           if (this.weight) { // Validity check?
16127             cfg.cls += " radio-" + this.weight;
16128         }
16129         if (this.disabled) {
16130             input.disabled=true;
16131         }
16132         
16133         if(this.checked){
16134             input.checked = this.checked;
16135         }
16136         
16137         if (this.name) {
16138             input.name = this.name;
16139         }
16140         
16141         if (this.size) {
16142             input.cls += ' input-' + this.size;
16143         }
16144         
16145         var settings=this;
16146         ['xs','sm','md','lg'].map(function(size){
16147             if (settings[size]) {
16148                 cfg.cls += ' col-' + size + '-' + settings[size];
16149             }
16150         });
16151         
16152         var inputblock = input;
16153         
16154         if (this.before || this.after) {
16155             
16156             inputblock = {
16157                 cls : 'input-group',
16158                 cn :  [] 
16159             };
16160             if (this.before) {
16161                 inputblock.cn.push({
16162                     tag :'span',
16163                     cls : 'input-group-addon',
16164                     html : this.before
16165                 });
16166             }
16167             inputblock.cn.push(input);
16168             if (this.after) {
16169                 inputblock.cn.push({
16170                     tag :'span',
16171                     cls : 'input-group-addon',
16172                     html : this.after
16173                 });
16174             }
16175             
16176         };
16177         
16178         if (align ==='left' && this.fieldLabel.length) {
16179                 Roo.log("left and has label");
16180                 cfg.cn = [
16181                     
16182                     {
16183                         tag: 'label',
16184                         'for' :  id,
16185                         cls : 'control-label col-md-' + this.labelWidth,
16186                         html : this.fieldLabel
16187                         
16188                     },
16189                     {
16190                         cls : "col-md-" + (12 - this.labelWidth), 
16191                         cn: [
16192                             inputblock
16193                         ]
16194                     }
16195                     
16196                 ];
16197         } else if ( this.fieldLabel.length) {
16198                 Roo.log(" label");
16199                  cfg.cn = [
16200                    
16201                     {
16202                         tag: 'label',
16203                         'for': id,
16204                         cls: 'control-label box-input-label',
16205                         //cls : 'input-group-addon',
16206                         html : this.fieldLabel
16207                         
16208                     },
16209                     
16210                     inputblock
16211                     
16212                 ];
16213
16214         } else {
16215             
16216                    Roo.log(" no label && no align");
16217                 cfg.cn = [
16218                     
16219                         inputblock
16220                     
16221                 ];
16222                 
16223                 
16224         };
16225         
16226         if(this.boxLabel){
16227             cfg.cn.push({
16228                 tag: 'label',
16229                 'for': id,
16230                 cls: 'box-label',
16231                 html: this.boxLabel
16232             })
16233         }
16234         
16235         return cfg;
16236         
16237     },
16238     inputEl: function ()
16239     {
16240         return this.el.select('input.roo-radio',true).first();
16241     },
16242     onClick : function()
16243     {   
16244         this.setChecked(true);
16245     },
16246     
16247     setChecked : function(state,suppressEvent)
16248     {
16249         if(state){
16250             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16251                 v.dom.checked = false;
16252             });
16253         }
16254         
16255         this.checked = state;
16256         this.inputEl().dom.checked = state;
16257         
16258         if(suppressEvent !== true){
16259             this.fireEvent('check', this, state);
16260         }
16261         
16262         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16263         
16264     },
16265     
16266     getGroupValue : function()
16267     {
16268         var value = ''
16269         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16270             if(v.dom.checked == true){
16271                 value = v.dom.value;
16272             }
16273         });
16274         
16275         return value;
16276     },
16277     
16278     /**
16279      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16280      * @return {Mixed} value The field value
16281      */
16282     getValue : function(){
16283         return this.getGroupValue();
16284     }
16285     
16286 });
16287
16288  
16289 //<script type="text/javascript">
16290
16291 /*
16292  * Based  Ext JS Library 1.1.1
16293  * Copyright(c) 2006-2007, Ext JS, LLC.
16294  * LGPL
16295  *
16296  */
16297  
16298 /**
16299  * @class Roo.HtmlEditorCore
16300  * @extends Roo.Component
16301  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16302  *
16303  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16304  */
16305
16306 Roo.HtmlEditorCore = function(config){
16307     
16308     
16309     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16310     
16311     
16312     this.addEvents({
16313         /**
16314          * @event initialize
16315          * Fires when the editor is fully initialized (including the iframe)
16316          * @param {Roo.HtmlEditorCore} this
16317          */
16318         initialize: true,
16319         /**
16320          * @event activate
16321          * Fires when the editor is first receives the focus. Any insertion must wait
16322          * until after this event.
16323          * @param {Roo.HtmlEditorCore} this
16324          */
16325         activate: true,
16326          /**
16327          * @event beforesync
16328          * Fires before the textarea is updated with content from the editor iframe. Return false
16329          * to cancel the sync.
16330          * @param {Roo.HtmlEditorCore} this
16331          * @param {String} html
16332          */
16333         beforesync: true,
16334          /**
16335          * @event beforepush
16336          * Fires before the iframe editor is updated with content from the textarea. Return false
16337          * to cancel the push.
16338          * @param {Roo.HtmlEditorCore} this
16339          * @param {String} html
16340          */
16341         beforepush: true,
16342          /**
16343          * @event sync
16344          * Fires when the textarea is updated with content from the editor iframe.
16345          * @param {Roo.HtmlEditorCore} this
16346          * @param {String} html
16347          */
16348         sync: true,
16349          /**
16350          * @event push
16351          * Fires when the iframe editor is updated with content from the textarea.
16352          * @param {Roo.HtmlEditorCore} this
16353          * @param {String} html
16354          */
16355         push: true,
16356         
16357         /**
16358          * @event editorevent
16359          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16360          * @param {Roo.HtmlEditorCore} this
16361          */
16362         editorevent: true
16363     });
16364     
16365     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16366     
16367     // defaults : white / black...
16368     this.applyBlacklists();
16369     
16370     
16371     
16372 };
16373
16374
16375 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16376
16377
16378      /**
16379      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16380      */
16381     
16382     owner : false,
16383     
16384      /**
16385      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16386      *                        Roo.resizable.
16387      */
16388     resizable : false,
16389      /**
16390      * @cfg {Number} height (in pixels)
16391      */   
16392     height: 300,
16393    /**
16394      * @cfg {Number} width (in pixels)
16395      */   
16396     width: 500,
16397     
16398     /**
16399      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16400      * 
16401      */
16402     stylesheets: false,
16403     
16404     // id of frame..
16405     frameId: false,
16406     
16407     // private properties
16408     validationEvent : false,
16409     deferHeight: true,
16410     initialized : false,
16411     activated : false,
16412     sourceEditMode : false,
16413     onFocus : Roo.emptyFn,
16414     iframePad:3,
16415     hideMode:'offsets',
16416     
16417     clearUp: true,
16418     
16419     // blacklist + whitelisted elements..
16420     black: false,
16421     white: false,
16422      
16423     
16424
16425     /**
16426      * Protected method that will not generally be called directly. It
16427      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16428      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16429      */
16430     getDocMarkup : function(){
16431         // body styles..
16432         var st = '';
16433         Roo.log(this.stylesheets);
16434         
16435         // inherit styels from page...?? 
16436         if (this.stylesheets === false) {
16437             
16438             Roo.get(document.head).select('style').each(function(node) {
16439                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16440             });
16441             
16442             Roo.get(document.head).select('link').each(function(node) { 
16443                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16444             });
16445             
16446         } else if (!this.stylesheets.length) {
16447                 // simple..
16448                 st = '<style type="text/css">' +
16449                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16450                    '</style>';
16451         } else {
16452             Roo.each(this.stylesheets, function(s) {
16453                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16454             });
16455             
16456         }
16457         
16458         st +=  '<style type="text/css">' +
16459             'IMG { cursor: pointer } ' +
16460         '</style>';
16461
16462         
16463         return '<html><head>' + st  +
16464             //<style type="text/css">' +
16465             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16466             //'</style>' +
16467             ' </head><body class="roo-htmleditor-body"></body></html>';
16468     },
16469
16470     // private
16471     onRender : function(ct, position)
16472     {
16473         var _t = this;
16474         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16475         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16476         
16477         
16478         this.el.dom.style.border = '0 none';
16479         this.el.dom.setAttribute('tabIndex', -1);
16480         this.el.addClass('x-hidden hide');
16481         
16482         
16483         
16484         if(Roo.isIE){ // fix IE 1px bogus margin
16485             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16486         }
16487        
16488         
16489         this.frameId = Roo.id();
16490         
16491          
16492         
16493         var iframe = this.owner.wrap.createChild({
16494             tag: 'iframe',
16495             cls: 'form-control', // bootstrap..
16496             id: this.frameId,
16497             name: this.frameId,
16498             frameBorder : 'no',
16499             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16500         }, this.el
16501         );
16502         
16503         
16504         this.iframe = iframe.dom;
16505
16506          this.assignDocWin();
16507         
16508         this.doc.designMode = 'on';
16509        
16510         this.doc.open();
16511         this.doc.write(this.getDocMarkup());
16512         this.doc.close();
16513
16514         
16515         var task = { // must defer to wait for browser to be ready
16516             run : function(){
16517                 //console.log("run task?" + this.doc.readyState);
16518                 this.assignDocWin();
16519                 if(this.doc.body || this.doc.readyState == 'complete'){
16520                     try {
16521                         this.doc.designMode="on";
16522                     } catch (e) {
16523                         return;
16524                     }
16525                     Roo.TaskMgr.stop(task);
16526                     this.initEditor.defer(10, this);
16527                 }
16528             },
16529             interval : 10,
16530             duration: 10000,
16531             scope: this
16532         };
16533         Roo.TaskMgr.start(task);
16534
16535         
16536          
16537     },
16538
16539     // private
16540     onResize : function(w, h)
16541     {
16542          Roo.log('resize: ' +w + ',' + h );
16543         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16544         if(!this.iframe){
16545             return;
16546         }
16547         if(typeof w == 'number'){
16548             
16549             this.iframe.style.width = w + 'px';
16550         }
16551         if(typeof h == 'number'){
16552             
16553             this.iframe.style.height = h + 'px';
16554             if(this.doc){
16555                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16556             }
16557         }
16558         
16559     },
16560
16561     /**
16562      * Toggles the editor between standard and source edit mode.
16563      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16564      */
16565     toggleSourceEdit : function(sourceEditMode){
16566         
16567         this.sourceEditMode = sourceEditMode === true;
16568         
16569         if(this.sourceEditMode){
16570  
16571             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16572             
16573         }else{
16574             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16575             //this.iframe.className = '';
16576             this.deferFocus();
16577         }
16578         //this.setSize(this.owner.wrap.getSize());
16579         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16580     },
16581
16582     
16583   
16584
16585     /**
16586      * Protected method that will not generally be called directly. If you need/want
16587      * custom HTML cleanup, this is the method you should override.
16588      * @param {String} html The HTML to be cleaned
16589      * return {String} The cleaned HTML
16590      */
16591     cleanHtml : function(html){
16592         html = String(html);
16593         if(html.length > 5){
16594             if(Roo.isSafari){ // strip safari nonsense
16595                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16596             }
16597         }
16598         if(html == '&nbsp;'){
16599             html = '';
16600         }
16601         return html;
16602     },
16603
16604     /**
16605      * HTML Editor -> Textarea
16606      * Protected method that will not generally be called directly. Syncs the contents
16607      * of the editor iframe with the textarea.
16608      */
16609     syncValue : function(){
16610         if(this.initialized){
16611             var bd = (this.doc.body || this.doc.documentElement);
16612             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16613             var html = bd.innerHTML;
16614             if(Roo.isSafari){
16615                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16616                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16617                 if(m && m[1]){
16618                     html = '<div style="'+m[0]+'">' + html + '</div>';
16619                 }
16620             }
16621             html = this.cleanHtml(html);
16622             // fix up the special chars.. normaly like back quotes in word...
16623             // however we do not want to do this with chinese..
16624             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16625                 var cc = b.charCodeAt();
16626                 if (
16627                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16628                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16629                     (cc >= 0xf900 && cc < 0xfb00 )
16630                 ) {
16631                         return b;
16632                 }
16633                 return "&#"+cc+";" 
16634             });
16635             if(this.owner.fireEvent('beforesync', this, html) !== false){
16636                 this.el.dom.value = html;
16637                 this.owner.fireEvent('sync', this, html);
16638             }
16639         }
16640     },
16641
16642     /**
16643      * Protected method that will not generally be called directly. Pushes the value of the textarea
16644      * into the iframe editor.
16645      */
16646     pushValue : function(){
16647         if(this.initialized){
16648             var v = this.el.dom.value.trim();
16649             
16650 //            if(v.length < 1){
16651 //                v = '&#160;';
16652 //            }
16653             
16654             if(this.owner.fireEvent('beforepush', this, v) !== false){
16655                 var d = (this.doc.body || this.doc.documentElement);
16656                 d.innerHTML = v;
16657                 this.cleanUpPaste();
16658                 this.el.dom.value = d.innerHTML;
16659                 this.owner.fireEvent('push', this, v);
16660             }
16661         }
16662     },
16663
16664     // private
16665     deferFocus : function(){
16666         this.focus.defer(10, this);
16667     },
16668
16669     // doc'ed in Field
16670     focus : function(){
16671         if(this.win && !this.sourceEditMode){
16672             this.win.focus();
16673         }else{
16674             this.el.focus();
16675         }
16676     },
16677     
16678     assignDocWin: function()
16679     {
16680         var iframe = this.iframe;
16681         
16682          if(Roo.isIE){
16683             this.doc = iframe.contentWindow.document;
16684             this.win = iframe.contentWindow;
16685         } else {
16686 //            if (!Roo.get(this.frameId)) {
16687 //                return;
16688 //            }
16689 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16690 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16691             
16692             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16693                 return;
16694             }
16695             
16696             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16697             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16698         }
16699     },
16700     
16701     // private
16702     initEditor : function(){
16703         //console.log("INIT EDITOR");
16704         this.assignDocWin();
16705         
16706         
16707         
16708         this.doc.designMode="on";
16709         this.doc.open();
16710         this.doc.write(this.getDocMarkup());
16711         this.doc.close();
16712         
16713         var dbody = (this.doc.body || this.doc.documentElement);
16714         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16715         // this copies styles from the containing element into thsi one..
16716         // not sure why we need all of this..
16717         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16718         
16719         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16720         //ss['background-attachment'] = 'fixed'; // w3c
16721         dbody.bgProperties = 'fixed'; // ie
16722         //Roo.DomHelper.applyStyles(dbody, ss);
16723         Roo.EventManager.on(this.doc, {
16724             //'mousedown': this.onEditorEvent,
16725             'mouseup': this.onEditorEvent,
16726             'dblclick': this.onEditorEvent,
16727             'click': this.onEditorEvent,
16728             'keyup': this.onEditorEvent,
16729             buffer:100,
16730             scope: this
16731         });
16732         if(Roo.isGecko){
16733             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16734         }
16735         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16736             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16737         }
16738         this.initialized = true;
16739
16740         this.owner.fireEvent('initialize', this);
16741         this.pushValue();
16742     },
16743
16744     // private
16745     onDestroy : function(){
16746         
16747         
16748         
16749         if(this.rendered){
16750             
16751             //for (var i =0; i < this.toolbars.length;i++) {
16752             //    // fixme - ask toolbars for heights?
16753             //    this.toolbars[i].onDestroy();
16754            // }
16755             
16756             //this.wrap.dom.innerHTML = '';
16757             //this.wrap.remove();
16758         }
16759     },
16760
16761     // private
16762     onFirstFocus : function(){
16763         
16764         this.assignDocWin();
16765         
16766         
16767         this.activated = true;
16768          
16769     
16770         if(Roo.isGecko){ // prevent silly gecko errors
16771             this.win.focus();
16772             var s = this.win.getSelection();
16773             if(!s.focusNode || s.focusNode.nodeType != 3){
16774                 var r = s.getRangeAt(0);
16775                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16776                 r.collapse(true);
16777                 this.deferFocus();
16778             }
16779             try{
16780                 this.execCmd('useCSS', true);
16781                 this.execCmd('styleWithCSS', false);
16782             }catch(e){}
16783         }
16784         this.owner.fireEvent('activate', this);
16785     },
16786
16787     // private
16788     adjustFont: function(btn){
16789         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16790         //if(Roo.isSafari){ // safari
16791         //    adjust *= 2;
16792        // }
16793         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16794         if(Roo.isSafari){ // safari
16795             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16796             v =  (v < 10) ? 10 : v;
16797             v =  (v > 48) ? 48 : v;
16798             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16799             
16800         }
16801         
16802         
16803         v = Math.max(1, v+adjust);
16804         
16805         this.execCmd('FontSize', v  );
16806     },
16807
16808     onEditorEvent : function(e){
16809         this.owner.fireEvent('editorevent', this, e);
16810       //  this.updateToolbar();
16811         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16812     },
16813
16814     insertTag : function(tg)
16815     {
16816         // could be a bit smarter... -> wrap the current selected tRoo..
16817         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16818             
16819             range = this.createRange(this.getSelection());
16820             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16821             wrappingNode.appendChild(range.extractContents());
16822             range.insertNode(wrappingNode);
16823
16824             return;
16825             
16826             
16827             
16828         }
16829         this.execCmd("formatblock",   tg);
16830         
16831     },
16832     
16833     insertText : function(txt)
16834     {
16835         
16836         
16837         var range = this.createRange();
16838         range.deleteContents();
16839                //alert(Sender.getAttribute('label'));
16840                
16841         range.insertNode(this.doc.createTextNode(txt));
16842     } ,
16843     
16844      
16845
16846     /**
16847      * Executes a Midas editor command on the editor document and performs necessary focus and
16848      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16849      * @param {String} cmd The Midas command
16850      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16851      */
16852     relayCmd : function(cmd, value){
16853         this.win.focus();
16854         this.execCmd(cmd, value);
16855         this.owner.fireEvent('editorevent', this);
16856         //this.updateToolbar();
16857         this.owner.deferFocus();
16858     },
16859
16860     /**
16861      * Executes a Midas editor command directly on the editor document.
16862      * For visual commands, you should use {@link #relayCmd} instead.
16863      * <b>This should only be called after the editor is initialized.</b>
16864      * @param {String} cmd The Midas command
16865      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16866      */
16867     execCmd : function(cmd, value){
16868         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16869         this.syncValue();
16870     },
16871  
16872  
16873    
16874     /**
16875      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16876      * to insert tRoo.
16877      * @param {String} text | dom node.. 
16878      */
16879     insertAtCursor : function(text)
16880     {
16881         
16882         
16883         
16884         if(!this.activated){
16885             return;
16886         }
16887         /*
16888         if(Roo.isIE){
16889             this.win.focus();
16890             var r = this.doc.selection.createRange();
16891             if(r){
16892                 r.collapse(true);
16893                 r.pasteHTML(text);
16894                 this.syncValue();
16895                 this.deferFocus();
16896             
16897             }
16898             return;
16899         }
16900         */
16901         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16902             this.win.focus();
16903             
16904             
16905             // from jquery ui (MIT licenced)
16906             var range, node;
16907             var win = this.win;
16908             
16909             if (win.getSelection && win.getSelection().getRangeAt) {
16910                 range = win.getSelection().getRangeAt(0);
16911                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16912                 range.insertNode(node);
16913             } else if (win.document.selection && win.document.selection.createRange) {
16914                 // no firefox support
16915                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16916                 win.document.selection.createRange().pasteHTML(txt);
16917             } else {
16918                 // no firefox support
16919                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16920                 this.execCmd('InsertHTML', txt);
16921             } 
16922             
16923             this.syncValue();
16924             
16925             this.deferFocus();
16926         }
16927     },
16928  // private
16929     mozKeyPress : function(e){
16930         if(e.ctrlKey){
16931             var c = e.getCharCode(), cmd;
16932           
16933             if(c > 0){
16934                 c = String.fromCharCode(c).toLowerCase();
16935                 switch(c){
16936                     case 'b':
16937                         cmd = 'bold';
16938                         break;
16939                     case 'i':
16940                         cmd = 'italic';
16941                         break;
16942                     
16943                     case 'u':
16944                         cmd = 'underline';
16945                         break;
16946                     
16947                     case 'v':
16948                         this.cleanUpPaste.defer(100, this);
16949                         return;
16950                         
16951                 }
16952                 if(cmd){
16953                     this.win.focus();
16954                     this.execCmd(cmd);
16955                     this.deferFocus();
16956                     e.preventDefault();
16957                 }
16958                 
16959             }
16960         }
16961     },
16962
16963     // private
16964     fixKeys : function(){ // load time branching for fastest keydown performance
16965         if(Roo.isIE){
16966             return function(e){
16967                 var k = e.getKey(), r;
16968                 if(k == e.TAB){
16969                     e.stopEvent();
16970                     r = this.doc.selection.createRange();
16971                     if(r){
16972                         r.collapse(true);
16973                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16974                         this.deferFocus();
16975                     }
16976                     return;
16977                 }
16978                 
16979                 if(k == e.ENTER){
16980                     r = this.doc.selection.createRange();
16981                     if(r){
16982                         var target = r.parentElement();
16983                         if(!target || target.tagName.toLowerCase() != 'li'){
16984                             e.stopEvent();
16985                             r.pasteHTML('<br />');
16986                             r.collapse(false);
16987                             r.select();
16988                         }
16989                     }
16990                 }
16991                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16992                     this.cleanUpPaste.defer(100, this);
16993                     return;
16994                 }
16995                 
16996                 
16997             };
16998         }else if(Roo.isOpera){
16999             return function(e){
17000                 var k = e.getKey();
17001                 if(k == e.TAB){
17002                     e.stopEvent();
17003                     this.win.focus();
17004                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17005                     this.deferFocus();
17006                 }
17007                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17008                     this.cleanUpPaste.defer(100, this);
17009                     return;
17010                 }
17011                 
17012             };
17013         }else if(Roo.isSafari){
17014             return function(e){
17015                 var k = e.getKey();
17016                 
17017                 if(k == e.TAB){
17018                     e.stopEvent();
17019                     this.execCmd('InsertText','\t');
17020                     this.deferFocus();
17021                     return;
17022                 }
17023                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17024                     this.cleanUpPaste.defer(100, this);
17025                     return;
17026                 }
17027                 
17028              };
17029         }
17030     }(),
17031     
17032     getAllAncestors: function()
17033     {
17034         var p = this.getSelectedNode();
17035         var a = [];
17036         if (!p) {
17037             a.push(p); // push blank onto stack..
17038             p = this.getParentElement();
17039         }
17040         
17041         
17042         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17043             a.push(p);
17044             p = p.parentNode;
17045         }
17046         a.push(this.doc.body);
17047         return a;
17048     },
17049     lastSel : false,
17050     lastSelNode : false,
17051     
17052     
17053     getSelection : function() 
17054     {
17055         this.assignDocWin();
17056         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17057     },
17058     
17059     getSelectedNode: function() 
17060     {
17061         // this may only work on Gecko!!!
17062         
17063         // should we cache this!!!!
17064         
17065         
17066         
17067          
17068         var range = this.createRange(this.getSelection()).cloneRange();
17069         
17070         if (Roo.isIE) {
17071             var parent = range.parentElement();
17072             while (true) {
17073                 var testRange = range.duplicate();
17074                 testRange.moveToElementText(parent);
17075                 if (testRange.inRange(range)) {
17076                     break;
17077                 }
17078                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17079                     break;
17080                 }
17081                 parent = parent.parentElement;
17082             }
17083             return parent;
17084         }
17085         
17086         // is ancestor a text element.
17087         var ac =  range.commonAncestorContainer;
17088         if (ac.nodeType == 3) {
17089             ac = ac.parentNode;
17090         }
17091         
17092         var ar = ac.childNodes;
17093          
17094         var nodes = [];
17095         var other_nodes = [];
17096         var has_other_nodes = false;
17097         for (var i=0;i<ar.length;i++) {
17098             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17099                 continue;
17100             }
17101             // fullly contained node.
17102             
17103             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17104                 nodes.push(ar[i]);
17105                 continue;
17106             }
17107             
17108             // probably selected..
17109             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17110                 other_nodes.push(ar[i]);
17111                 continue;
17112             }
17113             // outer..
17114             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17115                 continue;
17116             }
17117             
17118             
17119             has_other_nodes = true;
17120         }
17121         if (!nodes.length && other_nodes.length) {
17122             nodes= other_nodes;
17123         }
17124         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17125             return false;
17126         }
17127         
17128         return nodes[0];
17129     },
17130     createRange: function(sel)
17131     {
17132         // this has strange effects when using with 
17133         // top toolbar - not sure if it's a great idea.
17134         //this.editor.contentWindow.focus();
17135         if (typeof sel != "undefined") {
17136             try {
17137                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17138             } catch(e) {
17139                 return this.doc.createRange();
17140             }
17141         } else {
17142             return this.doc.createRange();
17143         }
17144     },
17145     getParentElement: function()
17146     {
17147         
17148         this.assignDocWin();
17149         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17150         
17151         var range = this.createRange(sel);
17152          
17153         try {
17154             var p = range.commonAncestorContainer;
17155             while (p.nodeType == 3) { // text node
17156                 p = p.parentNode;
17157             }
17158             return p;
17159         } catch (e) {
17160             return null;
17161         }
17162     
17163     },
17164     /***
17165      *
17166      * Range intersection.. the hard stuff...
17167      *  '-1' = before
17168      *  '0' = hits..
17169      *  '1' = after.
17170      *         [ -- selected range --- ]
17171      *   [fail]                        [fail]
17172      *
17173      *    basically..
17174      *      if end is before start or  hits it. fail.
17175      *      if start is after end or hits it fail.
17176      *
17177      *   if either hits (but other is outside. - then it's not 
17178      *   
17179      *    
17180      **/
17181     
17182     
17183     // @see http://www.thismuchiknow.co.uk/?p=64.
17184     rangeIntersectsNode : function(range, node)
17185     {
17186         var nodeRange = node.ownerDocument.createRange();
17187         try {
17188             nodeRange.selectNode(node);
17189         } catch (e) {
17190             nodeRange.selectNodeContents(node);
17191         }
17192     
17193         var rangeStartRange = range.cloneRange();
17194         rangeStartRange.collapse(true);
17195     
17196         var rangeEndRange = range.cloneRange();
17197         rangeEndRange.collapse(false);
17198     
17199         var nodeStartRange = nodeRange.cloneRange();
17200         nodeStartRange.collapse(true);
17201     
17202         var nodeEndRange = nodeRange.cloneRange();
17203         nodeEndRange.collapse(false);
17204     
17205         return rangeStartRange.compareBoundaryPoints(
17206                  Range.START_TO_START, nodeEndRange) == -1 &&
17207                rangeEndRange.compareBoundaryPoints(
17208                  Range.START_TO_START, nodeStartRange) == 1;
17209         
17210          
17211     },
17212     rangeCompareNode : function(range, node)
17213     {
17214         var nodeRange = node.ownerDocument.createRange();
17215         try {
17216             nodeRange.selectNode(node);
17217         } catch (e) {
17218             nodeRange.selectNodeContents(node);
17219         }
17220         
17221         
17222         range.collapse(true);
17223     
17224         nodeRange.collapse(true);
17225      
17226         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17227         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17228          
17229         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17230         
17231         var nodeIsBefore   =  ss == 1;
17232         var nodeIsAfter    = ee == -1;
17233         
17234         if (nodeIsBefore && nodeIsAfter)
17235             return 0; // outer
17236         if (!nodeIsBefore && nodeIsAfter)
17237             return 1; //right trailed.
17238         
17239         if (nodeIsBefore && !nodeIsAfter)
17240             return 2;  // left trailed.
17241         // fully contined.
17242         return 3;
17243     },
17244
17245     // private? - in a new class?
17246     cleanUpPaste :  function()
17247     {
17248         // cleans up the whole document..
17249         Roo.log('cleanuppaste');
17250         
17251         this.cleanUpChildren(this.doc.body);
17252         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17253         if (clean != this.doc.body.innerHTML) {
17254             this.doc.body.innerHTML = clean;
17255         }
17256         
17257     },
17258     
17259     cleanWordChars : function(input) {// change the chars to hex code
17260         var he = Roo.HtmlEditorCore;
17261         
17262         var output = input;
17263         Roo.each(he.swapCodes, function(sw) { 
17264             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17265             
17266             output = output.replace(swapper, sw[1]);
17267         });
17268         
17269         return output;
17270     },
17271     
17272     
17273     cleanUpChildren : function (n)
17274     {
17275         if (!n.childNodes.length) {
17276             return;
17277         }
17278         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17279            this.cleanUpChild(n.childNodes[i]);
17280         }
17281     },
17282     
17283     
17284         
17285     
17286     cleanUpChild : function (node)
17287     {
17288         var ed = this;
17289         //console.log(node);
17290         if (node.nodeName == "#text") {
17291             // clean up silly Windows -- stuff?
17292             return; 
17293         }
17294         if (node.nodeName == "#comment") {
17295             node.parentNode.removeChild(node);
17296             // clean up silly Windows -- stuff?
17297             return; 
17298         }
17299         var lcname = node.tagName.toLowerCase();
17300         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17301         // whitelist of tags..
17302         
17303         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17304             // remove node.
17305             node.parentNode.removeChild(node);
17306             return;
17307             
17308         }
17309         
17310         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17311         
17312         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17313         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17314         
17315         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17316         //    remove_keep_children = true;
17317         //}
17318         
17319         if (remove_keep_children) {
17320             this.cleanUpChildren(node);
17321             // inserts everything just before this node...
17322             while (node.childNodes.length) {
17323                 var cn = node.childNodes[0];
17324                 node.removeChild(cn);
17325                 node.parentNode.insertBefore(cn, node);
17326             }
17327             node.parentNode.removeChild(node);
17328             return;
17329         }
17330         
17331         if (!node.attributes || !node.attributes.length) {
17332             this.cleanUpChildren(node);
17333             return;
17334         }
17335         
17336         function cleanAttr(n,v)
17337         {
17338             
17339             if (v.match(/^\./) || v.match(/^\//)) {
17340                 return;
17341             }
17342             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17343                 return;
17344             }
17345             if (v.match(/^#/)) {
17346                 return;
17347             }
17348 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17349             node.removeAttribute(n);
17350             
17351         }
17352         
17353         var cwhite = this.cwhite;
17354         var cblack = this.cblack;
17355             
17356         function cleanStyle(n,v)
17357         {
17358             if (v.match(/expression/)) { //XSS?? should we even bother..
17359                 node.removeAttribute(n);
17360                 return;
17361             }
17362             
17363             var parts = v.split(/;/);
17364             var clean = [];
17365             
17366             Roo.each(parts, function(p) {
17367                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17368                 if (!p.length) {
17369                     return true;
17370                 }
17371                 var l = p.split(':').shift().replace(/\s+/g,'');
17372                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17373                 
17374                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17375 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17376                     //node.removeAttribute(n);
17377                     return true;
17378                 }
17379                 //Roo.log()
17380                 // only allow 'c whitelisted system attributes'
17381                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17382 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17383                     //node.removeAttribute(n);
17384                     return true;
17385                 }
17386                 
17387                 
17388                  
17389                 
17390                 clean.push(p);
17391                 return true;
17392             });
17393             if (clean.length) { 
17394                 node.setAttribute(n, clean.join(';'));
17395             } else {
17396                 node.removeAttribute(n);
17397             }
17398             
17399         }
17400         
17401         
17402         for (var i = node.attributes.length-1; i > -1 ; i--) {
17403             var a = node.attributes[i];
17404             //console.log(a);
17405             
17406             if (a.name.toLowerCase().substr(0,2)=='on')  {
17407                 node.removeAttribute(a.name);
17408                 continue;
17409             }
17410             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17411                 node.removeAttribute(a.name);
17412                 continue;
17413             }
17414             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17415                 cleanAttr(a.name,a.value); // fixme..
17416                 continue;
17417             }
17418             if (a.name == 'style') {
17419                 cleanStyle(a.name,a.value);
17420                 continue;
17421             }
17422             /// clean up MS crap..
17423             // tecnically this should be a list of valid class'es..
17424             
17425             
17426             if (a.name == 'class') {
17427                 if (a.value.match(/^Mso/)) {
17428                     node.className = '';
17429                 }
17430                 
17431                 if (a.value.match(/body/)) {
17432                     node.className = '';
17433                 }
17434                 continue;
17435             }
17436             
17437             // style cleanup!?
17438             // class cleanup?
17439             
17440         }
17441         
17442         
17443         this.cleanUpChildren(node);
17444         
17445         
17446     },
17447     /**
17448      * Clean up MS wordisms...
17449      */
17450     cleanWord : function(node)
17451     {
17452         var _t = this;
17453         var cleanWordChildren = function()
17454         {
17455             if (!node.childNodes.length) {
17456                 return;
17457             }
17458             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17459                _t.cleanWord(node.childNodes[i]);
17460             }
17461         }
17462         
17463         
17464         if (!node) {
17465             this.cleanWord(this.doc.body);
17466             return;
17467         }
17468         if (node.nodeName == "#text") {
17469             // clean up silly Windows -- stuff?
17470             return; 
17471         }
17472         if (node.nodeName == "#comment") {
17473             node.parentNode.removeChild(node);
17474             // clean up silly Windows -- stuff?
17475             return; 
17476         }
17477         
17478         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17479             node.parentNode.removeChild(node);
17480             return;
17481         }
17482         
17483         // remove - but keep children..
17484         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17485             while (node.childNodes.length) {
17486                 var cn = node.childNodes[0];
17487                 node.removeChild(cn);
17488                 node.parentNode.insertBefore(cn, node);
17489             }
17490             node.parentNode.removeChild(node);
17491             cleanWordChildren();
17492             return;
17493         }
17494         // clean styles
17495         if (node.className.length) {
17496             
17497             var cn = node.className.split(/\W+/);
17498             var cna = [];
17499             Roo.each(cn, function(cls) {
17500                 if (cls.match(/Mso[a-zA-Z]+/)) {
17501                     return;
17502                 }
17503                 cna.push(cls);
17504             });
17505             node.className = cna.length ? cna.join(' ') : '';
17506             if (!cna.length) {
17507                 node.removeAttribute("class");
17508             }
17509         }
17510         
17511         if (node.hasAttribute("lang")) {
17512             node.removeAttribute("lang");
17513         }
17514         
17515         if (node.hasAttribute("style")) {
17516             
17517             var styles = node.getAttribute("style").split(";");
17518             var nstyle = [];
17519             Roo.each(styles, function(s) {
17520                 if (!s.match(/:/)) {
17521                     return;
17522                 }
17523                 var kv = s.split(":");
17524                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17525                     return;
17526                 }
17527                 // what ever is left... we allow.
17528                 nstyle.push(s);
17529             });
17530             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17531             if (!nstyle.length) {
17532                 node.removeAttribute('style');
17533             }
17534         }
17535         
17536         cleanWordChildren();
17537         
17538         
17539     },
17540     domToHTML : function(currentElement, depth, nopadtext) {
17541         
17542         depth = depth || 0;
17543         nopadtext = nopadtext || false;
17544     
17545         if (!currentElement) {
17546             return this.domToHTML(this.doc.body);
17547         }
17548         
17549         //Roo.log(currentElement);
17550         var j;
17551         var allText = false;
17552         var nodeName = currentElement.nodeName;
17553         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17554         
17555         if  (nodeName == '#text') {
17556             return currentElement.nodeValue;
17557         }
17558         
17559         
17560         var ret = '';
17561         if (nodeName != 'BODY') {
17562              
17563             var i = 0;
17564             // Prints the node tagName, such as <A>, <IMG>, etc
17565             if (tagName) {
17566                 var attr = [];
17567                 for(i = 0; i < currentElement.attributes.length;i++) {
17568                     // quoting?
17569                     var aname = currentElement.attributes.item(i).name;
17570                     if (!currentElement.attributes.item(i).value.length) {
17571                         continue;
17572                     }
17573                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17574                 }
17575                 
17576                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17577             } 
17578             else {
17579                 
17580                 // eack
17581             }
17582         } else {
17583             tagName = false;
17584         }
17585         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17586             return ret;
17587         }
17588         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17589             nopadtext = true;
17590         }
17591         
17592         
17593         // Traverse the tree
17594         i = 0;
17595         var currentElementChild = currentElement.childNodes.item(i);
17596         var allText = true;
17597         var innerHTML  = '';
17598         lastnode = '';
17599         while (currentElementChild) {
17600             // Formatting code (indent the tree so it looks nice on the screen)
17601             var nopad = nopadtext;
17602             if (lastnode == 'SPAN') {
17603                 nopad  = true;
17604             }
17605             // text
17606             if  (currentElementChild.nodeName == '#text') {
17607                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17608                 if (!nopad && toadd.length > 80) {
17609                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17610                 }
17611                 innerHTML  += toadd;
17612                 
17613                 i++;
17614                 currentElementChild = currentElement.childNodes.item(i);
17615                 lastNode = '';
17616                 continue;
17617             }
17618             allText = false;
17619             
17620             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17621                 
17622             // Recursively traverse the tree structure of the child node
17623             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17624             lastnode = currentElementChild.nodeName;
17625             i++;
17626             currentElementChild=currentElement.childNodes.item(i);
17627         }
17628         
17629         ret += innerHTML;
17630         
17631         if (!allText) {
17632                 // The remaining code is mostly for formatting the tree
17633             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17634         }
17635         
17636         
17637         if (tagName) {
17638             ret+= "</"+tagName+">";
17639         }
17640         return ret;
17641         
17642     },
17643         
17644     applyBlacklists : function()
17645     {
17646         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17647         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17648         
17649         this.white = [];
17650         this.black = [];
17651         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17652             if (b.indexOf(tag) > -1) {
17653                 return;
17654             }
17655             this.white.push(tag);
17656             
17657         }, this);
17658         
17659         Roo.each(w, function(tag) {
17660             if (b.indexOf(tag) > -1) {
17661                 return;
17662             }
17663             if (this.white.indexOf(tag) > -1) {
17664                 return;
17665             }
17666             this.white.push(tag);
17667             
17668         }, this);
17669         
17670         
17671         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17672             if (w.indexOf(tag) > -1) {
17673                 return;
17674             }
17675             this.black.push(tag);
17676             
17677         }, this);
17678         
17679         Roo.each(b, function(tag) {
17680             if (w.indexOf(tag) > -1) {
17681                 return;
17682             }
17683             if (this.black.indexOf(tag) > -1) {
17684                 return;
17685             }
17686             this.black.push(tag);
17687             
17688         }, this);
17689         
17690         
17691         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17692         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17693         
17694         this.cwhite = [];
17695         this.cblack = [];
17696         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17697             if (b.indexOf(tag) > -1) {
17698                 return;
17699             }
17700             this.cwhite.push(tag);
17701             
17702         }, this);
17703         
17704         Roo.each(w, function(tag) {
17705             if (b.indexOf(tag) > -1) {
17706                 return;
17707             }
17708             if (this.cwhite.indexOf(tag) > -1) {
17709                 return;
17710             }
17711             this.cwhite.push(tag);
17712             
17713         }, this);
17714         
17715         
17716         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17717             if (w.indexOf(tag) > -1) {
17718                 return;
17719             }
17720             this.cblack.push(tag);
17721             
17722         }, this);
17723         
17724         Roo.each(b, function(tag) {
17725             if (w.indexOf(tag) > -1) {
17726                 return;
17727             }
17728             if (this.cblack.indexOf(tag) > -1) {
17729                 return;
17730             }
17731             this.cblack.push(tag);
17732             
17733         }, this);
17734     }
17735     
17736     // hide stuff that is not compatible
17737     /**
17738      * @event blur
17739      * @hide
17740      */
17741     /**
17742      * @event change
17743      * @hide
17744      */
17745     /**
17746      * @event focus
17747      * @hide
17748      */
17749     /**
17750      * @event specialkey
17751      * @hide
17752      */
17753     /**
17754      * @cfg {String} fieldClass @hide
17755      */
17756     /**
17757      * @cfg {String} focusClass @hide
17758      */
17759     /**
17760      * @cfg {String} autoCreate @hide
17761      */
17762     /**
17763      * @cfg {String} inputType @hide
17764      */
17765     /**
17766      * @cfg {String} invalidClass @hide
17767      */
17768     /**
17769      * @cfg {String} invalidText @hide
17770      */
17771     /**
17772      * @cfg {String} msgFx @hide
17773      */
17774     /**
17775      * @cfg {String} validateOnBlur @hide
17776      */
17777 });
17778
17779 Roo.HtmlEditorCore.white = [
17780         'area', 'br', 'img', 'input', 'hr', 'wbr',
17781         
17782        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17783        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17784        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17785        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17786        'table',   'ul',         'xmp', 
17787        
17788        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17789       'thead',   'tr', 
17790      
17791       'dir', 'menu', 'ol', 'ul', 'dl',
17792        
17793       'embed',  'object'
17794 ];
17795
17796
17797 Roo.HtmlEditorCore.black = [
17798     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17799         'applet', // 
17800         'base',   'basefont', 'bgsound', 'blink',  'body', 
17801         'frame',  'frameset', 'head',    'html',   'ilayer', 
17802         'iframe', 'layer',  'link',     'meta',    'object',   
17803         'script', 'style' ,'title',  'xml' // clean later..
17804 ];
17805 Roo.HtmlEditorCore.clean = [
17806     'script', 'style', 'title', 'xml'
17807 ];
17808 Roo.HtmlEditorCore.remove = [
17809     'font'
17810 ];
17811 // attributes..
17812
17813 Roo.HtmlEditorCore.ablack = [
17814     'on'
17815 ];
17816     
17817 Roo.HtmlEditorCore.aclean = [ 
17818     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17819 ];
17820
17821 // protocols..
17822 Roo.HtmlEditorCore.pwhite= [
17823         'http',  'https',  'mailto'
17824 ];
17825
17826 // white listed style attributes.
17827 Roo.HtmlEditorCore.cwhite= [
17828       //  'text-align', /// default is to allow most things..
17829       
17830          
17831 //        'font-size'//??
17832 ];
17833
17834 // black listed style attributes.
17835 Roo.HtmlEditorCore.cblack= [
17836       //  'font-size' -- this can be set by the project 
17837 ];
17838
17839
17840 Roo.HtmlEditorCore.swapCodes   =[ 
17841     [    8211, "--" ], 
17842     [    8212, "--" ], 
17843     [    8216,  "'" ],  
17844     [    8217, "'" ],  
17845     [    8220, '"' ],  
17846     [    8221, '"' ],  
17847     [    8226, "*" ],  
17848     [    8230, "..." ]
17849 ]; 
17850
17851     /*
17852  * - LGPL
17853  *
17854  * HtmlEditor
17855  * 
17856  */
17857
17858 /**
17859  * @class Roo.bootstrap.HtmlEditor
17860  * @extends Roo.bootstrap.TextArea
17861  * Bootstrap HtmlEditor class
17862
17863  * @constructor
17864  * Create a new HtmlEditor
17865  * @param {Object} config The config object
17866  */
17867
17868 Roo.bootstrap.HtmlEditor = function(config){
17869     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17870     if (!this.toolbars) {
17871         this.toolbars = [];
17872     }
17873     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17874     this.addEvents({
17875             /**
17876              * @event initialize
17877              * Fires when the editor is fully initialized (including the iframe)
17878              * @param {HtmlEditor} this
17879              */
17880             initialize: true,
17881             /**
17882              * @event activate
17883              * Fires when the editor is first receives the focus. Any insertion must wait
17884              * until after this event.
17885              * @param {HtmlEditor} this
17886              */
17887             activate: true,
17888              /**
17889              * @event beforesync
17890              * Fires before the textarea is updated with content from the editor iframe. Return false
17891              * to cancel the sync.
17892              * @param {HtmlEditor} this
17893              * @param {String} html
17894              */
17895             beforesync: true,
17896              /**
17897              * @event beforepush
17898              * Fires before the iframe editor is updated with content from the textarea. Return false
17899              * to cancel the push.
17900              * @param {HtmlEditor} this
17901              * @param {String} html
17902              */
17903             beforepush: true,
17904              /**
17905              * @event sync
17906              * Fires when the textarea is updated with content from the editor iframe.
17907              * @param {HtmlEditor} this
17908              * @param {String} html
17909              */
17910             sync: true,
17911              /**
17912              * @event push
17913              * Fires when the iframe editor is updated with content from the textarea.
17914              * @param {HtmlEditor} this
17915              * @param {String} html
17916              */
17917             push: true,
17918              /**
17919              * @event editmodechange
17920              * Fires when the editor switches edit modes
17921              * @param {HtmlEditor} this
17922              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17923              */
17924             editmodechange: true,
17925             /**
17926              * @event editorevent
17927              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17928              * @param {HtmlEditor} this
17929              */
17930             editorevent: true,
17931             /**
17932              * @event firstfocus
17933              * Fires when on first focus - needed by toolbars..
17934              * @param {HtmlEditor} this
17935              */
17936             firstfocus: true,
17937             /**
17938              * @event autosave
17939              * Auto save the htmlEditor value as a file into Events
17940              * @param {HtmlEditor} this
17941              */
17942             autosave: true,
17943             /**
17944              * @event savedpreview
17945              * preview the saved version of htmlEditor
17946              * @param {HtmlEditor} this
17947              */
17948             savedpreview: true
17949         });
17950 };
17951
17952
17953 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17954     
17955     
17956       /**
17957      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17958      */
17959     toolbars : false,
17960    
17961      /**
17962      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17963      *                        Roo.resizable.
17964      */
17965     resizable : false,
17966      /**
17967      * @cfg {Number} height (in pixels)
17968      */   
17969     height: 300,
17970    /**
17971      * @cfg {Number} width (in pixels)
17972      */   
17973     width: false,
17974     
17975     /**
17976      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17977      * 
17978      */
17979     stylesheets: false,
17980     
17981     // id of frame..
17982     frameId: false,
17983     
17984     // private properties
17985     validationEvent : false,
17986     deferHeight: true,
17987     initialized : false,
17988     activated : false,
17989     
17990     onFocus : Roo.emptyFn,
17991     iframePad:3,
17992     hideMode:'offsets',
17993     
17994     
17995     tbContainer : false,
17996     
17997     toolbarContainer :function() {
17998         return this.wrap.select('.x-html-editor-tb',true).first();
17999     },
18000
18001     /**
18002      * Protected method that will not generally be called directly. It
18003      * is called when the editor creates its toolbar. Override this method if you need to
18004      * add custom toolbar buttons.
18005      * @param {HtmlEditor} editor
18006      */
18007     createToolbar : function(){
18008         
18009         Roo.log("create toolbars");
18010         
18011         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18012         this.toolbars[0].render(this.toolbarContainer());
18013         
18014         return;
18015         
18016 //        if (!editor.toolbars || !editor.toolbars.length) {
18017 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18018 //        }
18019 //        
18020 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18021 //            editor.toolbars[i] = Roo.factory(
18022 //                    typeof(editor.toolbars[i]) == 'string' ?
18023 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18024 //                Roo.bootstrap.HtmlEditor);
18025 //            editor.toolbars[i].init(editor);
18026 //        }
18027     },
18028
18029      
18030     // private
18031     onRender : function(ct, position)
18032     {
18033        // Roo.log("Call onRender: " + this.xtype);
18034         var _t = this;
18035         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18036       
18037         this.wrap = this.inputEl().wrap({
18038             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18039         });
18040         
18041         this.editorcore.onRender(ct, position);
18042          
18043         if (this.resizable) {
18044             this.resizeEl = new Roo.Resizable(this.wrap, {
18045                 pinned : true,
18046                 wrap: true,
18047                 dynamic : true,
18048                 minHeight : this.height,
18049                 height: this.height,
18050                 handles : this.resizable,
18051                 width: this.width,
18052                 listeners : {
18053                     resize : function(r, w, h) {
18054                         _t.onResize(w,h); // -something
18055                     }
18056                 }
18057             });
18058             
18059         }
18060         this.createToolbar(this);
18061        
18062         
18063         if(!this.width && this.resizable){
18064             this.setSize(this.wrap.getSize());
18065         }
18066         if (this.resizeEl) {
18067             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18068             // should trigger onReize..
18069         }
18070         
18071     },
18072
18073     // private
18074     onResize : function(w, h)
18075     {
18076         Roo.log('resize: ' +w + ',' + h );
18077         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18078         var ew = false;
18079         var eh = false;
18080         
18081         if(this.inputEl() ){
18082             if(typeof w == 'number'){
18083                 var aw = w - this.wrap.getFrameWidth('lr');
18084                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18085                 ew = aw;
18086             }
18087             if(typeof h == 'number'){
18088                  var tbh = -11;  // fixme it needs to tool bar size!
18089                 for (var i =0; i < this.toolbars.length;i++) {
18090                     // fixme - ask toolbars for heights?
18091                     tbh += this.toolbars[i].el.getHeight();
18092                     //if (this.toolbars[i].footer) {
18093                     //    tbh += this.toolbars[i].footer.el.getHeight();
18094                     //}
18095                 }
18096               
18097                 
18098                 
18099                 
18100                 
18101                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18102                 ah -= 5; // knock a few pixes off for look..
18103                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18104                 var eh = ah;
18105             }
18106         }
18107         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18108         this.editorcore.onResize(ew,eh);
18109         
18110     },
18111
18112     /**
18113      * Toggles the editor between standard and source edit mode.
18114      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18115      */
18116     toggleSourceEdit : function(sourceEditMode)
18117     {
18118         this.editorcore.toggleSourceEdit(sourceEditMode);
18119         
18120         if(this.editorcore.sourceEditMode){
18121             Roo.log('editor - showing textarea');
18122             
18123 //            Roo.log('in');
18124 //            Roo.log(this.syncValue());
18125             this.syncValue();
18126             this.inputEl().removeClass(['hide', 'x-hidden']);
18127             this.inputEl().dom.removeAttribute('tabIndex');
18128             this.inputEl().focus();
18129         }else{
18130             Roo.log('editor - hiding textarea');
18131 //            Roo.log('out')
18132 //            Roo.log(this.pushValue()); 
18133             this.pushValue();
18134             
18135             this.inputEl().addClass(['hide', 'x-hidden']);
18136             this.inputEl().dom.setAttribute('tabIndex', -1);
18137             //this.deferFocus();
18138         }
18139          
18140         if(this.resizable){
18141             this.setSize(this.wrap.getSize());
18142         }
18143         
18144         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18145     },
18146  
18147     // private (for BoxComponent)
18148     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18149
18150     // private (for BoxComponent)
18151     getResizeEl : function(){
18152         return this.wrap;
18153     },
18154
18155     // private (for BoxComponent)
18156     getPositionEl : function(){
18157         return this.wrap;
18158     },
18159
18160     // private
18161     initEvents : function(){
18162         this.originalValue = this.getValue();
18163     },
18164
18165 //    /**
18166 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18167 //     * @method
18168 //     */
18169 //    markInvalid : Roo.emptyFn,
18170 //    /**
18171 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18172 //     * @method
18173 //     */
18174 //    clearInvalid : Roo.emptyFn,
18175
18176     setValue : function(v){
18177         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18178         this.editorcore.pushValue();
18179     },
18180
18181      
18182     // private
18183     deferFocus : function(){
18184         this.focus.defer(10, this);
18185     },
18186
18187     // doc'ed in Field
18188     focus : function(){
18189         this.editorcore.focus();
18190         
18191     },
18192       
18193
18194     // private
18195     onDestroy : function(){
18196         
18197         
18198         
18199         if(this.rendered){
18200             
18201             for (var i =0; i < this.toolbars.length;i++) {
18202                 // fixme - ask toolbars for heights?
18203                 this.toolbars[i].onDestroy();
18204             }
18205             
18206             this.wrap.dom.innerHTML = '';
18207             this.wrap.remove();
18208         }
18209     },
18210
18211     // private
18212     onFirstFocus : function(){
18213         //Roo.log("onFirstFocus");
18214         this.editorcore.onFirstFocus();
18215          for (var i =0; i < this.toolbars.length;i++) {
18216             this.toolbars[i].onFirstFocus();
18217         }
18218         
18219     },
18220     
18221     // private
18222     syncValue : function()
18223     {   
18224         this.editorcore.syncValue();
18225     },
18226     
18227     pushValue : function()
18228     {   
18229         this.editorcore.pushValue();
18230     }
18231      
18232     
18233     // hide stuff that is not compatible
18234     /**
18235      * @event blur
18236      * @hide
18237      */
18238     /**
18239      * @event change
18240      * @hide
18241      */
18242     /**
18243      * @event focus
18244      * @hide
18245      */
18246     /**
18247      * @event specialkey
18248      * @hide
18249      */
18250     /**
18251      * @cfg {String} fieldClass @hide
18252      */
18253     /**
18254      * @cfg {String} focusClass @hide
18255      */
18256     /**
18257      * @cfg {String} autoCreate @hide
18258      */
18259     /**
18260      * @cfg {String} inputType @hide
18261      */
18262     /**
18263      * @cfg {String} invalidClass @hide
18264      */
18265     /**
18266      * @cfg {String} invalidText @hide
18267      */
18268     /**
18269      * @cfg {String} msgFx @hide
18270      */
18271     /**
18272      * @cfg {String} validateOnBlur @hide
18273      */
18274 });
18275  
18276     
18277    
18278    
18279    
18280       
18281 Roo.namespace('Roo.bootstrap.htmleditor');
18282 /**
18283  * @class Roo.bootstrap.HtmlEditorToolbar1
18284  * Basic Toolbar
18285  * 
18286  * Usage:
18287  *
18288  new Roo.bootstrap.HtmlEditor({
18289     ....
18290     toolbars : [
18291         new Roo.bootstrap.HtmlEditorToolbar1({
18292             disable : { fonts: 1 , format: 1, ..., ... , ...],
18293             btns : [ .... ]
18294         })
18295     }
18296      
18297  * 
18298  * @cfg {Object} disable List of elements to disable..
18299  * @cfg {Array} btns List of additional buttons.
18300  * 
18301  * 
18302  * NEEDS Extra CSS? 
18303  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18304  */
18305  
18306 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18307 {
18308     
18309     Roo.apply(this, config);
18310     
18311     // default disabled, based on 'good practice'..
18312     this.disable = this.disable || {};
18313     Roo.applyIf(this.disable, {
18314         fontSize : true,
18315         colors : true,
18316         specialElements : true
18317     });
18318     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18319     
18320     this.editor = config.editor;
18321     this.editorcore = config.editor.editorcore;
18322     
18323     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18324     
18325     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18326     // dont call parent... till later.
18327 }
18328 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18329      
18330     bar : true,
18331     
18332     editor : false,
18333     editorcore : false,
18334     
18335     
18336     formats : [
18337         "p" ,  
18338         "h1","h2","h3","h4","h5","h6", 
18339         "pre", "code", 
18340         "abbr", "acronym", "address", "cite", "samp", "var",
18341         'div','span'
18342     ],
18343     
18344     onRender : function(ct, position)
18345     {
18346        // Roo.log("Call onRender: " + this.xtype);
18347         
18348        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18349        Roo.log(this.el);
18350        this.el.dom.style.marginBottom = '0';
18351        var _this = this;
18352        var editorcore = this.editorcore;
18353        var editor= this.editor;
18354        
18355        var children = [];
18356        var btn = function(id,cmd , toggle, handler){
18357        
18358             var  event = toggle ? 'toggle' : 'click';
18359        
18360             var a = {
18361                 size : 'sm',
18362                 xtype: 'Button',
18363                 xns: Roo.bootstrap,
18364                 glyphicon : id,
18365                 cmd : id || cmd,
18366                 enableToggle:toggle !== false,
18367                 //html : 'submit'
18368                 pressed : toggle ? false : null,
18369                 listeners : {}
18370             }
18371             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18372                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18373             }
18374             children.push(a);
18375             return a;
18376        }
18377         
18378         var style = {
18379                 xtype: 'Button',
18380                 size : 'sm',
18381                 xns: Roo.bootstrap,
18382                 glyphicon : 'font',
18383                 //html : 'submit'
18384                 menu : {
18385                     xtype: 'Menu',
18386                     xns: Roo.bootstrap,
18387                     items:  []
18388                 }
18389         };
18390         Roo.each(this.formats, function(f) {
18391             style.menu.items.push({
18392                 xtype :'MenuItem',
18393                 xns: Roo.bootstrap,
18394                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18395                 tagname : f,
18396                 listeners : {
18397                     click : function()
18398                     {
18399                         editorcore.insertTag(this.tagname);
18400                         editor.focus();
18401                     }
18402                 }
18403                 
18404             });
18405         });
18406          children.push(style);   
18407             
18408             
18409         btn('bold',false,true);
18410         btn('italic',false,true);
18411         btn('align-left', 'justifyleft',true);
18412         btn('align-center', 'justifycenter',true);
18413         btn('align-right' , 'justifyright',true);
18414         btn('link', false, false, function(btn) {
18415             //Roo.log("create link?");
18416             var url = prompt(this.createLinkText, this.defaultLinkValue);
18417             if(url && url != 'http:/'+'/'){
18418                 this.editorcore.relayCmd('createlink', url);
18419             }
18420         }),
18421         btn('list','insertunorderedlist',true);
18422         btn('pencil', false,true, function(btn){
18423                 Roo.log(this);
18424                 
18425                 this.toggleSourceEdit(btn.pressed);
18426         });
18427         /*
18428         var cog = {
18429                 xtype: 'Button',
18430                 size : 'sm',
18431                 xns: Roo.bootstrap,
18432                 glyphicon : 'cog',
18433                 //html : 'submit'
18434                 menu : {
18435                     xtype: 'Menu',
18436                     xns: Roo.bootstrap,
18437                     items:  []
18438                 }
18439         };
18440         
18441         cog.menu.items.push({
18442             xtype :'MenuItem',
18443             xns: Roo.bootstrap,
18444             html : Clean styles,
18445             tagname : f,
18446             listeners : {
18447                 click : function()
18448                 {
18449                     editorcore.insertTag(this.tagname);
18450                     editor.focus();
18451                 }
18452             }
18453             
18454         });
18455        */
18456         
18457          
18458        this.xtype = 'NavSimplebar';
18459         
18460         for(var i=0;i< children.length;i++) {
18461             
18462             this.buttons.add(this.addxtypeChild(children[i]));
18463             
18464         }
18465         
18466         editor.on('editorevent', this.updateToolbar, this);
18467     },
18468     onBtnClick : function(id)
18469     {
18470        this.editorcore.relayCmd(id);
18471        this.editorcore.focus();
18472     },
18473     
18474     /**
18475      * Protected method that will not generally be called directly. It triggers
18476      * a toolbar update by reading the markup state of the current selection in the editor.
18477      */
18478     updateToolbar: function(){
18479
18480         if(!this.editorcore.activated){
18481             this.editor.onFirstFocus(); // is this neeed?
18482             return;
18483         }
18484
18485         var btns = this.buttons; 
18486         var doc = this.editorcore.doc;
18487         btns.get('bold').setActive(doc.queryCommandState('bold'));
18488         btns.get('italic').setActive(doc.queryCommandState('italic'));
18489         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18490         
18491         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18492         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18493         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18494         
18495         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18496         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18497          /*
18498         
18499         var ans = this.editorcore.getAllAncestors();
18500         if (this.formatCombo) {
18501             
18502             
18503             var store = this.formatCombo.store;
18504             this.formatCombo.setValue("");
18505             for (var i =0; i < ans.length;i++) {
18506                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18507                     // select it..
18508                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18509                     break;
18510                 }
18511             }
18512         }
18513         
18514         
18515         
18516         // hides menus... - so this cant be on a menu...
18517         Roo.bootstrap.MenuMgr.hideAll();
18518         */
18519         Roo.bootstrap.MenuMgr.hideAll();
18520         //this.editorsyncValue();
18521     },
18522     onFirstFocus: function() {
18523         this.buttons.each(function(item){
18524            item.enable();
18525         });
18526     },
18527     toggleSourceEdit : function(sourceEditMode){
18528         
18529           
18530         if(sourceEditMode){
18531             Roo.log("disabling buttons");
18532            this.buttons.each( function(item){
18533                 if(item.cmd != 'pencil'){
18534                     item.disable();
18535                 }
18536             });
18537           
18538         }else{
18539             Roo.log("enabling buttons");
18540             if(this.editorcore.initialized){
18541                 this.buttons.each( function(item){
18542                     item.enable();
18543                 });
18544             }
18545             
18546         }
18547         Roo.log("calling toggole on editor");
18548         // tell the editor that it's been pressed..
18549         this.editor.toggleSourceEdit(sourceEditMode);
18550        
18551     }
18552 });
18553
18554
18555
18556
18557
18558 /**
18559  * @class Roo.bootstrap.Table.AbstractSelectionModel
18560  * @extends Roo.util.Observable
18561  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18562  * implemented by descendant classes.  This class should not be directly instantiated.
18563  * @constructor
18564  */
18565 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18566     this.locked = false;
18567     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18568 };
18569
18570
18571 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18572     /** @ignore Called by the grid automatically. Do not call directly. */
18573     init : function(grid){
18574         this.grid = grid;
18575         this.initEvents();
18576     },
18577
18578     /**
18579      * Locks the selections.
18580      */
18581     lock : function(){
18582         this.locked = true;
18583     },
18584
18585     /**
18586      * Unlocks the selections.
18587      */
18588     unlock : function(){
18589         this.locked = false;
18590     },
18591
18592     /**
18593      * Returns true if the selections are locked.
18594      * @return {Boolean}
18595      */
18596     isLocked : function(){
18597         return this.locked;
18598     }
18599 });
18600 /**
18601  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18602  * @class Roo.bootstrap.Table.RowSelectionModel
18603  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18604  * It supports multiple selections and keyboard selection/navigation. 
18605  * @constructor
18606  * @param {Object} config
18607  */
18608
18609 Roo.bootstrap.Table.RowSelectionModel = function(config){
18610     Roo.apply(this, config);
18611     this.selections = new Roo.util.MixedCollection(false, function(o){
18612         return o.id;
18613     });
18614
18615     this.last = false;
18616     this.lastActive = false;
18617
18618     this.addEvents({
18619         /**
18620              * @event selectionchange
18621              * Fires when the selection changes
18622              * @param {SelectionModel} this
18623              */
18624             "selectionchange" : true,
18625         /**
18626              * @event afterselectionchange
18627              * Fires after the selection changes (eg. by key press or clicking)
18628              * @param {SelectionModel} this
18629              */
18630             "afterselectionchange" : true,
18631         /**
18632              * @event beforerowselect
18633              * Fires when a row is selected being selected, return false to cancel.
18634              * @param {SelectionModel} this
18635              * @param {Number} rowIndex The selected index
18636              * @param {Boolean} keepExisting False if other selections will be cleared
18637              */
18638             "beforerowselect" : true,
18639         /**
18640              * @event rowselect
18641              * Fires when a row is selected.
18642              * @param {SelectionModel} this
18643              * @param {Number} rowIndex The selected index
18644              * @param {Roo.data.Record} r The record
18645              */
18646             "rowselect" : true,
18647         /**
18648              * @event rowdeselect
18649              * Fires when a row is deselected.
18650              * @param {SelectionModel} this
18651              * @param {Number} rowIndex The selected index
18652              */
18653         "rowdeselect" : true
18654     });
18655     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18656     this.locked = false;
18657 };
18658
18659 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18660     /**
18661      * @cfg {Boolean} singleSelect
18662      * True to allow selection of only one row at a time (defaults to false)
18663      */
18664     singleSelect : false,
18665
18666     // private
18667     initEvents : function(){
18668
18669         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18670             this.grid.on("mousedown", this.handleMouseDown, this);
18671         }else{ // allow click to work like normal
18672             this.grid.on("rowclick", this.handleDragableRowClick, this);
18673         }
18674
18675         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18676             "up" : function(e){
18677                 if(!e.shiftKey){
18678                     this.selectPrevious(e.shiftKey);
18679                 }else if(this.last !== false && this.lastActive !== false){
18680                     var last = this.last;
18681                     this.selectRange(this.last,  this.lastActive-1);
18682                     this.grid.getView().focusRow(this.lastActive);
18683                     if(last !== false){
18684                         this.last = last;
18685                     }
18686                 }else{
18687                     this.selectFirstRow();
18688                 }
18689                 this.fireEvent("afterselectionchange", this);
18690             },
18691             "down" : function(e){
18692                 if(!e.shiftKey){
18693                     this.selectNext(e.shiftKey);
18694                 }else if(this.last !== false && this.lastActive !== false){
18695                     var last = this.last;
18696                     this.selectRange(this.last,  this.lastActive+1);
18697                     this.grid.getView().focusRow(this.lastActive);
18698                     if(last !== false){
18699                         this.last = last;
18700                     }
18701                 }else{
18702                     this.selectFirstRow();
18703                 }
18704                 this.fireEvent("afterselectionchange", this);
18705             },
18706             scope: this
18707         });
18708
18709         var view = this.grid.view;
18710         view.on("refresh", this.onRefresh, this);
18711         view.on("rowupdated", this.onRowUpdated, this);
18712         view.on("rowremoved", this.onRemove, this);
18713     },
18714
18715     // private
18716     onRefresh : function(){
18717         var ds = this.grid.dataSource, i, v = this.grid.view;
18718         var s = this.selections;
18719         s.each(function(r){
18720             if((i = ds.indexOfId(r.id)) != -1){
18721                 v.onRowSelect(i);
18722             }else{
18723                 s.remove(r);
18724             }
18725         });
18726     },
18727
18728     // private
18729     onRemove : function(v, index, r){
18730         this.selections.remove(r);
18731     },
18732
18733     // private
18734     onRowUpdated : function(v, index, r){
18735         if(this.isSelected(r)){
18736             v.onRowSelect(index);
18737         }
18738     },
18739
18740     /**
18741      * Select records.
18742      * @param {Array} records The records to select
18743      * @param {Boolean} keepExisting (optional) True to keep existing selections
18744      */
18745     selectRecords : function(records, keepExisting){
18746         if(!keepExisting){
18747             this.clearSelections();
18748         }
18749         var ds = this.grid.dataSource;
18750         for(var i = 0, len = records.length; i < len; i++){
18751             this.selectRow(ds.indexOf(records[i]), true);
18752         }
18753     },
18754
18755     /**
18756      * Gets the number of selected rows.
18757      * @return {Number}
18758      */
18759     getCount : function(){
18760         return this.selections.length;
18761     },
18762
18763     /**
18764      * Selects the first row in the grid.
18765      */
18766     selectFirstRow : function(){
18767         this.selectRow(0);
18768     },
18769
18770     /**
18771      * Select the last row.
18772      * @param {Boolean} keepExisting (optional) True to keep existing selections
18773      */
18774     selectLastRow : function(keepExisting){
18775         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18776     },
18777
18778     /**
18779      * Selects the row immediately following the last selected row.
18780      * @param {Boolean} keepExisting (optional) True to keep existing selections
18781      */
18782     selectNext : function(keepExisting){
18783         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18784             this.selectRow(this.last+1, keepExisting);
18785             this.grid.getView().focusRow(this.last);
18786         }
18787     },
18788
18789     /**
18790      * Selects the row that precedes the last selected row.
18791      * @param {Boolean} keepExisting (optional) True to keep existing selections
18792      */
18793     selectPrevious : function(keepExisting){
18794         if(this.last){
18795             this.selectRow(this.last-1, keepExisting);
18796             this.grid.getView().focusRow(this.last);
18797         }
18798     },
18799
18800     /**
18801      * Returns the selected records
18802      * @return {Array} Array of selected records
18803      */
18804     getSelections : function(){
18805         return [].concat(this.selections.items);
18806     },
18807
18808     /**
18809      * Returns the first selected record.
18810      * @return {Record}
18811      */
18812     getSelected : function(){
18813         return this.selections.itemAt(0);
18814     },
18815
18816
18817     /**
18818      * Clears all selections.
18819      */
18820     clearSelections : function(fast){
18821         if(this.locked) return;
18822         if(fast !== true){
18823             var ds = this.grid.dataSource;
18824             var s = this.selections;
18825             s.each(function(r){
18826                 this.deselectRow(ds.indexOfId(r.id));
18827             }, this);
18828             s.clear();
18829         }else{
18830             this.selections.clear();
18831         }
18832         this.last = false;
18833     },
18834
18835
18836     /**
18837      * Selects all rows.
18838      */
18839     selectAll : function(){
18840         if(this.locked) return;
18841         this.selections.clear();
18842         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18843             this.selectRow(i, true);
18844         }
18845     },
18846
18847     /**
18848      * Returns True if there is a selection.
18849      * @return {Boolean}
18850      */
18851     hasSelection : function(){
18852         return this.selections.length > 0;
18853     },
18854
18855     /**
18856      * Returns True if the specified row is selected.
18857      * @param {Number/Record} record The record or index of the record to check
18858      * @return {Boolean}
18859      */
18860     isSelected : function(index){
18861         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18862         return (r && this.selections.key(r.id) ? true : false);
18863     },
18864
18865     /**
18866      * Returns True if the specified record id is selected.
18867      * @param {String} id The id of record to check
18868      * @return {Boolean}
18869      */
18870     isIdSelected : function(id){
18871         return (this.selections.key(id) ? true : false);
18872     },
18873
18874     // private
18875     handleMouseDown : function(e, t){
18876         var view = this.grid.getView(), rowIndex;
18877         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18878             return;
18879         };
18880         if(e.shiftKey && this.last !== false){
18881             var last = this.last;
18882             this.selectRange(last, rowIndex, e.ctrlKey);
18883             this.last = last; // reset the last
18884             view.focusRow(rowIndex);
18885         }else{
18886             var isSelected = this.isSelected(rowIndex);
18887             if(e.button !== 0 && isSelected){
18888                 view.focusRow(rowIndex);
18889             }else if(e.ctrlKey && isSelected){
18890                 this.deselectRow(rowIndex);
18891             }else if(!isSelected){
18892                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18893                 view.focusRow(rowIndex);
18894             }
18895         }
18896         this.fireEvent("afterselectionchange", this);
18897     },
18898     // private
18899     handleDragableRowClick :  function(grid, rowIndex, e) 
18900     {
18901         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18902             this.selectRow(rowIndex, false);
18903             grid.view.focusRow(rowIndex);
18904              this.fireEvent("afterselectionchange", this);
18905         }
18906     },
18907     
18908     /**
18909      * Selects multiple rows.
18910      * @param {Array} rows Array of the indexes of the row to select
18911      * @param {Boolean} keepExisting (optional) True to keep existing selections
18912      */
18913     selectRows : function(rows, keepExisting){
18914         if(!keepExisting){
18915             this.clearSelections();
18916         }
18917         for(var i = 0, len = rows.length; i < len; i++){
18918             this.selectRow(rows[i], true);
18919         }
18920     },
18921
18922     /**
18923      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18924      * @param {Number} startRow The index of the first row in the range
18925      * @param {Number} endRow The index of the last row in the range
18926      * @param {Boolean} keepExisting (optional) True to retain existing selections
18927      */
18928     selectRange : function(startRow, endRow, keepExisting){
18929         if(this.locked) return;
18930         if(!keepExisting){
18931             this.clearSelections();
18932         }
18933         if(startRow <= endRow){
18934             for(var i = startRow; i <= endRow; i++){
18935                 this.selectRow(i, true);
18936             }
18937         }else{
18938             for(var i = startRow; i >= endRow; i--){
18939                 this.selectRow(i, true);
18940             }
18941         }
18942     },
18943
18944     /**
18945      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18946      * @param {Number} startRow The index of the first row in the range
18947      * @param {Number} endRow The index of the last row in the range
18948      */
18949     deselectRange : function(startRow, endRow, preventViewNotify){
18950         if(this.locked) return;
18951         for(var i = startRow; i <= endRow; i++){
18952             this.deselectRow(i, preventViewNotify);
18953         }
18954     },
18955
18956     /**
18957      * Selects a row.
18958      * @param {Number} row The index of the row to select
18959      * @param {Boolean} keepExisting (optional) True to keep existing selections
18960      */
18961     selectRow : function(index, keepExisting, preventViewNotify){
18962         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18963         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18964             if(!keepExisting || this.singleSelect){
18965                 this.clearSelections();
18966             }
18967             var r = this.grid.dataSource.getAt(index);
18968             this.selections.add(r);
18969             this.last = this.lastActive = index;
18970             if(!preventViewNotify){
18971                 this.grid.getView().onRowSelect(index);
18972             }
18973             this.fireEvent("rowselect", this, index, r);
18974             this.fireEvent("selectionchange", this);
18975         }
18976     },
18977
18978     /**
18979      * Deselects a row.
18980      * @param {Number} row The index of the row to deselect
18981      */
18982     deselectRow : function(index, preventViewNotify){
18983         if(this.locked) return;
18984         if(this.last == index){
18985             this.last = false;
18986         }
18987         if(this.lastActive == index){
18988             this.lastActive = false;
18989         }
18990         var r = this.grid.dataSource.getAt(index);
18991         this.selections.remove(r);
18992         if(!preventViewNotify){
18993             this.grid.getView().onRowDeselect(index);
18994         }
18995         this.fireEvent("rowdeselect", this, index);
18996         this.fireEvent("selectionchange", this);
18997     },
18998
18999     // private
19000     restoreLast : function(){
19001         if(this._last){
19002             this.last = this._last;
19003         }
19004     },
19005
19006     // private
19007     acceptsNav : function(row, col, cm){
19008         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19009     },
19010
19011     // private
19012     onEditorKey : function(field, e){
19013         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19014         if(k == e.TAB){
19015             e.stopEvent();
19016             ed.completeEdit();
19017             if(e.shiftKey){
19018                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19019             }else{
19020                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19021             }
19022         }else if(k == e.ENTER && !e.ctrlKey){
19023             e.stopEvent();
19024             ed.completeEdit();
19025             if(e.shiftKey){
19026                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19027             }else{
19028                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19029             }
19030         }else if(k == e.ESC){
19031             ed.cancelEdit();
19032         }
19033         if(newCell){
19034             g.startEditing(newCell[0], newCell[1]);
19035         }
19036     }
19037 });/*
19038  * Based on:
19039  * Ext JS Library 1.1.1
19040  * Copyright(c) 2006-2007, Ext JS, LLC.
19041  *
19042  * Originally Released Under LGPL - original licence link has changed is not relivant.
19043  *
19044  * Fork - LGPL
19045  * <script type="text/javascript">
19046  */
19047  
19048 /**
19049  * @class Roo.bootstrap.PagingToolbar
19050  * @extends Roo.Row
19051  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19052  * @constructor
19053  * Create a new PagingToolbar
19054  * @param {Object} config The config object
19055  */
19056 Roo.bootstrap.PagingToolbar = function(config)
19057 {
19058     // old args format still supported... - xtype is prefered..
19059         // created from xtype...
19060     var ds = config.dataSource;
19061     this.toolbarItems = [];
19062     if (config.items) {
19063         this.toolbarItems = config.items;
19064 //        config.items = [];
19065     }
19066     
19067     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19068     this.ds = ds;
19069     this.cursor = 0;
19070     if (ds) { 
19071         this.bind(ds);
19072     }
19073     
19074     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19075     
19076 };
19077
19078 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19079     /**
19080      * @cfg {Roo.data.Store} dataSource
19081      * The underlying data store providing the paged data
19082      */
19083     /**
19084      * @cfg {String/HTMLElement/Element} container
19085      * container The id or element that will contain the toolbar
19086      */
19087     /**
19088      * @cfg {Boolean} displayInfo
19089      * True to display the displayMsg (defaults to false)
19090      */
19091     /**
19092      * @cfg {Number} pageSize
19093      * The number of records to display per page (defaults to 20)
19094      */
19095     pageSize: 20,
19096     /**
19097      * @cfg {String} displayMsg
19098      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19099      */
19100     displayMsg : 'Displaying {0} - {1} of {2}',
19101     /**
19102      * @cfg {String} emptyMsg
19103      * The message to display when no records are found (defaults to "No data to display")
19104      */
19105     emptyMsg : 'No data to display',
19106     /**
19107      * Customizable piece of the default paging text (defaults to "Page")
19108      * @type String
19109      */
19110     beforePageText : "Page",
19111     /**
19112      * Customizable piece of the default paging text (defaults to "of %0")
19113      * @type String
19114      */
19115     afterPageText : "of {0}",
19116     /**
19117      * Customizable piece of the default paging text (defaults to "First Page")
19118      * @type String
19119      */
19120     firstText : "First Page",
19121     /**
19122      * Customizable piece of the default paging text (defaults to "Previous Page")
19123      * @type String
19124      */
19125     prevText : "Previous Page",
19126     /**
19127      * Customizable piece of the default paging text (defaults to "Next Page")
19128      * @type String
19129      */
19130     nextText : "Next Page",
19131     /**
19132      * Customizable piece of the default paging text (defaults to "Last Page")
19133      * @type String
19134      */
19135     lastText : "Last Page",
19136     /**
19137      * Customizable piece of the default paging text (defaults to "Refresh")
19138      * @type String
19139      */
19140     refreshText : "Refresh",
19141
19142     buttons : false,
19143     // private
19144     onRender : function(ct, position) 
19145     {
19146         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19147         this.navgroup.parentId = this.id;
19148         this.navgroup.onRender(this.el, null);
19149         // add the buttons to the navgroup
19150         
19151         if(this.displayInfo){
19152             Roo.log(this.el.select('ul.navbar-nav',true).first());
19153             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19154             this.displayEl = this.el.select('.x-paging-info', true).first();
19155 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19156 //            this.displayEl = navel.el.select('span',true).first();
19157         }
19158         
19159         var _this = this;
19160         
19161         if(this.buttons){
19162             Roo.each(_this.buttons, function(e){
19163                Roo.factory(e).onRender(_this.el, null);
19164             });
19165         }
19166             
19167         Roo.each(_this.toolbarItems, function(e) {
19168             _this.navgroup.addItem(e);
19169         });
19170         
19171         this.first = this.navgroup.addItem({
19172             tooltip: this.firstText,
19173             cls: "prev",
19174             icon : 'fa fa-backward',
19175             disabled: true,
19176             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19177         });
19178         
19179         this.prev =  this.navgroup.addItem({
19180             tooltip: this.prevText,
19181             cls: "prev",
19182             icon : 'fa fa-step-backward',
19183             disabled: true,
19184             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19185         });
19186     //this.addSeparator();
19187         
19188         
19189         var field = this.navgroup.addItem( {
19190             tagtype : 'span',
19191             cls : 'x-paging-position',
19192             
19193             html : this.beforePageText  +
19194                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19195                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19196          } ); //?? escaped?
19197         
19198         this.field = field.el.select('input', true).first();
19199         this.field.on("keydown", this.onPagingKeydown, this);
19200         this.field.on("focus", function(){this.dom.select();});
19201     
19202     
19203         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19204         //this.field.setHeight(18);
19205         //this.addSeparator();
19206         this.next = this.navgroup.addItem({
19207             tooltip: this.nextText,
19208             cls: "next",
19209             html : ' <i class="fa fa-step-forward">',
19210             disabled: true,
19211             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19212         });
19213         this.last = this.navgroup.addItem({
19214             tooltip: this.lastText,
19215             icon : 'fa fa-forward',
19216             cls: "next",
19217             disabled: true,
19218             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19219         });
19220     //this.addSeparator();
19221         this.loading = this.navgroup.addItem({
19222             tooltip: this.refreshText,
19223             icon: 'fa fa-refresh',
19224             
19225             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19226         });
19227
19228     },
19229
19230     // private
19231     updateInfo : function(){
19232         if(this.displayEl){
19233             var count = this.ds.getCount();
19234             var msg = count == 0 ?
19235                 this.emptyMsg :
19236                 String.format(
19237                     this.displayMsg,
19238                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19239                 );
19240             this.displayEl.update(msg);
19241         }
19242     },
19243
19244     // private
19245     onLoad : function(ds, r, o){
19246        this.cursor = o.params ? o.params.start : 0;
19247        var d = this.getPageData(),
19248             ap = d.activePage,
19249             ps = d.pages;
19250         
19251        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19252        this.field.dom.value = ap;
19253        this.first.setDisabled(ap == 1);
19254        this.prev.setDisabled(ap == 1);
19255        this.next.setDisabled(ap == ps);
19256        this.last.setDisabled(ap == ps);
19257        this.loading.enable();
19258        this.updateInfo();
19259     },
19260
19261     // private
19262     getPageData : function(){
19263         var total = this.ds.getTotalCount();
19264         return {
19265             total : total,
19266             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19267             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19268         };
19269     },
19270
19271     // private
19272     onLoadError : function(){
19273         this.loading.enable();
19274     },
19275
19276     // private
19277     onPagingKeydown : function(e){
19278         var k = e.getKey();
19279         var d = this.getPageData();
19280         if(k == e.RETURN){
19281             var v = this.field.dom.value, pageNum;
19282             if(!v || isNaN(pageNum = parseInt(v, 10))){
19283                 this.field.dom.value = d.activePage;
19284                 return;
19285             }
19286             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19287             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19288             e.stopEvent();
19289         }
19290         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))
19291         {
19292           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19293           this.field.dom.value = pageNum;
19294           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19295           e.stopEvent();
19296         }
19297         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19298         {
19299           var v = this.field.dom.value, pageNum; 
19300           var increment = (e.shiftKey) ? 10 : 1;
19301           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19302             increment *= -1;
19303           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19304             this.field.dom.value = d.activePage;
19305             return;
19306           }
19307           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19308           {
19309             this.field.dom.value = parseInt(v, 10) + increment;
19310             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19311             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19312           }
19313           e.stopEvent();
19314         }
19315     },
19316
19317     // private
19318     beforeLoad : function(){
19319         if(this.loading){
19320             this.loading.disable();
19321         }
19322     },
19323
19324     // private
19325     onClick : function(which){
19326         var ds = this.ds;
19327         if (!ds) {
19328             return;
19329         }
19330         switch(which){
19331             case "first":
19332                 ds.load({params:{start: 0, limit: this.pageSize}});
19333             break;
19334             case "prev":
19335                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19336             break;
19337             case "next":
19338                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19339             break;
19340             case "last":
19341                 var total = ds.getTotalCount();
19342                 var extra = total % this.pageSize;
19343                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19344                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19345             break;
19346             case "refresh":
19347                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19348             break;
19349         }
19350     },
19351
19352     /**
19353      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19354      * @param {Roo.data.Store} store The data store to unbind
19355      */
19356     unbind : function(ds){
19357         ds.un("beforeload", this.beforeLoad, this);
19358         ds.un("load", this.onLoad, this);
19359         ds.un("loadexception", this.onLoadError, this);
19360         ds.un("remove", this.updateInfo, this);
19361         ds.un("add", this.updateInfo, this);
19362         this.ds = undefined;
19363     },
19364
19365     /**
19366      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19367      * @param {Roo.data.Store} store The data store to bind
19368      */
19369     bind : function(ds){
19370         ds.on("beforeload", this.beforeLoad, this);
19371         ds.on("load", this.onLoad, this);
19372         ds.on("loadexception", this.onLoadError, this);
19373         ds.on("remove", this.updateInfo, this);
19374         ds.on("add", this.updateInfo, this);
19375         this.ds = ds;
19376     }
19377 });/*
19378  * - LGPL
19379  *
19380  * element
19381  * 
19382  */
19383
19384 /**
19385  * @class Roo.bootstrap.MessageBar
19386  * @extends Roo.bootstrap.Component
19387  * Bootstrap MessageBar class
19388  * @cfg {String} html contents of the MessageBar
19389  * @cfg {String} weight (info | success | warning | danger) default info
19390  * @cfg {String} beforeClass insert the bar before the given class
19391  * @cfg {Boolean} closable (true | false) default false
19392  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19393  * 
19394  * @constructor
19395  * Create a new Element
19396  * @param {Object} config The config object
19397  */
19398
19399 Roo.bootstrap.MessageBar = function(config){
19400     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19401 };
19402
19403 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19404     
19405     html: '',
19406     weight: 'info',
19407     closable: false,
19408     fixed: false,
19409     beforeClass: 'bootstrap-sticky-wrap',
19410     
19411     getAutoCreate : function(){
19412         
19413         var cfg = {
19414             tag: 'div',
19415             cls: 'alert alert-dismissable alert-' + this.weight,
19416             cn: [
19417                 {
19418                     tag: 'span',
19419                     cls: 'message',
19420                     html: this.html || ''
19421                 }
19422             ]
19423         }
19424         
19425         if(this.fixed){
19426             cfg.cls += ' alert-messages-fixed';
19427         }
19428         
19429         if(this.closable){
19430             cfg.cn.push({
19431                 tag: 'button',
19432                 cls: 'close',
19433                 html: 'x'
19434             });
19435         }
19436         
19437         return cfg;
19438     },
19439     
19440     onRender : function(ct, position)
19441     {
19442         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19443         
19444         if(!this.el){
19445             var cfg = Roo.apply({},  this.getAutoCreate());
19446             cfg.id = Roo.id();
19447             
19448             if (this.cls) {
19449                 cfg.cls += ' ' + this.cls;
19450             }
19451             if (this.style) {
19452                 cfg.style = this.style;
19453             }
19454             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19455             
19456             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19457         }
19458         
19459         this.el.select('>button.close').on('click', this.hide, this);
19460         
19461     },
19462     
19463     show : function()
19464     {
19465         if (!this.rendered) {
19466             this.render();
19467         }
19468         
19469         this.el.show();
19470         
19471         this.fireEvent('show', this);
19472         
19473     },
19474     
19475     hide : function()
19476     {
19477         if (!this.rendered) {
19478             this.render();
19479         }
19480         
19481         this.el.hide();
19482         
19483         this.fireEvent('hide', this);
19484     },
19485     
19486     update : function()
19487     {
19488 //        var e = this.el.dom.firstChild;
19489 //        
19490 //        if(this.closable){
19491 //            e = e.nextSibling;
19492 //        }
19493 //        
19494 //        e.data = this.html || '';
19495
19496         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19497     }
19498    
19499 });
19500
19501  
19502
19503      /*
19504  * - LGPL
19505  *
19506  * Graph
19507  * 
19508  */
19509
19510
19511 /**
19512  * @class Roo.bootstrap.Graph
19513  * @extends Roo.bootstrap.Component
19514  * Bootstrap Graph class
19515 > Prameters
19516  -sm {number} sm 4
19517  -md {number} md 5
19518  @cfg {String} graphtype  bar | vbar | pie
19519  @cfg {number} g_x coodinator | centre x (pie)
19520  @cfg {number} g_y coodinator | centre y (pie)
19521  @cfg {number} g_r radius (pie)
19522  @cfg {number} g_height height of the chart (respected by all elements in the set)
19523  @cfg {number} g_width width of the chart (respected by all elements in the set)
19524  @cfg {Object} title The title of the chart
19525     
19526  -{Array}  values
19527  -opts (object) options for the chart 
19528      o {
19529      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19530      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19531      o vgutter (number)
19532      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.
19533      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19534      o to
19535      o stretch (boolean)
19536      o }
19537  -opts (object) options for the pie
19538      o{
19539      o cut
19540      o startAngle (number)
19541      o endAngle (number)
19542      } 
19543  *
19544  * @constructor
19545  * Create a new Input
19546  * @param {Object} config The config object
19547  */
19548
19549 Roo.bootstrap.Graph = function(config){
19550     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19551     
19552     this.addEvents({
19553         // img events
19554         /**
19555          * @event click
19556          * The img click event for the img.
19557          * @param {Roo.EventObject} e
19558          */
19559         "click" : true
19560     });
19561 };
19562
19563 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19564     
19565     sm: 4,
19566     md: 5,
19567     graphtype: 'bar',
19568     g_height: 250,
19569     g_width: 400,
19570     g_x: 50,
19571     g_y: 50,
19572     g_r: 30,
19573     opts:{
19574         //g_colors: this.colors,
19575         g_type: 'soft',
19576         g_gutter: '20%'
19577
19578     },
19579     title : false,
19580
19581     getAutoCreate : function(){
19582         
19583         var cfg = {
19584             tag: 'div',
19585             html : null
19586         }
19587         
19588         
19589         return  cfg;
19590     },
19591
19592     onRender : function(ct,position){
19593         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19594         this.raphael = Raphael(this.el.dom);
19595         
19596                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19597                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19598                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19599                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19600                 /*
19601                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19602                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19603                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19604                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19605                 
19606                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19607                 r.barchart(330, 10, 300, 220, data1);
19608                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19609                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19610                 */
19611                 
19612                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19613                 // r.barchart(30, 30, 560, 250,  xdata, {
19614                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19615                 //     axis : "0 0 1 1",
19616                 //     axisxlabels :  xdata
19617                 //     //yvalues : cols,
19618                    
19619                 // });
19620 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19621 //        
19622 //        this.load(null,xdata,{
19623 //                axis : "0 0 1 1",
19624 //                axisxlabels :  xdata
19625 //                });
19626
19627     },
19628
19629     load : function(graphtype,xdata,opts){
19630         this.raphael.clear();
19631         if(!graphtype) {
19632             graphtype = this.graphtype;
19633         }
19634         if(!opts){
19635             opts = this.opts;
19636         }
19637         var r = this.raphael,
19638             fin = function () {
19639                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19640             },
19641             fout = function () {
19642                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19643             },
19644             pfin = function() {
19645                 this.sector.stop();
19646                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19647
19648                 if (this.label) {
19649                     this.label[0].stop();
19650                     this.label[0].attr({ r: 7.5 });
19651                     this.label[1].attr({ "font-weight": 800 });
19652                 }
19653             },
19654             pfout = function() {
19655                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19656
19657                 if (this.label) {
19658                     this.label[0].animate({ r: 5 }, 500, "bounce");
19659                     this.label[1].attr({ "font-weight": 400 });
19660                 }
19661             };
19662
19663         switch(graphtype){
19664             case 'bar':
19665                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19666                 break;
19667             case 'hbar':
19668                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19669                 break;
19670             case 'pie':
19671 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19672 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19673 //            
19674                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19675                 
19676                 break;
19677
19678         }
19679         
19680         if(this.title){
19681             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19682         }
19683         
19684     },
19685     
19686     setTitle: function(o)
19687     {
19688         this.title = o;
19689     },
19690     
19691     initEvents: function() {
19692         
19693         if(!this.href){
19694             this.el.on('click', this.onClick, this);
19695         }
19696     },
19697     
19698     onClick : function(e)
19699     {
19700         Roo.log('img onclick');
19701         this.fireEvent('click', this, e);
19702     }
19703    
19704 });
19705
19706  
19707 /*
19708  * - LGPL
19709  *
19710  * numberBox
19711  * 
19712  */
19713 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19714
19715 /**
19716  * @class Roo.bootstrap.dash.NumberBox
19717  * @extends Roo.bootstrap.Component
19718  * Bootstrap NumberBox class
19719  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19720  * @cfg {String} headline Box headline
19721  * @cfg {String} content Box content
19722  * @cfg {String} icon Box icon
19723  * @cfg {String} footer Footer text
19724  * @cfg {String} fhref Footer href
19725  * 
19726  * @constructor
19727  * Create a new NumberBox
19728  * @param {Object} config The config object
19729  */
19730
19731
19732 Roo.bootstrap.dash.NumberBox = function(config){
19733     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19734     
19735 };
19736
19737 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19738     
19739     bgcolor : 'aqua',
19740     headline : '',
19741     content : '',
19742     icon : '',
19743     footer : '',
19744     fhref : '',
19745     ficon : '',
19746     
19747     getAutoCreate : function(){
19748         
19749         var cfg = {
19750             tag : 'div',
19751             cls : 'small-box bg-' + this.bgcolor,
19752             cn : [
19753                 {
19754                     tag : 'div',
19755                     cls : 'inner',
19756                     cn :[
19757                         {
19758                             tag : 'h3',
19759                             cls : 'roo-headline',
19760                             html : this.headline
19761                         },
19762                         {
19763                             tag : 'p',
19764                             cls : 'roo-content',
19765                             html : this.content
19766                         }
19767                     ]
19768                 }
19769             ]
19770         }
19771         
19772         if(this.icon){
19773             cfg.cn.push({
19774                 tag : 'div',
19775                 cls : 'icon',
19776                 cn :[
19777                     {
19778                         tag : 'i',
19779                         cls : 'ion ' + this.icon
19780                     }
19781                 ]
19782             });
19783         }
19784         
19785         if(this.footer){
19786             var footer = {
19787                 tag : 'a',
19788                 cls : 'small-box-footer',
19789                 href : this.fhref || '#',
19790                 html : this.footer
19791             };
19792             
19793             cfg.cn.push(footer);
19794             
19795         }
19796         
19797         return  cfg;
19798     },
19799
19800     onRender : function(ct,position){
19801         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19802
19803
19804        
19805                 
19806     },
19807
19808     setHeadline: function (value)
19809     {
19810         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19811     },
19812     
19813     setFooter: function (value, href)
19814     {
19815         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19816         
19817         if(href){
19818             this.el.select('a.small-box-footer',true).first().attr('href', href);
19819         }
19820         
19821     },
19822
19823     setContent: function (value)
19824     {
19825         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19826     },
19827
19828     initEvents: function() 
19829     {   
19830         
19831     }
19832     
19833 });
19834
19835  
19836 /*
19837  * - LGPL
19838  *
19839  * TabBox
19840  * 
19841  */
19842 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19843
19844 /**
19845  * @class Roo.bootstrap.dash.TabBox
19846  * @extends Roo.bootstrap.Component
19847  * Bootstrap TabBox class
19848  * @cfg {String} title Title of the TabBox
19849  * @cfg {String} icon Icon of the TabBox
19850  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19851  * 
19852  * @constructor
19853  * Create a new TabBox
19854  * @param {Object} config The config object
19855  */
19856
19857
19858 Roo.bootstrap.dash.TabBox = function(config){
19859     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19860     this.addEvents({
19861         // raw events
19862         /**
19863          * @event addpane
19864          * When a pane is added
19865          * @param {Roo.bootstrap.dash.TabPane} pane
19866          */
19867         "addpane" : true
19868          
19869     });
19870 };
19871
19872 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19873
19874     title : '',
19875     icon : false,
19876     showtabs : true,
19877     
19878     getChildContainer : function()
19879     {
19880         return this.el.select('.tab-content', true).first();
19881     },
19882     
19883     getAutoCreate : function(){
19884         
19885         var header = {
19886             tag: 'li',
19887             cls: 'pull-left header',
19888             html: this.title,
19889             cn : []
19890         };
19891         
19892         if(this.icon){
19893             header.cn.push({
19894                 tag: 'i',
19895                 cls: 'fa ' + this.icon
19896             });
19897         }
19898         
19899         
19900         var cfg = {
19901             tag: 'div',
19902             cls: 'nav-tabs-custom',
19903             cn: [
19904                 {
19905                     tag: 'ul',
19906                     cls: 'nav nav-tabs pull-right',
19907                     cn: [
19908                         header
19909                     ]
19910                 },
19911                 {
19912                     tag: 'div',
19913                     cls: 'tab-content no-padding',
19914                     cn: []
19915                 }
19916             ]
19917         }
19918
19919         return  cfg;
19920     },
19921     initEvents : function()
19922     {
19923         //Roo.log('add add pane handler');
19924         this.on('addpane', this.onAddPane, this);
19925     },
19926      /**
19927      * Updates the box title
19928      * @param {String} html to set the title to.
19929      */
19930     setTitle : function(value)
19931     {
19932         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19933     },
19934     onAddPane : function(pane)
19935     {
19936         //Roo.log('addpane');
19937         //Roo.log(pane);
19938         // tabs are rendere left to right..
19939         if(!this.showtabs){
19940             return;
19941         }
19942         
19943         var ctr = this.el.select('.nav-tabs', true).first();
19944          
19945          
19946         var existing = ctr.select('.nav-tab',true);
19947         var qty = existing.getCount();;
19948         
19949         
19950         var tab = ctr.createChild({
19951             tag : 'li',
19952             cls : 'nav-tab' + (qty ? '' : ' active'),
19953             cn : [
19954                 {
19955                     tag : 'a',
19956                     href:'#',
19957                     html : pane.title
19958                 }
19959             ]
19960         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19961         pane.tab = tab;
19962         
19963         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19964         if (!qty) {
19965             pane.el.addClass('active');
19966         }
19967         
19968                 
19969     },
19970     onTabClick : function(ev,un,ob,pane)
19971     {
19972         //Roo.log('tab - prev default');
19973         ev.preventDefault();
19974         
19975         
19976         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19977         pane.tab.addClass('active');
19978         //Roo.log(pane.title);
19979         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19980         // technically we should have a deactivate event.. but maybe add later.
19981         // and it should not de-activate the selected tab...
19982         
19983         pane.el.addClass('active');
19984         pane.fireEvent('activate');
19985         
19986         
19987     }
19988     
19989     
19990 });
19991
19992  
19993 /*
19994  * - LGPL
19995  *
19996  * Tab pane
19997  * 
19998  */
19999 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20000 /**
20001  * @class Roo.bootstrap.TabPane
20002  * @extends Roo.bootstrap.Component
20003  * Bootstrap TabPane class
20004  * @cfg {Boolean} active (false | true) Default false
20005  * @cfg {String} title title of panel
20006
20007  * 
20008  * @constructor
20009  * Create a new TabPane
20010  * @param {Object} config The config object
20011  */
20012
20013 Roo.bootstrap.dash.TabPane = function(config){
20014     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20015     
20016     this.addEvents({
20017         // raw events
20018         /**
20019          * @event activate
20020          * When a pane is activated
20021          * @param {Roo.bootstrap.dash.TabPane} pane
20022          */
20023         "activate" : true
20024          
20025     });
20026 };
20027
20028 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20029     
20030     active : false,
20031     title : '',
20032     
20033     // the tabBox that this is attached to.
20034     tab : false,
20035      
20036     getAutoCreate : function() 
20037     {
20038         var cfg = {
20039             tag: 'div',
20040             cls: 'tab-pane'
20041         }
20042         
20043         if(this.active){
20044             cfg.cls += ' active';
20045         }
20046         
20047         return cfg;
20048     },
20049     initEvents  : function()
20050     {
20051         //Roo.log('trigger add pane handler');
20052         this.parent().fireEvent('addpane', this)
20053     },
20054     
20055      /**
20056      * Updates the tab title 
20057      * @param {String} html to set the title to.
20058      */
20059     setTitle: function(str)
20060     {
20061         if (!this.tab) {
20062             return;
20063         }
20064         this.title = str;
20065         this.tab.select('a', true).first().dom.innerHTML = str;
20066         
20067     }
20068     
20069     
20070     
20071 });
20072
20073  
20074
20075
20076  /*
20077  * - LGPL
20078  *
20079  * menu
20080  * 
20081  */
20082 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20083
20084 /**
20085  * @class Roo.bootstrap.menu.Menu
20086  * @extends Roo.bootstrap.Component
20087  * Bootstrap Menu class - container for Menu
20088  * @cfg {String} html Text of the menu
20089  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20090  * @cfg {String} icon Font awesome icon
20091  * @cfg {String} pos Menu align to (top | bottom) default bottom
20092  * 
20093  * 
20094  * @constructor
20095  * Create a new Menu
20096  * @param {Object} config The config object
20097  */
20098
20099
20100 Roo.bootstrap.menu.Menu = function(config){
20101     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20102     
20103     this.addEvents({
20104         /**
20105          * @event beforeshow
20106          * Fires before this menu is displayed
20107          * @param {Roo.bootstrap.menu.Menu} this
20108          */
20109         beforeshow : true,
20110         /**
20111          * @event beforehide
20112          * Fires before this menu is hidden
20113          * @param {Roo.bootstrap.menu.Menu} this
20114          */
20115         beforehide : true,
20116         /**
20117          * @event show
20118          * Fires after this menu is displayed
20119          * @param {Roo.bootstrap.menu.Menu} this
20120          */
20121         show : true,
20122         /**
20123          * @event hide
20124          * Fires after this menu is hidden
20125          * @param {Roo.bootstrap.menu.Menu} this
20126          */
20127         hide : true,
20128         /**
20129          * @event click
20130          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20131          * @param {Roo.bootstrap.menu.Menu} this
20132          * @param {Roo.EventObject} e
20133          */
20134         click : true
20135     });
20136     
20137 };
20138
20139 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20140     
20141     submenu : false,
20142     html : '',
20143     weight : 'default',
20144     icon : false,
20145     pos : 'bottom',
20146     
20147     
20148     getChildContainer : function() {
20149         if(this.isSubMenu){
20150             return this.el;
20151         }
20152         
20153         return this.el.select('ul.dropdown-menu', true).first();  
20154     },
20155     
20156     getAutoCreate : function()
20157     {
20158         var text = [
20159             {
20160                 tag : 'span',
20161                 cls : 'roo-menu-text',
20162                 html : this.html
20163             }
20164         ];
20165         
20166         if(this.icon){
20167             text.unshift({
20168                 tag : 'i',
20169                 cls : 'fa ' + this.icon
20170             })
20171         }
20172         
20173         
20174         var cfg = {
20175             tag : 'div',
20176             cls : 'btn-group',
20177             cn : [
20178                 {
20179                     tag : 'button',
20180                     cls : 'dropdown-button btn btn-' + this.weight,
20181                     cn : text
20182                 },
20183                 {
20184                     tag : 'button',
20185                     cls : 'dropdown-toggle btn btn-' + this.weight,
20186                     cn : [
20187                         {
20188                             tag : 'span',
20189                             cls : 'caret'
20190                         }
20191                     ]
20192                 },
20193                 {
20194                     tag : 'ul',
20195                     cls : 'dropdown-menu'
20196                 }
20197             ]
20198             
20199         };
20200         
20201         if(this.pos == 'top'){
20202             cfg.cls += ' dropup';
20203         }
20204         
20205         if(this.isSubMenu){
20206             cfg = {
20207                 tag : 'ul',
20208                 cls : 'dropdown-menu'
20209             }
20210         }
20211         
20212         return cfg;
20213     },
20214     
20215     onRender : function(ct, position)
20216     {
20217         this.isSubMenu = ct.hasClass('dropdown-submenu');
20218         
20219         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20220     },
20221     
20222     initEvents : function() 
20223     {
20224         if(this.isSubMenu){
20225             return;
20226         }
20227         
20228         this.hidden = true;
20229         
20230         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20231         this.triggerEl.on('click', this.onTriggerPress, this);
20232         
20233         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20234         this.buttonEl.on('click', this.onClick, this);
20235         
20236     },
20237     
20238     list : function()
20239     {
20240         if(this.isSubMenu){
20241             return this.el;
20242         }
20243         
20244         return this.el.select('ul.dropdown-menu', true).first();
20245     },
20246     
20247     onClick : function(e)
20248     {
20249         this.fireEvent("click", this, e);
20250     },
20251     
20252     onTriggerPress  : function(e)
20253     {   
20254         if (this.isVisible()) {
20255             this.hide();
20256         } else {
20257             this.show();
20258         }
20259     },
20260     
20261     isVisible : function(){
20262         return !this.hidden;
20263     },
20264     
20265     show : function()
20266     {
20267         this.fireEvent("beforeshow", this);
20268         
20269         this.hidden = false;
20270         this.el.addClass('open');
20271         
20272         Roo.get(document).on("mouseup", this.onMouseUp, this);
20273         
20274         this.fireEvent("show", this);
20275         
20276         
20277     },
20278     
20279     hide : function()
20280     {
20281         this.fireEvent("beforehide", this);
20282         
20283         this.hidden = true;
20284         this.el.removeClass('open');
20285         
20286         Roo.get(document).un("mouseup", this.onMouseUp);
20287         
20288         this.fireEvent("hide", this);
20289     },
20290     
20291     onMouseUp : function()
20292     {
20293         this.hide();
20294     }
20295     
20296 });
20297
20298  
20299  /*
20300  * - LGPL
20301  *
20302  * menu item
20303  * 
20304  */
20305 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20306
20307 /**
20308  * @class Roo.bootstrap.menu.Item
20309  * @extends Roo.bootstrap.Component
20310  * Bootstrap MenuItem class
20311  * @cfg {Boolean} submenu (true | false) default false
20312  * @cfg {String} html text of the item
20313  * @cfg {String} href the link
20314  * @cfg {Boolean} disable (true | false) default false
20315  * @cfg {Boolean} preventDefault (true | false) default true
20316  * @cfg {String} icon Font awesome icon
20317  * @cfg {String} pos Submenu align to (left | right) default right 
20318  * 
20319  * 
20320  * @constructor
20321  * Create a new Item
20322  * @param {Object} config The config object
20323  */
20324
20325
20326 Roo.bootstrap.menu.Item = function(config){
20327     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20328     this.addEvents({
20329         /**
20330          * @event mouseover
20331          * Fires when the mouse is hovering over this menu
20332          * @param {Roo.bootstrap.menu.Item} this
20333          * @param {Roo.EventObject} e
20334          */
20335         mouseover : true,
20336         /**
20337          * @event mouseout
20338          * Fires when the mouse exits this menu
20339          * @param {Roo.bootstrap.menu.Item} this
20340          * @param {Roo.EventObject} e
20341          */
20342         mouseout : true,
20343         // raw events
20344         /**
20345          * @event click
20346          * The raw click event for the entire grid.
20347          * @param {Roo.EventObject} e
20348          */
20349         click : true
20350     });
20351 };
20352
20353 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20354     
20355     submenu : false,
20356     href : '',
20357     html : '',
20358     preventDefault: true,
20359     disable : false,
20360     icon : false,
20361     pos : 'right',
20362     
20363     getAutoCreate : function()
20364     {
20365         var text = [
20366             {
20367                 tag : 'span',
20368                 cls : 'roo-menu-item-text',
20369                 html : this.html
20370             }
20371         ];
20372         
20373         if(this.icon){
20374             text.unshift({
20375                 tag : 'i',
20376                 cls : 'fa ' + this.icon
20377             })
20378         }
20379         
20380         var cfg = {
20381             tag : 'li',
20382             cn : [
20383                 {
20384                     tag : 'a',
20385                     href : this.href || '#',
20386                     cn : text
20387                 }
20388             ]
20389         };
20390         
20391         if(this.disable){
20392             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20393         }
20394         
20395         if(this.submenu){
20396             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20397             
20398             if(this.pos == 'left'){
20399                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20400             }
20401         }
20402         
20403         return cfg;
20404     },
20405     
20406     initEvents : function() 
20407     {
20408         this.el.on('mouseover', this.onMouseOver, this);
20409         this.el.on('mouseout', this.onMouseOut, this);
20410         
20411         this.el.select('a', true).first().on('click', this.onClick, this);
20412         
20413     },
20414     
20415     onClick : function(e)
20416     {
20417         if(this.preventDefault){
20418             e.preventDefault();
20419         }
20420         
20421         this.fireEvent("click", this, e);
20422     },
20423     
20424     onMouseOver : function(e)
20425     {
20426         if(this.submenu && this.pos == 'left'){
20427             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20428         }
20429         
20430         this.fireEvent("mouseover", this, e);
20431     },
20432     
20433     onMouseOut : function(e)
20434     {
20435         this.fireEvent("mouseout", this, e);
20436     }
20437 });
20438
20439  
20440
20441  /*
20442  * - LGPL
20443  *
20444  * menu separator
20445  * 
20446  */
20447 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20448
20449 /**
20450  * @class Roo.bootstrap.menu.Separator
20451  * @extends Roo.bootstrap.Component
20452  * Bootstrap Separator class
20453  * 
20454  * @constructor
20455  * Create a new Separator
20456  * @param {Object} config The config object
20457  */
20458
20459
20460 Roo.bootstrap.menu.Separator = function(config){
20461     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20462 };
20463
20464 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20465     
20466     getAutoCreate : function(){
20467         var cfg = {
20468             tag : 'li',
20469             cls: 'divider'
20470         };
20471         
20472         return cfg;
20473     }
20474    
20475 });
20476
20477  
20478
20479