roojs-bootstrap.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(t);
1714        if(t.isContainer){
1715            return;
1716        }
1717        
1718         Roo.log('pass click event');
1719         
1720         t.onClick(e);
1721         
1722         this.fireEvent("click", this, t, e);
1723         
1724         this.hide();
1725     },
1726      onMouseOver : function(e){
1727         var t  = this.findTargetItem(e);
1728         //Roo.log(t);
1729         //if(t){
1730         //    if(t.canActivate && !t.disabled){
1731         //        this.setActiveItem(t, true);
1732         //    }
1733         //}
1734         
1735         this.fireEvent("mouseover", this, e, t);
1736     },
1737     isVisible : function(){
1738         return !this.hidden;
1739     },
1740      onMouseOut : function(e){
1741         var t  = this.findTargetItem(e);
1742         
1743         //if(t ){
1744         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1745         //        this.activeItem.deactivate();
1746         //        delete this.activeItem;
1747         //    }
1748         //}
1749         this.fireEvent("mouseout", this, e, t);
1750     },
1751     
1752     
1753     /**
1754      * Displays this menu relative to another element
1755      * @param {String/HTMLElement/Roo.Element} element The element to align to
1756      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1757      * the element (defaults to this.defaultAlign)
1758      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1759      */
1760     show : function(el, pos, parentMenu){
1761         this.parentMenu = parentMenu;
1762         if(!this.el){
1763             this.render();
1764         }
1765         this.fireEvent("beforeshow", this);
1766         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1767     },
1768      /**
1769      * Displays this menu at a specific xy position
1770      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1771      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1772      */
1773     showAt : function(xy, parentMenu, /* private: */_e){
1774         this.parentMenu = parentMenu;
1775         if(!this.el){
1776             this.render();
1777         }
1778         if(_e !== false){
1779             this.fireEvent("beforeshow", this);
1780             
1781             //xy = this.el.adjustForConstraints(xy);
1782         }
1783         //this.el.setXY(xy);
1784         //this.el.show();
1785         this.hideMenuItems();
1786         this.hidden = false;
1787         this.triggerEl.addClass('open');
1788         this.focus();
1789         this.fireEvent("show", this);
1790     },
1791     
1792     focus : function(){
1793         return;
1794         if(!this.hidden){
1795             this.doFocus.defer(50, this);
1796         }
1797     },
1798
1799     doFocus : function(){
1800         if(!this.hidden){
1801             this.focusEl.focus();
1802         }
1803     },
1804
1805     /**
1806      * Hides this menu and optionally all parent menus
1807      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1808      */
1809     hide : function(deep){
1810         
1811         this.hideMenuItems();
1812         if(this.el && this.isVisible()){
1813             this.fireEvent("beforehide", this);
1814             if(this.activeItem){
1815                 this.activeItem.deactivate();
1816                 this.activeItem = null;
1817             }
1818             this.triggerEl.removeClass('open');;
1819             this.hidden = true;
1820             this.fireEvent("hide", this);
1821         }
1822         if(deep === true && this.parentMenu){
1823             this.parentMenu.hide(true);
1824         }
1825     },
1826     
1827     onTriggerPress  : function(e)
1828     {
1829         
1830         Roo.log('trigger press');
1831         //Roo.log(e.getTarget());
1832        // Roo.log(this.triggerEl.dom);
1833         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1834             return;
1835         }
1836         if (this.isVisible()) {
1837             Roo.log('hide');
1838             this.hide();
1839         } else {
1840             this.show(this.triggerEl, false, false);
1841         }
1842         
1843         
1844     },
1845     
1846          
1847        
1848     
1849     hideMenuItems : function()
1850     {
1851         //$(backdrop).remove()
1852         Roo.select('.open',true).each(function(aa) {
1853             
1854             aa.removeClass('open');
1855           //var parent = getParent($(this))
1856           //var relatedTarget = { relatedTarget: this }
1857           
1858            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1859           //if (e.isDefaultPrevented()) return
1860            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1861         })
1862     },
1863     addxtypeChild : function (tree, cntr) {
1864         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1865           
1866         this.menuitems.add(comp);
1867         return comp;
1868
1869     },
1870     getEl : function()
1871     {
1872         Roo.log(this.el);
1873         return this.el;
1874     }
1875 });
1876
1877  
1878  /*
1879  * - LGPL
1880  *
1881  * menu item
1882  * 
1883  */
1884
1885
1886 /**
1887  * @class Roo.bootstrap.MenuItem
1888  * @extends Roo.bootstrap.Component
1889  * Bootstrap MenuItem class
1890  * @cfg {String} html the menu label
1891  * @cfg {String} href the link
1892  * @cfg {Boolean} preventDefault (true | false) default true
1893  * @cfg {Boolean} isContainer (true | false) default false
1894  * 
1895  * 
1896  * @constructor
1897  * Create a new MenuItem
1898  * @param {Object} config The config object
1899  */
1900
1901
1902 Roo.bootstrap.MenuItem = function(config){
1903     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1904     this.addEvents({
1905         // raw events
1906         /**
1907          * @event click
1908          * The raw click event for the entire grid.
1909          * @param {Roo.EventObject} e
1910          */
1911         "click" : true
1912     });
1913 };
1914
1915 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1916     
1917     href : false,
1918     html : false,
1919     preventDefault: true,
1920     isContainer : false,
1921     
1922     getAutoCreate : function(){
1923         
1924         if(this.isContainer){
1925             return {
1926                 tag: 'li',
1927                 cls: 'dropdown-menu-item'
1928             };
1929         }
1930         
1931         var cfg= {
1932             tag: 'li',
1933             cls: 'dropdown-menu-item',
1934             cn: [
1935                     {
1936                         tag : 'a',
1937                         href : '#',
1938                         html : 'Link'
1939                     }
1940                 ]
1941         };
1942         if (this.parent().type == 'treeview') {
1943             cfg.cls = 'treeview-menu';
1944         }
1945         
1946         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1947         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1948         return cfg;
1949     },
1950     
1951     initEvents: function() {
1952         
1953         //this.el.select('a').on('click', this.onClick, this);
1954         
1955     },
1956     onClick : function(e)
1957     {
1958         Roo.log('item on click ');
1959         //if(this.preventDefault){
1960         //    e.preventDefault();
1961         //}
1962         //this.parent().hideMenuItems();
1963         
1964         this.fireEvent('click', this, e);
1965     },
1966     getEl : function()
1967     {
1968         return this.el;
1969     }
1970 });
1971
1972  
1973
1974  /*
1975  * - LGPL
1976  *
1977  * menu separator
1978  * 
1979  */
1980
1981
1982 /**
1983  * @class Roo.bootstrap.MenuSeparator
1984  * @extends Roo.bootstrap.Component
1985  * Bootstrap MenuSeparator class
1986  * 
1987  * @constructor
1988  * Create a new MenuItem
1989  * @param {Object} config The config object
1990  */
1991
1992
1993 Roo.bootstrap.MenuSeparator = function(config){
1994     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1995 };
1996
1997 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1998     
1999     getAutoCreate : function(){
2000         var cfg = {
2001             cls: 'divider',
2002             tag : 'li'
2003         };
2004         
2005         return cfg;
2006     }
2007    
2008 });
2009
2010  
2011
2012  
2013 /*
2014 <div class="modal fade">
2015   <div class="modal-dialog">
2016     <div class="modal-content">
2017       <div class="modal-header">
2018         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2019         <h4 class="modal-title">Modal title</h4>
2020       </div>
2021       <div class="modal-body">
2022         <p>One fine body&hellip;</p>
2023       </div>
2024       <div class="modal-footer">
2025         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2026         <button type="button" class="btn btn-primary">Save changes</button>
2027       </div>
2028     </div><!-- /.modal-content -->
2029   </div><!-- /.modal-dialog -->
2030 </div><!-- /.modal -->
2031 */
2032 /*
2033  * - LGPL
2034  *
2035  * page contgainer.
2036  * 
2037  */
2038
2039 /**
2040  * @class Roo.bootstrap.Modal
2041  * @extends Roo.bootstrap.Component
2042  * Bootstrap Modal class
2043  * @cfg {String} title Title of dialog
2044  * @cfg {Boolean} specificTitle (true|false) default false
2045  * @cfg {Array} buttons Array of buttons or standard button set..
2046  * @cfg {String} buttonPosition (left|right|center) default right
2047  * @cfg {Boolean} animate (true | false) default true
2048  * 
2049  * @constructor
2050  * Create a new Modal Dialog
2051  * @param {Object} config The config object
2052  */
2053
2054 Roo.bootstrap.Modal = function(config){
2055     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2056     this.addEvents({
2057         // raw events
2058         /**
2059          * @event btnclick
2060          * The raw btnclick event for the button
2061          * @param {Roo.EventObject} e
2062          */
2063         "btnclick" : true
2064     });
2065     this.buttons = this.buttons || [];
2066 };
2067
2068 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2069     
2070     title : 'test dialog',
2071    
2072     buttons : false,
2073     
2074     // set on load...
2075     body:  false,
2076     
2077     specificTitle: false,
2078     
2079     buttonPosition: 'right',
2080     
2081     animate : true,
2082     
2083     onRender : function(ct, position)
2084     {
2085         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2086      
2087         if(!this.el){
2088             var cfg = Roo.apply({},  this.getAutoCreate());
2089             cfg.id = Roo.id();
2090             //if(!cfg.name){
2091             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2092             //}
2093             //if (!cfg.name.length) {
2094             //    delete cfg.name;
2095            // }
2096             if (this.cls) {
2097                 cfg.cls += ' ' + this.cls;
2098             }
2099             if (this.style) {
2100                 cfg.style = this.style;
2101             }
2102             this.el = Roo.get(document.body).createChild(cfg, position);
2103         }
2104         //var type = this.el.dom.type;
2105         
2106         if(this.tabIndex !== undefined){
2107             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2108         }
2109         
2110         
2111         
2112         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2113         this.maskEl.enableDisplayMode("block");
2114         this.maskEl.hide();
2115         //this.el.addClass("x-dlg-modal");
2116     
2117         if (this.buttons.length) {
2118             Roo.each(this.buttons, function(bb) {
2119                 b = Roo.apply({}, bb);
2120                 b.xns = b.xns || Roo.bootstrap;
2121                 b.xtype = b.xtype || 'Button';
2122                 if (typeof(b.listeners) == 'undefined') {
2123                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2124                 }
2125                 
2126                 var btn = Roo.factory(b);
2127                 
2128                 btn.onRender(this.el.select('.modal-footer div').first());
2129                 
2130             },this);
2131         }
2132         // render the children.
2133         var nitems = [];
2134         
2135         if(typeof(this.items) != 'undefined'){
2136             var items = this.items;
2137             delete this.items;
2138
2139             for(var i =0;i < items.length;i++) {
2140                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2141             }
2142         }
2143         
2144         this.items = nitems;
2145         
2146         this.body = this.el.select('.modal-body',true).first();
2147         this.close = this.el.select('.modal-header .close', true).first();
2148         this.footer = this.el.select('.modal-footer',true).first();
2149         this.initEvents();
2150         //this.el.addClass([this.fieldClass, this.cls]);
2151         
2152     },
2153     getAutoCreate : function(){
2154         
2155         
2156         var bdy = {
2157                 cls : 'modal-body',
2158                 html : this.html || ''
2159         };
2160         
2161         var title = {
2162             tag: 'h4',
2163             cls : 'modal-title',
2164             html : this.title
2165         };
2166         
2167         if(this.specificTitle){
2168             title = this.title;
2169         };
2170         
2171         var modal = {
2172             cls: "modal",
2173             style : 'display: none',
2174             cn : [
2175                 {
2176                     cls: "modal-dialog",
2177                     cn : [
2178                         {
2179                             cls : "modal-content",
2180                             cn : [
2181                                 {
2182                                     cls : 'modal-header',
2183                                     cn : [
2184                                         {
2185                                             tag: 'button',
2186                                             cls : 'close',
2187                                             html : '&times'
2188                                         },
2189                                         title
2190                                     ]
2191                                 },
2192                                 bdy,
2193                                 {
2194                                     cls : 'modal-footer',
2195                                     cn : [
2196                                         {
2197                                             tag: 'div',
2198                                             cls: 'btn-' + this.buttonPosition
2199                                         }
2200                                     ]
2201                                     
2202                                 }
2203                                 
2204                                 
2205                             ]
2206                             
2207                         }
2208                     ]
2209                         
2210                 }
2211             ]
2212         };
2213         
2214         if(this.animate){
2215             modal.cls += ' fade';
2216         }
2217         
2218         return modal;
2219           
2220     },
2221     getChildContainer : function() {
2222          
2223          return this.el.select('.modal-body',true).first();
2224         
2225     },
2226     getButtonContainer : function() {
2227          return this.el.select('.modal-footer div',true).first();
2228         
2229     },
2230     initEvents : function()
2231     {
2232         this.el.select('.modal-header .close').on('click', this.hide, this);
2233 //        
2234 //        this.addxtype(this);
2235     },
2236     show : function() {
2237         
2238         if (!this.rendered) {
2239             this.render();
2240         }
2241         
2242         this.el.setStyle('display', 'block');
2243         
2244         if(this.animate){
2245             var _this = this;
2246             (function(){ _this.el.addClass('in'); }).defer(50);
2247         }else{
2248             this.el.addClass('in');
2249         }
2250         
2251         Roo.get(document.body).addClass("x-body-masked");
2252         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2253         this.maskEl.show();
2254         this.el.setStyle('zIndex', '10001');
2255         this.fireEvent('show', this);
2256         
2257         
2258     },
2259     hide : function()
2260     {
2261         this.maskEl.hide();
2262         Roo.get(document.body).removeClass("x-body-masked");
2263         this.el.removeClass('in');
2264         
2265         if(this.animate){
2266             var _this = this;
2267             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2268         }else{
2269             this.el.setStyle('display', 'none');
2270         }
2271         
2272         this.fireEvent('hide', this);
2273     },
2274     
2275     addButton : function(str, cb)
2276     {
2277          
2278         
2279         var b = Roo.apply({}, { html : str } );
2280         b.xns = b.xns || Roo.bootstrap;
2281         b.xtype = b.xtype || 'Button';
2282         if (typeof(b.listeners) == 'undefined') {
2283             b.listeners = { click : cb.createDelegate(this)  };
2284         }
2285         
2286         var btn = Roo.factory(b);
2287            
2288         btn.onRender(this.el.select('.modal-footer div').first());
2289         
2290         return btn;   
2291        
2292     },
2293     
2294     setDefaultButton : function(btn)
2295     {
2296         //this.el.select('.modal-footer').()
2297     },
2298     resizeTo: function(w,h)
2299     {
2300         // skip..
2301     },
2302     setContentSize  : function(w, h)
2303     {
2304         
2305     },
2306     onButtonClick: function(btn,e)
2307     {
2308         //Roo.log([a,b,c]);
2309         this.fireEvent('btnclick', btn.name, e);
2310     },
2311     setTitle: function(str) {
2312         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2313         
2314     }
2315 });
2316
2317
2318 Roo.apply(Roo.bootstrap.Modal,  {
2319     /**
2320          * Button config that displays a single OK button
2321          * @type Object
2322          */
2323         OK :  [{
2324             name : 'ok',
2325             weight : 'primary',
2326             html : 'OK'
2327         }], 
2328         /**
2329          * Button config that displays Yes and No buttons
2330          * @type Object
2331          */
2332         YESNO : [
2333             {
2334                 name  : 'no',
2335                 html : 'No'
2336             },
2337             {
2338                 name  :'yes',
2339                 weight : 'primary',
2340                 html : 'Yes'
2341             }
2342         ],
2343         
2344         /**
2345          * Button config that displays OK and Cancel buttons
2346          * @type Object
2347          */
2348         OKCANCEL : [
2349             {
2350                name : 'cancel',
2351                 html : 'Cancel'
2352             },
2353             {
2354                 name : 'ok',
2355                 weight : 'primary',
2356                 html : 'OK'
2357             }
2358         ],
2359         /**
2360          * Button config that displays Yes, No and Cancel buttons
2361          * @type Object
2362          */
2363         YESNOCANCEL : [
2364             {
2365                 name : 'yes',
2366                 weight : 'primary',
2367                 html : 'Yes'
2368             },
2369             {
2370                 name : 'no',
2371                 html : 'No'
2372             },
2373             {
2374                 name : 'cancel',
2375                 html : 'Cancel'
2376             }
2377         ]
2378 });
2379  /*
2380  * - LGPL
2381  *
2382  * messagebox - can be used as a replace
2383  * 
2384  */
2385 /**
2386  * @class Roo.MessageBox
2387  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2388  * Example usage:
2389  *<pre><code>
2390 // Basic alert:
2391 Roo.Msg.alert('Status', 'Changes saved successfully.');
2392
2393 // Prompt for user data:
2394 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2395     if (btn == 'ok'){
2396         // process text value...
2397     }
2398 });
2399
2400 // Show a dialog using config options:
2401 Roo.Msg.show({
2402    title:'Save Changes?',
2403    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2404    buttons: Roo.Msg.YESNOCANCEL,
2405    fn: processResult,
2406    animEl: 'elId'
2407 });
2408 </code></pre>
2409  * @singleton
2410  */
2411 Roo.bootstrap.MessageBox = function(){
2412     var dlg, opt, mask, waitTimer;
2413     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2414     var buttons, activeTextEl, bwidth;
2415
2416     
2417     // private
2418     var handleButton = function(button){
2419         dlg.hide();
2420         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2421     };
2422
2423     // private
2424     var handleHide = function(){
2425         if(opt && opt.cls){
2426             dlg.el.removeClass(opt.cls);
2427         }
2428         //if(waitTimer){
2429         //    Roo.TaskMgr.stop(waitTimer);
2430         //    waitTimer = null;
2431         //}
2432     };
2433
2434     // private
2435     var updateButtons = function(b){
2436         var width = 0;
2437         if(!b){
2438             buttons["ok"].hide();
2439             buttons["cancel"].hide();
2440             buttons["yes"].hide();
2441             buttons["no"].hide();
2442             //dlg.footer.dom.style.display = 'none';
2443             return width;
2444         }
2445         dlg.footer.dom.style.display = '';
2446         for(var k in buttons){
2447             if(typeof buttons[k] != "function"){
2448                 if(b[k]){
2449                     buttons[k].show();
2450                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2451                     width += buttons[k].el.getWidth()+15;
2452                 }else{
2453                     buttons[k].hide();
2454                 }
2455             }
2456         }
2457         return width;
2458     };
2459
2460     // private
2461     var handleEsc = function(d, k, e){
2462         if(opt && opt.closable !== false){
2463             dlg.hide();
2464         }
2465         if(e){
2466             e.stopEvent();
2467         }
2468     };
2469
2470     return {
2471         /**
2472          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2473          * @return {Roo.BasicDialog} The BasicDialog element
2474          */
2475         getDialog : function(){
2476            if(!dlg){
2477                 dlg = new Roo.bootstrap.Modal( {
2478                     //draggable: true,
2479                     //resizable:false,
2480                     //constraintoviewport:false,
2481                     //fixedcenter:true,
2482                     //collapsible : false,
2483                     //shim:true,
2484                     //modal: true,
2485                   //  width:400,
2486                   //  height:100,
2487                     //buttonAlign:"center",
2488                     closeClick : function(){
2489                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2490                             handleButton("no");
2491                         }else{
2492                             handleButton("cancel");
2493                         }
2494                     }
2495                 });
2496                 dlg.render();
2497                 dlg.on("hide", handleHide);
2498                 mask = dlg.mask;
2499                 //dlg.addKeyListener(27, handleEsc);
2500                 buttons = {};
2501                 this.buttons = buttons;
2502                 var bt = this.buttonText;
2503                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2504                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2505                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2506                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2507                 Roo.log(buttons)
2508                 bodyEl = dlg.body.createChild({
2509
2510                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2511                         '<textarea class="roo-mb-textarea"></textarea>' +
2512                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2513                 });
2514                 msgEl = bodyEl.dom.firstChild;
2515                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2516                 textboxEl.enableDisplayMode();
2517                 textboxEl.addKeyListener([10,13], function(){
2518                     if(dlg.isVisible() && opt && opt.buttons){
2519                         if(opt.buttons.ok){
2520                             handleButton("ok");
2521                         }else if(opt.buttons.yes){
2522                             handleButton("yes");
2523                         }
2524                     }
2525                 });
2526                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2527                 textareaEl.enableDisplayMode();
2528                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2529                 progressEl.enableDisplayMode();
2530                 var pf = progressEl.dom.firstChild;
2531                 if (pf) {
2532                     pp = Roo.get(pf.firstChild);
2533                     pp.setHeight(pf.offsetHeight);
2534                 }
2535                 
2536             }
2537             return dlg;
2538         },
2539
2540         /**
2541          * Updates the message box body text
2542          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2543          * the XHTML-compliant non-breaking space character '&amp;#160;')
2544          * @return {Roo.MessageBox} This message box
2545          */
2546         updateText : function(text){
2547             if(!dlg.isVisible() && !opt.width){
2548                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2549             }
2550             msgEl.innerHTML = text || '&#160;';
2551       
2552             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2553             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2554             var w = Math.max(
2555                     Math.min(opt.width || cw , this.maxWidth), 
2556                     Math.max(opt.minWidth || this.minWidth, bwidth)
2557             );
2558             if(opt.prompt){
2559                 activeTextEl.setWidth(w);
2560             }
2561             if(dlg.isVisible()){
2562                 dlg.fixedcenter = false;
2563             }
2564             // to big, make it scroll. = But as usual stupid IE does not support
2565             // !important..
2566             
2567             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2568                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2569                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2570             } else {
2571                 bodyEl.dom.style.height = '';
2572                 bodyEl.dom.style.overflowY = '';
2573             }
2574             if (cw > w) {
2575                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2576             } else {
2577                 bodyEl.dom.style.overflowX = '';
2578             }
2579             
2580             dlg.setContentSize(w, bodyEl.getHeight());
2581             if(dlg.isVisible()){
2582                 dlg.fixedcenter = true;
2583             }
2584             return this;
2585         },
2586
2587         /**
2588          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2589          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2590          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2591          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2592          * @return {Roo.MessageBox} This message box
2593          */
2594         updateProgress : function(value, text){
2595             if(text){
2596                 this.updateText(text);
2597             }
2598             if (pp) { // weird bug on my firefox - for some reason this is not defined
2599                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2600             }
2601             return this;
2602         },        
2603
2604         /**
2605          * Returns true if the message box is currently displayed
2606          * @return {Boolean} True if the message box is visible, else false
2607          */
2608         isVisible : function(){
2609             return dlg && dlg.isVisible();  
2610         },
2611
2612         /**
2613          * Hides the message box if it is displayed
2614          */
2615         hide : function(){
2616             if(this.isVisible()){
2617                 dlg.hide();
2618             }  
2619         },
2620
2621         /**
2622          * Displays a new message box, or reinitializes an existing message box, based on the config options
2623          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2624          * The following config object properties are supported:
2625          * <pre>
2626 Property    Type             Description
2627 ----------  ---------------  ------------------------------------------------------------------------------------
2628 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2629                                    closes (defaults to undefined)
2630 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2631                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2632 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2633                                    progress and wait dialogs will ignore this property and always hide the
2634                                    close button as they can only be closed programmatically.
2635 cls               String           A custom CSS class to apply to the message box element
2636 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2637                                    displayed (defaults to 75)
2638 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2639                                    function will be btn (the name of the button that was clicked, if applicable,
2640                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2641                                    Progress and wait dialogs will ignore this option since they do not respond to
2642                                    user actions and can only be closed programmatically, so any required function
2643                                    should be called by the same code after it closes the dialog.
2644 icon              String           A CSS class that provides a background image to be used as an icon for
2645                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2646 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2647 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2648 modal             Boolean          False to allow user interaction with the page while the message box is
2649                                    displayed (defaults to true)
2650 msg               String           A string that will replace the existing message box body text (defaults
2651                                    to the XHTML-compliant non-breaking space character '&#160;')
2652 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2653 progress          Boolean          True to display a progress bar (defaults to false)
2654 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2655 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2656 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2657 title             String           The title text
2658 value             String           The string value to set into the active textbox element if displayed
2659 wait              Boolean          True to display a progress bar (defaults to false)
2660 width             Number           The width of the dialog in pixels
2661 </pre>
2662          *
2663          * Example usage:
2664          * <pre><code>
2665 Roo.Msg.show({
2666    title: 'Address',
2667    msg: 'Please enter your address:',
2668    width: 300,
2669    buttons: Roo.MessageBox.OKCANCEL,
2670    multiline: true,
2671    fn: saveAddress,
2672    animEl: 'addAddressBtn'
2673 });
2674 </code></pre>
2675          * @param {Object} config Configuration options
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         show : function(options)
2679         {
2680             
2681             // this causes nightmares if you show one dialog after another
2682             // especially on callbacks..
2683              
2684             if(this.isVisible()){
2685                 
2686                 this.hide();
2687                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2688                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2689                 Roo.log("New Dialog Message:" +  options.msg )
2690                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2691                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2692                 
2693             }
2694             var d = this.getDialog();
2695             opt = options;
2696             d.setTitle(opt.title || "&#160;");
2697             d.close.setDisplayed(opt.closable !== false);
2698             activeTextEl = textboxEl;
2699             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2700             if(opt.prompt){
2701                 if(opt.multiline){
2702                     textboxEl.hide();
2703                     textareaEl.show();
2704                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2705                         opt.multiline : this.defaultTextHeight);
2706                     activeTextEl = textareaEl;
2707                 }else{
2708                     textboxEl.show();
2709                     textareaEl.hide();
2710                 }
2711             }else{
2712                 textboxEl.hide();
2713                 textareaEl.hide();
2714             }
2715             progressEl.setDisplayed(opt.progress === true);
2716             this.updateProgress(0);
2717             activeTextEl.dom.value = opt.value || "";
2718             if(opt.prompt){
2719                 dlg.setDefaultButton(activeTextEl);
2720             }else{
2721                 var bs = opt.buttons;
2722                 var db = null;
2723                 if(bs && bs.ok){
2724                     db = buttons["ok"];
2725                 }else if(bs && bs.yes){
2726                     db = buttons["yes"];
2727                 }
2728                 dlg.setDefaultButton(db);
2729             }
2730             bwidth = updateButtons(opt.buttons);
2731             this.updateText(opt.msg);
2732             if(opt.cls){
2733                 d.el.addClass(opt.cls);
2734             }
2735             d.proxyDrag = opt.proxyDrag === true;
2736             d.modal = opt.modal !== false;
2737             d.mask = opt.modal !== false ? mask : false;
2738             if(!d.isVisible()){
2739                 // force it to the end of the z-index stack so it gets a cursor in FF
2740                 document.body.appendChild(dlg.el.dom);
2741                 d.animateTarget = null;
2742                 d.show(options.animEl);
2743             }
2744             return this;
2745         },
2746
2747         /**
2748          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2749          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2750          * and closing the message box when the process is complete.
2751          * @param {String} title The title bar text
2752          * @param {String} msg The message box body text
2753          * @return {Roo.MessageBox} This message box
2754          */
2755         progress : function(title, msg){
2756             this.show({
2757                 title : title,
2758                 msg : msg,
2759                 buttons: false,
2760                 progress:true,
2761                 closable:false,
2762                 minWidth: this.minProgressWidth,
2763                 modal : true
2764             });
2765             return this;
2766         },
2767
2768         /**
2769          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2770          * If a callback function is passed it will be called after the user clicks the button, and the
2771          * id of the button that was clicked will be passed as the only parameter to the callback
2772          * (could also be the top-right close button).
2773          * @param {String} title The title bar text
2774          * @param {String} msg The message box body text
2775          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2776          * @param {Object} scope (optional) The scope of the callback function
2777          * @return {Roo.MessageBox} This message box
2778          */
2779         alert : function(title, msg, fn, scope){
2780             this.show({
2781                 title : title,
2782                 msg : msg,
2783                 buttons: this.OK,
2784                 fn: fn,
2785                 scope : scope,
2786                 modal : true
2787             });
2788             return this;
2789         },
2790
2791         /**
2792          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2793          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2794          * You are responsible for closing the message box when the process is complete.
2795          * @param {String} msg The message box body text
2796          * @param {String} title (optional) The title bar text
2797          * @return {Roo.MessageBox} This message box
2798          */
2799         wait : function(msg, title){
2800             this.show({
2801                 title : title,
2802                 msg : msg,
2803                 buttons: false,
2804                 closable:false,
2805                 progress:true,
2806                 modal:true,
2807                 width:300,
2808                 wait:true
2809             });
2810             waitTimer = Roo.TaskMgr.start({
2811                 run: function(i){
2812                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2813                 },
2814                 interval: 1000
2815             });
2816             return this;
2817         },
2818
2819         /**
2820          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2821          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2822          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2823          * @param {String} title The title bar text
2824          * @param {String} msg The message box body text
2825          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2826          * @param {Object} scope (optional) The scope of the callback function
2827          * @return {Roo.MessageBox} This message box
2828          */
2829         confirm : function(title, msg, fn, scope){
2830             this.show({
2831                 title : title,
2832                 msg : msg,
2833                 buttons: this.YESNO,
2834                 fn: fn,
2835                 scope : scope,
2836                 modal : true
2837             });
2838             return this;
2839         },
2840
2841         /**
2842          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2843          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2844          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2845          * (could also be the top-right close button) and the text that was entered will be passed as the two
2846          * parameters to the callback.
2847          * @param {String} title The title bar text
2848          * @param {String} msg The message box body text
2849          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2850          * @param {Object} scope (optional) The scope of the callback function
2851          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2852          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2853          * @return {Roo.MessageBox} This message box
2854          */
2855         prompt : function(title, msg, fn, scope, multiline){
2856             this.show({
2857                 title : title,
2858                 msg : msg,
2859                 buttons: this.OKCANCEL,
2860                 fn: fn,
2861                 minWidth:250,
2862                 scope : scope,
2863                 prompt:true,
2864                 multiline: multiline,
2865                 modal : true
2866             });
2867             return this;
2868         },
2869
2870         /**
2871          * Button config that displays a single OK button
2872          * @type Object
2873          */
2874         OK : {ok:true},
2875         /**
2876          * Button config that displays Yes and No buttons
2877          * @type Object
2878          */
2879         YESNO : {yes:true, no:true},
2880         /**
2881          * Button config that displays OK and Cancel buttons
2882          * @type Object
2883          */
2884         OKCANCEL : {ok:true, cancel:true},
2885         /**
2886          * Button config that displays Yes, No and Cancel buttons
2887          * @type Object
2888          */
2889         YESNOCANCEL : {yes:true, no:true, cancel:true},
2890
2891         /**
2892          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2893          * @type Number
2894          */
2895         defaultTextHeight : 75,
2896         /**
2897          * The maximum width in pixels of the message box (defaults to 600)
2898          * @type Number
2899          */
2900         maxWidth : 600,
2901         /**
2902          * The minimum width in pixels of the message box (defaults to 100)
2903          * @type Number
2904          */
2905         minWidth : 100,
2906         /**
2907          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2908          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2909          * @type Number
2910          */
2911         minProgressWidth : 250,
2912         /**
2913          * An object containing the default button text strings that can be overriden for localized language support.
2914          * Supported properties are: ok, cancel, yes and no.
2915          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2916          * @type Object
2917          */
2918         buttonText : {
2919             ok : "OK",
2920             cancel : "Cancel",
2921             yes : "Yes",
2922             no : "No"
2923         }
2924     };
2925 }();
2926
2927 /**
2928  * Shorthand for {@link Roo.MessageBox}
2929  */
2930 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2931 Roo.Msg = Roo.Msg || Roo.MessageBox;
2932 /*
2933  * - LGPL
2934  *
2935  * navbar
2936  * 
2937  */
2938
2939 /**
2940  * @class Roo.bootstrap.Navbar
2941  * @extends Roo.bootstrap.Component
2942  * Bootstrap Navbar class
2943
2944  * @constructor
2945  * Create a new Navbar
2946  * @param {Object} config The config object
2947  */
2948
2949
2950 Roo.bootstrap.Navbar = function(config){
2951     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2952     
2953 };
2954
2955 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2956     
2957     
2958    
2959     // private
2960     navItems : false,
2961     loadMask : false,
2962     
2963     
2964     getAutoCreate : function(){
2965         
2966         
2967         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2968         
2969     },
2970     
2971     initEvents :function ()
2972     {
2973         //Roo.log(this.el.select('.navbar-toggle',true));
2974         this.el.select('.navbar-toggle',true).on('click', function() {
2975            // Roo.log('click');
2976             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2977         }, this);
2978         
2979         var mark = {
2980             tag: "div",
2981             cls:"x-dlg-mask"
2982         }
2983         
2984         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2985         
2986         var size = this.el.getSize();
2987         this.maskEl.setSize(size.width, size.height);
2988         this.maskEl.enableDisplayMode("block");
2989         this.maskEl.hide();
2990         
2991         if(this.loadMask){
2992             this.maskEl.show();
2993         }
2994     },
2995     
2996     
2997     getChildContainer : function()
2998     {
2999         if (this.el.select('.collapse').getCount()) {
3000             return this.el.select('.collapse',true).first();
3001         }
3002         
3003         return this.el;
3004     },
3005     
3006     mask : function()
3007     {
3008         this.maskEl.show();
3009     },
3010     
3011     unmask : function()
3012     {
3013         this.maskEl.hide();
3014     } 
3015     
3016     
3017     
3018     
3019 });
3020
3021
3022
3023  
3024
3025  /*
3026  * - LGPL
3027  *
3028  * navbar
3029  * 
3030  */
3031
3032 /**
3033  * @class Roo.bootstrap.NavSimplebar
3034  * @extends Roo.bootstrap.Navbar
3035  * Bootstrap Sidebar class
3036  *
3037  * @cfg {Boolean} inverse is inverted color
3038  * 
3039  * @cfg {String} type (nav | pills | tabs)
3040  * @cfg {Boolean} arrangement stacked | justified
3041  * @cfg {String} align (left | right) alignment
3042  * 
3043  * @cfg {Boolean} main (true|false) main nav bar? default false
3044  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3045  * 
3046  * @cfg {String} tag (header|footer|nav|div) default is nav 
3047
3048  * 
3049  * 
3050  * 
3051  * @constructor
3052  * Create a new Sidebar
3053  * @param {Object} config The config object
3054  */
3055
3056
3057 Roo.bootstrap.NavSimplebar = function(config){
3058     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3059 };
3060
3061 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3062     
3063     inverse: false,
3064     
3065     type: false,
3066     arrangement: '',
3067     align : false,
3068     
3069     
3070     
3071     main : false,
3072     
3073     
3074     tag : false,
3075     
3076     
3077     getAutoCreate : function(){
3078         
3079         
3080         var cfg = {
3081             tag : this.tag || 'div',
3082             cls : 'navbar'
3083         };
3084           
3085         
3086         cfg.cn = [
3087             {
3088                 cls: 'nav',
3089                 tag : 'ul'
3090             }
3091         ];
3092         
3093          
3094         this.type = this.type || 'nav';
3095         if (['tabs','pills'].indexOf(this.type)!==-1) {
3096             cfg.cn[0].cls += ' nav-' + this.type
3097         
3098         
3099         } else {
3100             if (this.type!=='nav') {
3101                 Roo.log('nav type must be nav/tabs/pills')
3102             }
3103             cfg.cn[0].cls += ' navbar-nav'
3104         }
3105         
3106         
3107         
3108         
3109         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3110             cfg.cn[0].cls += ' nav-' + this.arrangement;
3111         }
3112         
3113         
3114         if (this.align === 'right') {
3115             cfg.cn[0].cls += ' navbar-right';
3116         }
3117         
3118         if (this.inverse) {
3119             cfg.cls += ' navbar-inverse';
3120             
3121         }
3122         
3123         
3124         return cfg;
3125     
3126         
3127     }
3128     
3129     
3130     
3131 });
3132
3133
3134
3135  
3136
3137  
3138        /*
3139  * - LGPL
3140  *
3141  * navbar
3142  * 
3143  */
3144
3145 /**
3146  * @class Roo.bootstrap.NavHeaderbar
3147  * @extends Roo.bootstrap.NavSimplebar
3148  * Bootstrap Sidebar class
3149  *
3150  * @cfg {String} brand what is brand
3151  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3152  * @cfg {String} brand_href href of the brand
3153  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3154  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3155  * 
3156  * @constructor
3157  * Create a new Sidebar
3158  * @param {Object} config The config object
3159  */
3160
3161
3162 Roo.bootstrap.NavHeaderbar = function(config){
3163     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3164 };
3165
3166 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3167     
3168     position: '',
3169     brand: '',
3170     brand_href: false,
3171     srButton : true,
3172     autohide : false,
3173     
3174     getAutoCreate : function(){
3175         
3176         var   cfg = {
3177             tag: this.nav || 'nav',
3178             cls: 'navbar',
3179             role: 'navigation',
3180             cn: []
3181         };
3182         
3183         if(this.srButton){
3184             cfg.cn.push({
3185                 tag: 'div',
3186                 cls: 'navbar-header',
3187                 cn: [
3188                     {
3189                         tag: 'button',
3190                         type: 'button',
3191                         cls: 'navbar-toggle',
3192                         'data-toggle': 'collapse',
3193                         cn: [
3194                             {
3195                                 tag: 'span',
3196                                 cls: 'sr-only',
3197                                 html: 'Toggle navigation'
3198                             },
3199                             {
3200                                 tag: 'span',
3201                                 cls: 'icon-bar'
3202                             },
3203                             {
3204                                 tag: 'span',
3205                                 cls: 'icon-bar'
3206                             },
3207                             {
3208                                 tag: 'span',
3209                                 cls: 'icon-bar'
3210                             }
3211                         ]
3212                     }
3213                 ]
3214             });
3215         }
3216         
3217         cfg.cn.push({
3218             tag: 'div',
3219             cls: 'collapse navbar-collapse',
3220             cn : []
3221         });
3222         
3223         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3224         
3225         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3226             cfg.cls += ' navbar-' + this.position;
3227             
3228             // tag can override this..
3229             
3230             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3231         }
3232         
3233         if (this.brand !== '') {
3234             cfg.cn[0].cn.push({
3235                 tag: 'a',
3236                 href: this.brand_href ? this.brand_href : '#',
3237                 cls: 'navbar-brand',
3238                 cn: [
3239                 this.brand
3240                 ]
3241             });
3242         }
3243         
3244         if(this.main){
3245             cfg.cls += ' main-nav';
3246         }
3247         
3248         
3249         return cfg;
3250
3251         
3252     },
3253     initEvents : function()
3254     {
3255         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3256         
3257         if (this.autohide) {
3258             
3259             var prevScroll = 0;
3260             var ft = this.el;
3261             
3262             Roo.get(document).on('scroll',function(e) {
3263                 var ns = Roo.get(document).getScroll().top;
3264                 var os = prevScroll;
3265                 prevScroll = ns;
3266                 
3267                 if(ns > os){
3268                     ft.removeClass('slideDown');
3269                     ft.addClass('slideUp');
3270                     return;
3271                 }
3272                 ft.removeClass('slideUp');
3273                 ft.addClass('slideDown');
3274                  
3275               
3276           },this);
3277         }
3278     }    
3279           
3280       
3281     
3282     
3283 });
3284
3285
3286
3287  
3288
3289  /*
3290  * - LGPL
3291  *
3292  * navbar
3293  * 
3294  */
3295
3296 /**
3297  * @class Roo.bootstrap.NavSidebar
3298  * @extends Roo.bootstrap.Navbar
3299  * Bootstrap Sidebar class
3300  * 
3301  * @constructor
3302  * Create a new Sidebar
3303  * @param {Object} config The config object
3304  */
3305
3306
3307 Roo.bootstrap.NavSidebar = function(config){
3308     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3309 };
3310
3311 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3312     
3313     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3314     
3315     getAutoCreate : function(){
3316         
3317         
3318         return  {
3319             tag: 'div',
3320             cls: 'sidebar sidebar-nav'
3321         };
3322     
3323         
3324     }
3325     
3326     
3327     
3328 });
3329
3330
3331
3332  
3333
3334  /*
3335  * - LGPL
3336  *
3337  * nav group
3338  * 
3339  */
3340
3341 /**
3342  * @class Roo.bootstrap.NavGroup
3343  * @extends Roo.bootstrap.Component
3344  * Bootstrap NavGroup class
3345  * @cfg {String} align left | right
3346  * @cfg {Boolean} inverse false | true
3347  * @cfg {String} type (nav|pills|tab) default nav
3348  * @cfg {String} navId - reference Id for navbar.
3349
3350  * 
3351  * @constructor
3352  * Create a new nav group
3353  * @param {Object} config The config object
3354  */
3355
3356 Roo.bootstrap.NavGroup = function(config){
3357     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3358     this.navItems = [];
3359    
3360     Roo.bootstrap.NavGroup.register(this);
3361      this.addEvents({
3362         /**
3363              * @event changed
3364              * Fires when the active item changes
3365              * @param {Roo.bootstrap.NavGroup} this
3366              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3367              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3368          */
3369         'changed': true
3370      });
3371     
3372 };
3373
3374 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3375     
3376     align: '',
3377     inverse: false,
3378     form: false,
3379     type: 'nav',
3380     navId : '',
3381     // private
3382     
3383     navItems : false, 
3384     
3385     getAutoCreate : function()
3386     {
3387         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3388         
3389         cfg = {
3390             tag : 'ul',
3391             cls: 'nav' 
3392         }
3393         
3394         if (['tabs','pills'].indexOf(this.type)!==-1) {
3395             cfg.cls += ' nav-' + this.type
3396         } else {
3397             if (this.type!=='nav') {
3398                 Roo.log('nav type must be nav/tabs/pills')
3399             }
3400             cfg.cls += ' navbar-nav'
3401         }
3402         
3403         if (this.parent().sidebar) {
3404             cfg = {
3405                 tag: 'ul',
3406                 cls: 'dashboard-menu sidebar-menu'
3407             }
3408             
3409             return cfg;
3410         }
3411         
3412         if (this.form === true) {
3413             cfg = {
3414                 tag: 'form',
3415                 cls: 'navbar-form'
3416             }
3417             
3418             if (this.align === 'right') {
3419                 cfg.cls += ' navbar-right';
3420             } else {
3421                 cfg.cls += ' navbar-left';
3422             }
3423         }
3424         
3425         if (this.align === 'right') {
3426             cfg.cls += ' navbar-right';
3427         }
3428         
3429         if (this.inverse) {
3430             cfg.cls += ' navbar-inverse';
3431             
3432         }
3433         
3434         
3435         return cfg;
3436     },
3437     /**
3438     * sets the active Navigation item
3439     * @param {Roo.bootstrap.NavItem} the new current navitem
3440     */
3441     setActiveItem : function(item)
3442     {
3443         var prev = false;
3444         Roo.each(this.navItems, function(v){
3445             if (v == item) {
3446                 return ;
3447             }
3448             if (v.isActive()) {
3449                 v.setActive(false, true);
3450                 prev = v;
3451                 
3452             }
3453             
3454         });
3455
3456         item.setActive(true, true);
3457         this.fireEvent('changed', this, item, prev);
3458         
3459         
3460     },
3461     /**
3462     * gets the active Navigation item
3463     * @return {Roo.bootstrap.NavItem} the current navitem
3464     */
3465     getActive : function()
3466     {
3467         
3468         var prev = false;
3469         Roo.each(this.navItems, function(v){
3470             
3471             if (v.isActive()) {
3472                 prev = v;
3473                 
3474             }
3475             
3476         });
3477         return prev;
3478     },
3479     
3480     indexOfNav : function()
3481     {
3482         
3483         var prev = false;
3484         Roo.each(this.navItems, function(v,i){
3485             
3486             if (v.isActive()) {
3487                 prev = i;
3488                 
3489             }
3490             
3491         });
3492         return prev;
3493     },
3494     /**
3495     * adds a Navigation item
3496     * @param {Roo.bootstrap.NavItem} the navitem to add
3497     */
3498     addItem : function(cfg)
3499     {
3500         var cn = new Roo.bootstrap.NavItem(cfg);
3501         this.register(cn);
3502         cn.parentId = this.id;
3503         cn.onRender(this.el, null);
3504         return cn;
3505     },
3506     /**
3507     * register a Navigation item
3508     * @param {Roo.bootstrap.NavItem} the navitem to add
3509     */
3510     register : function(item)
3511     {
3512         this.navItems.push( item);
3513         item.navId = this.navId;
3514     
3515     },
3516     
3517     /**
3518     * clear all the Navigation item
3519     */
3520    
3521     clearAll : function()
3522     {
3523         this.navItems = [];
3524         this.el.dom.innerHTML = '';
3525     },
3526     
3527     getNavItem: function(tabId)
3528     {
3529         var ret = false;
3530         Roo.each(this.navItems, function(e) {
3531             if (e.tabId == tabId) {
3532                ret =  e;
3533                return false;
3534             }
3535             return true;
3536             
3537         });
3538         return ret;
3539     },
3540     
3541     setActiveNext : function()
3542     {
3543         var i = this.indexOfNav(this.getActive());
3544         if (i > this.navItems.length) {
3545             return;
3546         }
3547         this.setActiveItem(this.navItems[i+1]);
3548     },
3549     setActivePrev : function()
3550     {
3551         var i = this.indexOfNav(this.getActive());
3552         if (i  < 1) {
3553             return;
3554         }
3555         this.setActiveItem(this.navItems[i-1]);
3556     },
3557     clearWasActive : function(except) {
3558         Roo.each(this.navItems, function(e) {
3559             if (e.tabId != except.tabId && e.was_active) {
3560                e.was_active = false;
3561                return false;
3562             }
3563             return true;
3564             
3565         });
3566     },
3567     getWasActive : function ()
3568     {
3569         var r = false;
3570         Roo.each(this.navItems, function(e) {
3571             if (e.was_active) {
3572                r = e;
3573                return false;
3574             }
3575             return true;
3576             
3577         });
3578         return r;
3579     }
3580     
3581     
3582 });
3583
3584  
3585 Roo.apply(Roo.bootstrap.NavGroup, {
3586     
3587     groups: {},
3588      /**
3589     * register a Navigation Group
3590     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3591     */
3592     register : function(navgrp)
3593     {
3594         this.groups[navgrp.navId] = navgrp;
3595         
3596     },
3597     /**
3598     * fetch a Navigation Group based on the navigation ID
3599     * @param {string} the navgroup to add
3600     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3601     */
3602     get: function(navId) {
3603         if (typeof(this.groups[navId]) == 'undefined') {
3604             return false;
3605             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3606         }
3607         return this.groups[navId] ;
3608     }
3609     
3610     
3611     
3612 });
3613
3614  /*
3615  * - LGPL
3616  *
3617  * row
3618  * 
3619  */
3620
3621 /**
3622  * @class Roo.bootstrap.NavItem
3623  * @extends Roo.bootstrap.Component
3624  * Bootstrap Navbar.NavItem class
3625  * @cfg {String} href  link to
3626  * @cfg {String} html content of button
3627  * @cfg {String} badge text inside badge
3628  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3629  * @cfg {String} glyphicon name of glyphicon
3630  * @cfg {String} icon name of font awesome icon
3631  * @cfg {Boolean} active Is item active
3632  * @cfg {Boolean} disabled Is item disabled
3633  
3634  * @cfg {Boolean} preventDefault (true | false) default false
3635  * @cfg {String} tabId the tab that this item activates.
3636  * @cfg {String} tagtype (a|span) render as a href or span?
3637   
3638  * @constructor
3639  * Create a new Navbar Item
3640  * @param {Object} config The config object
3641  */
3642 Roo.bootstrap.NavItem = function(config){
3643     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3644     this.addEvents({
3645         // raw events
3646         /**
3647          * @event click
3648          * The raw click event for the entire grid.
3649          * @param {Roo.EventObject} e
3650          */
3651         "click" : true,
3652          /**
3653             * @event changed
3654             * Fires when the active item active state changes
3655             * @param {Roo.bootstrap.NavItem} this
3656             * @param {boolean} state the new state
3657              
3658          */
3659         'changed': true
3660     });
3661    
3662 };
3663
3664 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3665     
3666     href: false,
3667     html: '',
3668     badge: '',
3669     icon: false,
3670     glyphicon: false,
3671     active: false,
3672     preventDefault : false,
3673     tabId : false,
3674     tagtype : 'a',
3675     disabled : false,
3676     
3677     was_active : false,
3678     
3679     getAutoCreate : function(){
3680          
3681         var cfg = {
3682             tag: 'li',
3683             cls: 'nav-item'
3684             
3685         }
3686         if (this.active) {
3687             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3688         }
3689         if (this.disabled) {
3690             cfg.cls += ' disabled';
3691         }
3692         
3693         if (this.href || this.html || this.glyphicon || this.icon) {
3694             cfg.cn = [
3695                 {
3696                     tag: this.tagtype,
3697                     href : this.href || "#",
3698                     html: this.html || ''
3699                 }
3700             ];
3701             
3702             if (this.icon) {
3703                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3704             }
3705
3706             if(this.glyphicon) {
3707                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3708             }
3709             
3710             if (this.menu) {
3711                 
3712                 cfg.cn[0].html += " <span class='caret'></span>";
3713              
3714             }
3715             
3716             if (this.badge !== '') {
3717                  
3718                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3719             }
3720         }
3721         
3722         
3723         
3724         return cfg;
3725     },
3726     initEvents: function() {
3727        // Roo.log('init events?');
3728        // Roo.log(this.el.dom);
3729         if (typeof (this.menu) != 'undefined') {
3730             this.menu.parentType = this.xtype;
3731             this.menu.triggerEl = this.el;
3732             this.addxtype(Roo.apply({}, this.menu));
3733         }
3734
3735        
3736         this.el.select('a',true).on('click', this.onClick, this);
3737         // at this point parent should be available..
3738         this.parent().register(this);
3739     },
3740     
3741     onClick : function(e)
3742     {
3743          
3744         if(this.preventDefault){
3745             e.preventDefault();
3746         }
3747         if (this.disabled) {
3748             return;
3749         }
3750         
3751         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3752         if (tg && tg.transition) {
3753             Roo.log("waiting for the transitionend");
3754             return;
3755         }
3756         
3757         Roo.log("fire event clicked");
3758         if(this.fireEvent('click', this, e) === false){
3759             return;
3760         };
3761         var p = this.parent();
3762         if (['tabs','pills'].indexOf(p.type)!==-1) {
3763             if (typeof(p.setActiveItem) !== 'undefined') {
3764                 p.setActiveItem(this);
3765             }
3766         }
3767         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3768         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3769             // remove the collapsed menu expand...
3770             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3771         }
3772         
3773     },
3774     
3775     isActive: function () {
3776         return this.active
3777     },
3778     setActive : function(state, fire, is_was_active)
3779     {
3780         if (this.active && !state & this.navId) {
3781             this.was_active = true;
3782             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3783             if (nv) {
3784                 nv.clearWasActive(this);
3785             }
3786             
3787         }
3788         this.active = state;
3789         
3790         if (!state ) {
3791             this.el.removeClass('active');
3792         } else if (!this.el.hasClass('active')) {
3793             this.el.addClass('active');
3794         }
3795         if (fire) {
3796             this.fireEvent('changed', this, state);
3797         }
3798         
3799         // show a panel if it's registered and related..
3800         
3801         if (!this.navId || !this.tabId || !state || is_was_active) {
3802             return;
3803         }
3804         
3805         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3806         if (!tg) {
3807             return;
3808         }
3809         var pan = tg.getPanelByName(this.tabId);
3810         if (!pan) {
3811             return;
3812         }
3813         // if we can not flip to new panel - go back to old nav highlight..
3814         if (false == tg.showPanel(pan)) {
3815             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3816             if (nv) {
3817                 var onav = nv.getWasActive();
3818                 if (onav) {
3819                     onav.setActive(true, false, true);
3820                 }
3821             }
3822             
3823         }
3824         
3825         
3826         
3827     },
3828      // this should not be here...
3829     setDisabled : function(state)
3830     {
3831         this.disabled = state;
3832         if (!state ) {
3833             this.el.removeClass('disabled');
3834         } else if (!this.el.hasClass('disabled')) {
3835             this.el.addClass('disabled');
3836         }
3837         
3838     }
3839 });
3840  
3841
3842  /*
3843  * - LGPL
3844  *
3845  * sidebar item
3846  *
3847  *  li
3848  *    <span> icon </span>
3849  *    <span> text </span>
3850  *    <span>badge </span>
3851  */
3852
3853 /**
3854  * @class Roo.bootstrap.NavSidebarItem
3855  * @extends Roo.bootstrap.NavItem
3856  * Bootstrap Navbar.NavSidebarItem class
3857  * @constructor
3858  * Create a new Navbar Button
3859  * @param {Object} config The config object
3860  */
3861 Roo.bootstrap.NavSidebarItem = function(config){
3862     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3863     this.addEvents({
3864         // raw events
3865         /**
3866          * @event click
3867          * The raw click event for the entire grid.
3868          * @param {Roo.EventObject} e
3869          */
3870         "click" : true,
3871          /**
3872             * @event changed
3873             * Fires when the active item active state changes
3874             * @param {Roo.bootstrap.NavSidebarItem} this
3875             * @param {boolean} state the new state
3876              
3877          */
3878         'changed': true
3879     });
3880    
3881 };
3882
3883 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3884     
3885     
3886     getAutoCreate : function(){
3887         
3888         
3889         var a = {
3890                 tag: 'a',
3891                 href : this.href || '#',
3892                 cls: '',
3893                 html : '',
3894                 cn : []
3895         };
3896         var cfg = {
3897             tag: 'li',
3898             cls: '',
3899             cn: [ a ]
3900         }
3901         var span = {
3902             tag: 'span',
3903             html : this.html || ''
3904         }
3905         
3906         
3907         if (this.active) {
3908             cfg.cls += ' active';
3909         }
3910         
3911         // left icon..
3912         if (this.glyphicon || this.icon) {
3913             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3914             a.cn.push({ tag : 'i', cls : c }) ;
3915         }
3916         // html..
3917         a.cn.push(span);
3918         // then badge..
3919         if (this.badge !== '') {
3920             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3921         }
3922         // fi
3923         if (this.menu) {
3924             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3925             a.cls += 'dropdown-toggle treeview' ;
3926             
3927         }
3928         
3929         
3930         
3931         return cfg;
3932          
3933            
3934     }
3935    
3936      
3937  
3938 });
3939  
3940
3941  /*
3942  * - LGPL
3943  *
3944  * row
3945  * 
3946  */
3947
3948 /**
3949  * @class Roo.bootstrap.Row
3950  * @extends Roo.bootstrap.Component
3951  * Bootstrap Row class (contains columns...)
3952  * 
3953  * @constructor
3954  * Create a new Row
3955  * @param {Object} config The config object
3956  */
3957
3958 Roo.bootstrap.Row = function(config){
3959     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3960 };
3961
3962 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3963     
3964     getAutoCreate : function(){
3965        return {
3966             cls: 'row clearfix'
3967        };
3968     }
3969     
3970     
3971 });
3972
3973  
3974
3975  /*
3976  * - LGPL
3977  *
3978  * element
3979  * 
3980  */
3981
3982 /**
3983  * @class Roo.bootstrap.Element
3984  * @extends Roo.bootstrap.Component
3985  * Bootstrap Element class
3986  * @cfg {String} html contents of the element
3987  * @cfg {String} tag tag of the element
3988  * @cfg {String} cls class of the element
3989  * 
3990  * @constructor
3991  * Create a new Element
3992  * @param {Object} config The config object
3993  */
3994
3995 Roo.bootstrap.Element = function(config){
3996     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3997 };
3998
3999 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4000     
4001     tag: 'div',
4002     cls: '',
4003     html: '',
4004      
4005     
4006     getAutoCreate : function(){
4007         
4008         var cfg = {
4009             tag: this.tag,
4010             cls: this.cls,
4011             html: this.html
4012         }
4013         
4014         
4015         
4016         return cfg;
4017     }
4018    
4019 });
4020
4021  
4022
4023  /*
4024  * - LGPL
4025  *
4026  * pagination
4027  * 
4028  */
4029
4030 /**
4031  * @class Roo.bootstrap.Pagination
4032  * @extends Roo.bootstrap.Component
4033  * Bootstrap Pagination class
4034  * @cfg {String} size xs | sm | md | lg
4035  * @cfg {Boolean} inverse false | true
4036  * 
4037  * @constructor
4038  * Create a new Pagination
4039  * @param {Object} config The config object
4040  */
4041
4042 Roo.bootstrap.Pagination = function(config){
4043     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4044 };
4045
4046 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4047     
4048     cls: false,
4049     size: false,
4050     inverse: false,
4051     
4052     getAutoCreate : function(){
4053         var cfg = {
4054             tag: 'ul',
4055                 cls: 'pagination'
4056         };
4057         if (this.inverse) {
4058             cfg.cls += ' inverse';
4059         }
4060         if (this.html) {
4061             cfg.html=this.html;
4062         }
4063         if (this.cls) {
4064             cfg.cls += " " + this.cls;
4065         }
4066         return cfg;
4067     }
4068    
4069 });
4070
4071  
4072
4073  /*
4074  * - LGPL
4075  *
4076  * Pagination item
4077  * 
4078  */
4079
4080
4081 /**
4082  * @class Roo.bootstrap.PaginationItem
4083  * @extends Roo.bootstrap.Component
4084  * Bootstrap PaginationItem class
4085  * @cfg {String} html text
4086  * @cfg {String} href the link
4087  * @cfg {Boolean} preventDefault (true | false) default true
4088  * @cfg {Boolean} active (true | false) default false
4089  * 
4090  * 
4091  * @constructor
4092  * Create a new PaginationItem
4093  * @param {Object} config The config object
4094  */
4095
4096
4097 Roo.bootstrap.PaginationItem = function(config){
4098     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4099     this.addEvents({
4100         // raw events
4101         /**
4102          * @event click
4103          * The raw click event for the entire grid.
4104          * @param {Roo.EventObject} e
4105          */
4106         "click" : true
4107     });
4108 };
4109
4110 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4111     
4112     href : false,
4113     html : false,
4114     preventDefault: true,
4115     active : false,
4116     cls : false,
4117     
4118     getAutoCreate : function(){
4119         var cfg= {
4120             tag: 'li',
4121             cn: [
4122                 {
4123                     tag : 'a',
4124                     href : this.href ? this.href : '#',
4125                     html : this.html ? this.html : ''
4126                 }
4127             ]
4128         };
4129         
4130         if(this.cls){
4131             cfg.cls = this.cls;
4132         }
4133         
4134         if(this.active){
4135             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4136         }
4137         
4138         return cfg;
4139     },
4140     
4141     initEvents: function() {
4142         
4143         this.el.on('click', this.onClick, this);
4144         
4145     },
4146     onClick : function(e)
4147     {
4148         Roo.log('PaginationItem on click ');
4149         if(this.preventDefault){
4150             e.preventDefault();
4151         }
4152         
4153         this.fireEvent('click', this, e);
4154     }
4155    
4156 });
4157
4158  
4159
4160  /*
4161  * - LGPL
4162  *
4163  * slider
4164  * 
4165  */
4166
4167
4168 /**
4169  * @class Roo.bootstrap.Slider
4170  * @extends Roo.bootstrap.Component
4171  * Bootstrap Slider class
4172  *    
4173  * @constructor
4174  * Create a new Slider
4175  * @param {Object} config The config object
4176  */
4177
4178 Roo.bootstrap.Slider = function(config){
4179     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4180 };
4181
4182 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4183     
4184     getAutoCreate : function(){
4185         
4186         var cfg = {
4187             tag: 'div',
4188             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4189             cn: [
4190                 {
4191                     tag: 'a',
4192                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4193                 }
4194             ]
4195         }
4196         
4197         return cfg;
4198     }
4199    
4200 });
4201
4202  /*
4203  * Based on:
4204  * Ext JS Library 1.1.1
4205  * Copyright(c) 2006-2007, Ext JS, LLC.
4206  *
4207  * Originally Released Under LGPL - original licence link has changed is not relivant.
4208  *
4209  * Fork - LGPL
4210  * <script type="text/javascript">
4211  */
4212  
4213
4214 /**
4215  * @class Roo.grid.ColumnModel
4216  * @extends Roo.util.Observable
4217  * This is the default implementation of a ColumnModel used by the Grid. It defines
4218  * the columns in the grid.
4219  * <br>Usage:<br>
4220  <pre><code>
4221  var colModel = new Roo.grid.ColumnModel([
4222         {header: "Ticker", width: 60, sortable: true, locked: true},
4223         {header: "Company Name", width: 150, sortable: true},
4224         {header: "Market Cap.", width: 100, sortable: true},
4225         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4226         {header: "Employees", width: 100, sortable: true, resizable: false}
4227  ]);
4228  </code></pre>
4229  * <p>
4230  
4231  * The config options listed for this class are options which may appear in each
4232  * individual column definition.
4233  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4234  * @constructor
4235  * @param {Object} config An Array of column config objects. See this class's
4236  * config objects for details.
4237 */
4238 Roo.grid.ColumnModel = function(config){
4239         /**
4240      * The config passed into the constructor
4241      */
4242     this.config = config;
4243     this.lookup = {};
4244
4245     // if no id, create one
4246     // if the column does not have a dataIndex mapping,
4247     // map it to the order it is in the config
4248     for(var i = 0, len = config.length; i < len; i++){
4249         var c = config[i];
4250         if(typeof c.dataIndex == "undefined"){
4251             c.dataIndex = i;
4252         }
4253         if(typeof c.renderer == "string"){
4254             c.renderer = Roo.util.Format[c.renderer];
4255         }
4256         if(typeof c.id == "undefined"){
4257             c.id = Roo.id();
4258         }
4259         if(c.editor && c.editor.xtype){
4260             c.editor  = Roo.factory(c.editor, Roo.grid);
4261         }
4262         if(c.editor && c.editor.isFormField){
4263             c.editor = new Roo.grid.GridEditor(c.editor);
4264         }
4265         this.lookup[c.id] = c;
4266     }
4267
4268     /**
4269      * The width of columns which have no width specified (defaults to 100)
4270      * @type Number
4271      */
4272     this.defaultWidth = 100;
4273
4274     /**
4275      * Default sortable of columns which have no sortable specified (defaults to false)
4276      * @type Boolean
4277      */
4278     this.defaultSortable = false;
4279
4280     this.addEvents({
4281         /**
4282              * @event widthchange
4283              * Fires when the width of a column changes.
4284              * @param {ColumnModel} this
4285              * @param {Number} columnIndex The column index
4286              * @param {Number} newWidth The new width
4287              */
4288             "widthchange": true,
4289         /**
4290              * @event headerchange
4291              * Fires when the text of a header changes.
4292              * @param {ColumnModel} this
4293              * @param {Number} columnIndex The column index
4294              * @param {Number} newText The new header text
4295              */
4296             "headerchange": true,
4297         /**
4298              * @event hiddenchange
4299              * Fires when a column is hidden or "unhidden".
4300              * @param {ColumnModel} this
4301              * @param {Number} columnIndex The column index
4302              * @param {Boolean} hidden true if hidden, false otherwise
4303              */
4304             "hiddenchange": true,
4305             /**
4306          * @event columnmoved
4307          * Fires when a column is moved.
4308          * @param {ColumnModel} this
4309          * @param {Number} oldIndex
4310          * @param {Number} newIndex
4311          */
4312         "columnmoved" : true,
4313         /**
4314          * @event columlockchange
4315          * Fires when a column's locked state is changed
4316          * @param {ColumnModel} this
4317          * @param {Number} colIndex
4318          * @param {Boolean} locked true if locked
4319          */
4320         "columnlockchange" : true
4321     });
4322     Roo.grid.ColumnModel.superclass.constructor.call(this);
4323 };
4324 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4325     /**
4326      * @cfg {String} header The header text to display in the Grid view.
4327      */
4328     /**
4329      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4330      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4331      * specified, the column's index is used as an index into the Record's data Array.
4332      */
4333     /**
4334      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4335      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4336      */
4337     /**
4338      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4339      * Defaults to the value of the {@link #defaultSortable} property.
4340      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4341      */
4342     /**
4343      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4344      */
4345     /**
4346      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4347      */
4348     /**
4349      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4350      */
4351     /**
4352      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4353      */
4354     /**
4355      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4356      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4357      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4358      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4359      */
4360        /**
4361      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4362      */
4363     /**
4364      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4365      */
4366
4367     /**
4368      * Returns the id of the column at the specified index.
4369      * @param {Number} index The column index
4370      * @return {String} the id
4371      */
4372     getColumnId : function(index){
4373         return this.config[index].id;
4374     },
4375
4376     /**
4377      * Returns the column for a specified id.
4378      * @param {String} id The column id
4379      * @return {Object} the column
4380      */
4381     getColumnById : function(id){
4382         return this.lookup[id];
4383     },
4384
4385     
4386     /**
4387      * Returns the column for a specified dataIndex.
4388      * @param {String} dataIndex The column dataIndex
4389      * @return {Object|Boolean} the column or false if not found
4390      */
4391     getColumnByDataIndex: function(dataIndex){
4392         var index = this.findColumnIndex(dataIndex);
4393         return index > -1 ? this.config[index] : false;
4394     },
4395     
4396     /**
4397      * Returns the index for a specified column id.
4398      * @param {String} id The column id
4399      * @return {Number} the index, or -1 if not found
4400      */
4401     getIndexById : function(id){
4402         for(var i = 0, len = this.config.length; i < len; i++){
4403             if(this.config[i].id == id){
4404                 return i;
4405             }
4406         }
4407         return -1;
4408     },
4409     
4410     /**
4411      * Returns the index for a specified column dataIndex.
4412      * @param {String} dataIndex The column dataIndex
4413      * @return {Number} the index, or -1 if not found
4414      */
4415     
4416     findColumnIndex : function(dataIndex){
4417         for(var i = 0, len = this.config.length; i < len; i++){
4418             if(this.config[i].dataIndex == dataIndex){
4419                 return i;
4420             }
4421         }
4422         return -1;
4423     },
4424     
4425     
4426     moveColumn : function(oldIndex, newIndex){
4427         var c = this.config[oldIndex];
4428         this.config.splice(oldIndex, 1);
4429         this.config.splice(newIndex, 0, c);
4430         this.dataMap = null;
4431         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4432     },
4433
4434     isLocked : function(colIndex){
4435         return this.config[colIndex].locked === true;
4436     },
4437
4438     setLocked : function(colIndex, value, suppressEvent){
4439         if(this.isLocked(colIndex) == value){
4440             return;
4441         }
4442         this.config[colIndex].locked = value;
4443         if(!suppressEvent){
4444             this.fireEvent("columnlockchange", this, colIndex, value);
4445         }
4446     },
4447
4448     getTotalLockedWidth : function(){
4449         var totalWidth = 0;
4450         for(var i = 0; i < this.config.length; i++){
4451             if(this.isLocked(i) && !this.isHidden(i)){
4452                 this.totalWidth += this.getColumnWidth(i);
4453             }
4454         }
4455         return totalWidth;
4456     },
4457
4458     getLockedCount : function(){
4459         for(var i = 0, len = this.config.length; i < len; i++){
4460             if(!this.isLocked(i)){
4461                 return i;
4462             }
4463         }
4464     },
4465
4466     /**
4467      * Returns the number of columns.
4468      * @return {Number}
4469      */
4470     getColumnCount : function(visibleOnly){
4471         if(visibleOnly === true){
4472             var c = 0;
4473             for(var i = 0, len = this.config.length; i < len; i++){
4474                 if(!this.isHidden(i)){
4475                     c++;
4476                 }
4477             }
4478             return c;
4479         }
4480         return this.config.length;
4481     },
4482
4483     /**
4484      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4485      * @param {Function} fn
4486      * @param {Object} scope (optional)
4487      * @return {Array} result
4488      */
4489     getColumnsBy : function(fn, scope){
4490         var r = [];
4491         for(var i = 0, len = this.config.length; i < len; i++){
4492             var c = this.config[i];
4493             if(fn.call(scope||this, c, i) === true){
4494                 r[r.length] = c;
4495             }
4496         }
4497         return r;
4498     },
4499
4500     /**
4501      * Returns true if the specified column is sortable.
4502      * @param {Number} col The column index
4503      * @return {Boolean}
4504      */
4505     isSortable : function(col){
4506         if(typeof this.config[col].sortable == "undefined"){
4507             return this.defaultSortable;
4508         }
4509         return this.config[col].sortable;
4510     },
4511
4512     /**
4513      * Returns the rendering (formatting) function defined for the column.
4514      * @param {Number} col The column index.
4515      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4516      */
4517     getRenderer : function(col){
4518         if(!this.config[col].renderer){
4519             return Roo.grid.ColumnModel.defaultRenderer;
4520         }
4521         return this.config[col].renderer;
4522     },
4523
4524     /**
4525      * Sets the rendering (formatting) function for a column.
4526      * @param {Number} col The column index
4527      * @param {Function} fn The function to use to process the cell's raw data
4528      * to return HTML markup for the grid view. The render function is called with
4529      * the following parameters:<ul>
4530      * <li>Data value.</li>
4531      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4532      * <li>css A CSS style string to apply to the table cell.</li>
4533      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4534      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4535      * <li>Row index</li>
4536      * <li>Column index</li>
4537      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4538      */
4539     setRenderer : function(col, fn){
4540         this.config[col].renderer = fn;
4541     },
4542
4543     /**
4544      * Returns the width for the specified column.
4545      * @param {Number} col The column index
4546      * @return {Number}
4547      */
4548     getColumnWidth : function(col){
4549         return this.config[col].width * 1 || this.defaultWidth;
4550     },
4551
4552     /**
4553      * Sets the width for a column.
4554      * @param {Number} col The column index
4555      * @param {Number} width The new width
4556      */
4557     setColumnWidth : function(col, width, suppressEvent){
4558         this.config[col].width = width;
4559         this.totalWidth = null;
4560         if(!suppressEvent){
4561              this.fireEvent("widthchange", this, col, width);
4562         }
4563     },
4564
4565     /**
4566      * Returns the total width of all columns.
4567      * @param {Boolean} includeHidden True to include hidden column widths
4568      * @return {Number}
4569      */
4570     getTotalWidth : function(includeHidden){
4571         if(!this.totalWidth){
4572             this.totalWidth = 0;
4573             for(var i = 0, len = this.config.length; i < len; i++){
4574                 if(includeHidden || !this.isHidden(i)){
4575                     this.totalWidth += this.getColumnWidth(i);
4576                 }
4577             }
4578         }
4579         return this.totalWidth;
4580     },
4581
4582     /**
4583      * Returns the header for the specified column.
4584      * @param {Number} col The column index
4585      * @return {String}
4586      */
4587     getColumnHeader : function(col){
4588         return this.config[col].header;
4589     },
4590
4591     /**
4592      * Sets the header for a column.
4593      * @param {Number} col The column index
4594      * @param {String} header The new header
4595      */
4596     setColumnHeader : function(col, header){
4597         this.config[col].header = header;
4598         this.fireEvent("headerchange", this, col, header);
4599     },
4600
4601     /**
4602      * Returns the tooltip for the specified column.
4603      * @param {Number} col The column index
4604      * @return {String}
4605      */
4606     getColumnTooltip : function(col){
4607             return this.config[col].tooltip;
4608     },
4609     /**
4610      * Sets the tooltip for a column.
4611      * @param {Number} col The column index
4612      * @param {String} tooltip The new tooltip
4613      */
4614     setColumnTooltip : function(col, tooltip){
4615             this.config[col].tooltip = tooltip;
4616     },
4617
4618     /**
4619      * Returns the dataIndex for the specified column.
4620      * @param {Number} col The column index
4621      * @return {Number}
4622      */
4623     getDataIndex : function(col){
4624         return this.config[col].dataIndex;
4625     },
4626
4627     /**
4628      * Sets the dataIndex for a column.
4629      * @param {Number} col The column index
4630      * @param {Number} dataIndex The new dataIndex
4631      */
4632     setDataIndex : function(col, dataIndex){
4633         this.config[col].dataIndex = dataIndex;
4634     },
4635
4636     
4637     
4638     /**
4639      * Returns true if the cell is editable.
4640      * @param {Number} colIndex The column index
4641      * @param {Number} rowIndex The row index
4642      * @return {Boolean}
4643      */
4644     isCellEditable : function(colIndex, rowIndex){
4645         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4646     },
4647
4648     /**
4649      * Returns the editor defined for the cell/column.
4650      * return false or null to disable editing.
4651      * @param {Number} colIndex The column index
4652      * @param {Number} rowIndex The row index
4653      * @return {Object}
4654      */
4655     getCellEditor : function(colIndex, rowIndex){
4656         return this.config[colIndex].editor;
4657     },
4658
4659     /**
4660      * Sets if a column is editable.
4661      * @param {Number} col The column index
4662      * @param {Boolean} editable True if the column is editable
4663      */
4664     setEditable : function(col, editable){
4665         this.config[col].editable = editable;
4666     },
4667
4668
4669     /**
4670      * Returns true if the column is hidden.
4671      * @param {Number} colIndex The column index
4672      * @return {Boolean}
4673      */
4674     isHidden : function(colIndex){
4675         return this.config[colIndex].hidden;
4676     },
4677
4678
4679     /**
4680      * Returns true if the column width cannot be changed
4681      */
4682     isFixed : function(colIndex){
4683         return this.config[colIndex].fixed;
4684     },
4685
4686     /**
4687      * Returns true if the column can be resized
4688      * @return {Boolean}
4689      */
4690     isResizable : function(colIndex){
4691         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4692     },
4693     /**
4694      * Sets if a column is hidden.
4695      * @param {Number} colIndex The column index
4696      * @param {Boolean} hidden True if the column is hidden
4697      */
4698     setHidden : function(colIndex, hidden){
4699         this.config[colIndex].hidden = hidden;
4700         this.totalWidth = null;
4701         this.fireEvent("hiddenchange", this, colIndex, hidden);
4702     },
4703
4704     /**
4705      * Sets the editor for a column.
4706      * @param {Number} col The column index
4707      * @param {Object} editor The editor object
4708      */
4709     setEditor : function(col, editor){
4710         this.config[col].editor = editor;
4711     }
4712 });
4713
4714 Roo.grid.ColumnModel.defaultRenderer = function(value){
4715         if(typeof value == "string" && value.length < 1){
4716             return "&#160;";
4717         }
4718         return value;
4719 };
4720
4721 // Alias for backwards compatibility
4722 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4723 /*
4724  * Based on:
4725  * Ext JS Library 1.1.1
4726  * Copyright(c) 2006-2007, Ext JS, LLC.
4727  *
4728  * Originally Released Under LGPL - original licence link has changed is not relivant.
4729  *
4730  * Fork - LGPL
4731  * <script type="text/javascript">
4732  */
4733  
4734 /**
4735  * @class Roo.LoadMask
4736  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4737  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4738  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4739  * element's UpdateManager load indicator and will be destroyed after the initial load.
4740  * @constructor
4741  * Create a new LoadMask
4742  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4743  * @param {Object} config The config object
4744  */
4745 Roo.LoadMask = function(el, config){
4746     this.el = Roo.get(el);
4747     Roo.apply(this, config);
4748     if(this.store){
4749         this.store.on('beforeload', this.onBeforeLoad, this);
4750         this.store.on('load', this.onLoad, this);
4751         this.store.on('loadexception', this.onLoadException, this);
4752         this.removeMask = false;
4753     }else{
4754         var um = this.el.getUpdateManager();
4755         um.showLoadIndicator = false; // disable the default indicator
4756         um.on('beforeupdate', this.onBeforeLoad, this);
4757         um.on('update', this.onLoad, this);
4758         um.on('failure', this.onLoad, this);
4759         this.removeMask = true;
4760     }
4761 };
4762
4763 Roo.LoadMask.prototype = {
4764     /**
4765      * @cfg {Boolean} removeMask
4766      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4767      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4768      */
4769     /**
4770      * @cfg {String} msg
4771      * The text to display in a centered loading message box (defaults to 'Loading...')
4772      */
4773     msg : 'Loading...',
4774     /**
4775      * @cfg {String} msgCls
4776      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4777      */
4778     msgCls : 'x-mask-loading',
4779
4780     /**
4781      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4782      * @type Boolean
4783      */
4784     disabled: false,
4785
4786     /**
4787      * Disables the mask to prevent it from being displayed
4788      */
4789     disable : function(){
4790        this.disabled = true;
4791     },
4792
4793     /**
4794      * Enables the mask so that it can be displayed
4795      */
4796     enable : function(){
4797         this.disabled = false;
4798     },
4799     
4800     onLoadException : function()
4801     {
4802         Roo.log(arguments);
4803         
4804         if (typeof(arguments[3]) != 'undefined') {
4805             Roo.MessageBox.alert("Error loading",arguments[3]);
4806         } 
4807         /*
4808         try {
4809             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4810                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4811             }   
4812         } catch(e) {
4813             
4814         }
4815         */
4816     
4817         
4818         
4819         this.el.unmask(this.removeMask);
4820     },
4821     // private
4822     onLoad : function()
4823     {
4824         this.el.unmask(this.removeMask);
4825     },
4826
4827     // private
4828     onBeforeLoad : function(){
4829         if(!this.disabled){
4830             this.el.mask(this.msg, this.msgCls);
4831         }
4832     },
4833
4834     // private
4835     destroy : function(){
4836         if(this.store){
4837             this.store.un('beforeload', this.onBeforeLoad, this);
4838             this.store.un('load', this.onLoad, this);
4839             this.store.un('loadexception', this.onLoadException, this);
4840         }else{
4841             var um = this.el.getUpdateManager();
4842             um.un('beforeupdate', this.onBeforeLoad, this);
4843             um.un('update', this.onLoad, this);
4844             um.un('failure', this.onLoad, this);
4845         }
4846     }
4847 };/*
4848  * - LGPL
4849  *
4850  * table
4851  * 
4852  */
4853
4854 /**
4855  * @class Roo.bootstrap.Table
4856  * @extends Roo.bootstrap.Component
4857  * Bootstrap Table class
4858  * @cfg {String} cls table class
4859  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4860  * @cfg {String} bgcolor Specifies the background color for a table
4861  * @cfg {Number} border Specifies whether the table cells should have borders or not
4862  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4863  * @cfg {Number} cellspacing Specifies the space between cells
4864  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4865  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4866  * @cfg {String} sortable Specifies that the table should be sortable
4867  * @cfg {String} summary Specifies a summary of the content of a table
4868  * @cfg {Number} width Specifies the width of a table
4869  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4870  * 
4871  * @cfg {boolean} striped Should the rows be alternative striped
4872  * @cfg {boolean} bordered Add borders to the table
4873  * @cfg {boolean} hover Add hover highlighting
4874  * @cfg {boolean} condensed Format condensed
4875  * @cfg {boolean} responsive Format condensed
4876  * @cfg {Boolean} loadMask (true|false) default false
4877  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4878  * @cfg {Boolean} thead (true|false) generate thead, default true
4879  * @cfg {Boolean} RowSelection (true|false) default false
4880  * @cfg {Boolean} CellSelection (true|false) default false
4881  *
4882  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4883  
4884  * 
4885  * @constructor
4886  * Create a new Table
4887  * @param {Object} config The config object
4888  */
4889
4890 Roo.bootstrap.Table = function(config){
4891     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4892     
4893     if (this.sm) {
4894         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4895         this.sm = this.selModel;
4896         this.sm.xmodule = this.xmodule || false;
4897     }
4898     if (this.cm && typeof(this.cm.config) == 'undefined') {
4899         this.colModel = new Roo.grid.ColumnModel(this.cm);
4900         this.cm = this.colModel;
4901         this.cm.xmodule = this.xmodule || false;
4902     }
4903     if (this.store) {
4904         this.store= Roo.factory(this.store, Roo.data);
4905         this.ds = this.store;
4906         this.ds.xmodule = this.xmodule || false;
4907          
4908     }
4909     if (this.footer && this.store) {
4910         this.footer.dataSource = this.ds;
4911         this.footer = Roo.factory(this.footer);
4912     }
4913     
4914     /** @private */
4915     this.addEvents({
4916         /**
4917          * @event cellclick
4918          * Fires when a cell is clicked
4919          * @param {Roo.bootstrap.Table} this
4920          * @param {Roo.Element} el
4921          * @param {Number} rowIndex
4922          * @param {Number} columnIndex
4923          * @param {Roo.EventObject} e
4924          */
4925         "cellclick" : true,
4926         /**
4927          * @event celldblclick
4928          * Fires when a cell is double clicked
4929          * @param {Roo.bootstrap.Table} this
4930          * @param {Roo.Element} el
4931          * @param {Number} rowIndex
4932          * @param {Number} columnIndex
4933          * @param {Roo.EventObject} e
4934          */
4935         "celldblclick" : true,
4936         /**
4937          * @event rowclick
4938          * Fires when a row is clicked
4939          * @param {Roo.bootstrap.Table} this
4940          * @param {Roo.Element} el
4941          * @param {Number} rowIndex
4942          * @param {Roo.EventObject} e
4943          */
4944         "rowclick" : true,
4945         /**
4946          * @event rowdblclick
4947          * Fires when a row is double clicked
4948          * @param {Roo.bootstrap.Table} this
4949          * @param {Roo.Element} el
4950          * @param {Number} rowIndex
4951          * @param {Roo.EventObject} e
4952          */
4953         "rowdblclick" : true,
4954         /**
4955          * @event mouseover
4956          * Fires when a mouseover occur
4957          * @param {Roo.bootstrap.Table} this
4958          * @param {Roo.Element} el
4959          * @param {Number} rowIndex
4960          * @param {Number} columnIndex
4961          * @param {Roo.EventObject} e
4962          */
4963         "mouseover" : true,
4964         /**
4965          * @event mouseout
4966          * Fires when a mouseout occur
4967          * @param {Roo.bootstrap.Table} this
4968          * @param {Roo.Element} el
4969          * @param {Number} rowIndex
4970          * @param {Number} columnIndex
4971          * @param {Roo.EventObject} e
4972          */
4973         "mouseout" : true,
4974         /**
4975          * @event rowclass
4976          * Fires when a row is rendered, so you can change add a style to it.
4977          * @param {Roo.bootstrap.Table} this
4978          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4979          */
4980         'rowclass' : true
4981         
4982     });
4983 };
4984
4985 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4986     
4987     cls: false,
4988     align: false,
4989     bgcolor: false,
4990     border: false,
4991     cellpadding: false,
4992     cellspacing: false,
4993     frame: false,
4994     rules: false,
4995     sortable: false,
4996     summary: false,
4997     width: false,
4998     striped : false,
4999     bordered: false,
5000     hover:  false,
5001     condensed : false,
5002     responsive : false,
5003     sm : false,
5004     cm : false,
5005     store : false,
5006     loadMask : false,
5007     tfoot : true,
5008     thead : true,
5009     RowSelection : false,
5010     CellSelection : false,
5011     layout : false,
5012     
5013     // Roo.Element - the tbody
5014     mainBody: false, 
5015     
5016     getAutoCreate : function(){
5017         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5018         
5019         cfg = {
5020             tag: 'table',
5021             cls : 'table',
5022             cn : []
5023         }
5024             
5025         if (this.striped) {
5026             cfg.cls += ' table-striped';
5027         }
5028         
5029         if (this.hover) {
5030             cfg.cls += ' table-hover';
5031         }
5032         if (this.bordered) {
5033             cfg.cls += ' table-bordered';
5034         }
5035         if (this.condensed) {
5036             cfg.cls += ' table-condensed';
5037         }
5038         if (this.responsive) {
5039             cfg.cls += ' table-responsive';
5040         }
5041         
5042         if (this.cls) {
5043             cfg.cls+=  ' ' +this.cls;
5044         }
5045         
5046         // this lot should be simplifed...
5047         
5048         if (this.align) {
5049             cfg.align=this.align;
5050         }
5051         if (this.bgcolor) {
5052             cfg.bgcolor=this.bgcolor;
5053         }
5054         if (this.border) {
5055             cfg.border=this.border;
5056         }
5057         if (this.cellpadding) {
5058             cfg.cellpadding=this.cellpadding;
5059         }
5060         if (this.cellspacing) {
5061             cfg.cellspacing=this.cellspacing;
5062         }
5063         if (this.frame) {
5064             cfg.frame=this.frame;
5065         }
5066         if (this.rules) {
5067             cfg.rules=this.rules;
5068         }
5069         if (this.sortable) {
5070             cfg.sortable=this.sortable;
5071         }
5072         if (this.summary) {
5073             cfg.summary=this.summary;
5074         }
5075         if (this.width) {
5076             cfg.width=this.width;
5077         }
5078         if (this.layout) {
5079             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5080         }
5081         
5082         if(this.store || this.cm){
5083             if(this.thead){
5084                 cfg.cn.push(this.renderHeader());
5085             }
5086             
5087             cfg.cn.push(this.renderBody());
5088             
5089             if(this.tfoot){
5090                 cfg.cn.push(this.renderFooter());
5091             }
5092             
5093             cfg.cls+=  ' TableGrid';
5094         }
5095         
5096         return { cn : [ cfg ] };
5097     },
5098     
5099     initEvents : function()
5100     {   
5101         if(!this.store || !this.cm){
5102             return;
5103         }
5104         
5105         //Roo.log('initEvents with ds!!!!');
5106         
5107         this.mainBody = this.el.select('tbody', true).first();
5108         
5109         
5110         var _this = this;
5111         
5112         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5113             e.on('click', _this.sort, _this);
5114         });
5115         
5116         this.el.on("click", this.onClick, this);
5117         this.el.on("dblclick", this.onDblClick, this);
5118         
5119         this.parent().el.setStyle('position', 'relative');
5120         if (this.footer) {
5121             this.footer.parentId = this.id;
5122             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5123         }
5124         
5125         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5126         
5127         this.store.on('load', this.onLoad, this);
5128         this.store.on('beforeload', this.onBeforeLoad, this);
5129         this.store.on('update', this.onUpdate, this);
5130         
5131     },
5132     
5133     onMouseover : function(e, el)
5134     {
5135         var cell = Roo.get(el);
5136         
5137         if(!cell){
5138             return;
5139         }
5140         
5141         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5142             cell = cell.findParent('td', false, true);
5143         }
5144         
5145         var row = cell.findParent('tr', false, true);
5146         var cellIndex = cell.dom.cellIndex;
5147         var rowIndex = row.dom.rowIndex - 1; // start from 0
5148         
5149         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5150         
5151     },
5152     
5153     onMouseout : function(e, el)
5154     {
5155         var cell = Roo.get(el);
5156         
5157         if(!cell){
5158             return;
5159         }
5160         
5161         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5162             cell = cell.findParent('td', false, true);
5163         }
5164         
5165         var row = cell.findParent('tr', false, true);
5166         var cellIndex = cell.dom.cellIndex;
5167         var rowIndex = row.dom.rowIndex - 1; // start from 0
5168         
5169         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5170         
5171     },
5172     
5173     onClick : function(e, el)
5174     {
5175         var cell = Roo.get(el);
5176         
5177         if(!cell || (!this.CellSelection && !this.RowSelection)){
5178             return;
5179         }
5180         
5181         
5182         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5183             cell = cell.findParent('td', false, true);
5184         }
5185         
5186         var row = cell.findParent('tr', false, true);
5187         var cellIndex = cell.dom.cellIndex;
5188         var rowIndex = row.dom.rowIndex - 1;
5189         
5190         if(this.CellSelection){
5191             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5192         }
5193         
5194         if(this.RowSelection){
5195             this.fireEvent('rowclick', this, row, rowIndex, e);
5196         }
5197         
5198         
5199     },
5200     
5201     onDblClick : function(e,el)
5202     {
5203         var cell = Roo.get(el);
5204         
5205         if(!cell || (!this.CellSelection && !this.RowSelection)){
5206             return;
5207         }
5208         
5209         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5210             cell = cell.findParent('td', false, true);
5211         }
5212         
5213         var row = cell.findParent('tr', false, true);
5214         var cellIndex = cell.dom.cellIndex;
5215         var rowIndex = row.dom.rowIndex - 1;
5216         
5217         if(this.CellSelection){
5218             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5219         }
5220         
5221         if(this.RowSelection){
5222             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5223         }
5224     },
5225     
5226     sort : function(e,el)
5227     {
5228         var col = Roo.get(el)
5229         
5230         if(!col.hasClass('sortable')){
5231             return;
5232         }
5233         
5234         var sort = col.attr('sort');
5235         var dir = 'ASC';
5236         
5237         if(col.hasClass('glyphicon-arrow-up')){
5238             dir = 'DESC';
5239         }
5240         
5241         this.store.sortInfo = {field : sort, direction : dir};
5242         
5243         if (this.footer) {
5244             Roo.log("calling footer first");
5245             this.footer.onClick('first');
5246         } else {
5247         
5248             this.store.load({ params : { start : 0 } });
5249         }
5250     },
5251     
5252     renderHeader : function()
5253     {
5254         var header = {
5255             tag: 'thead',
5256             cn : []
5257         };
5258         
5259         var cm = this.cm;
5260         
5261         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5262             
5263             var config = cm.config[i];
5264                     
5265             var c = {
5266                 tag: 'th',
5267                 style : '',
5268                 html: cm.getColumnHeader(i)
5269             };
5270             
5271             if(typeof(config.hidden) != 'undefined' && config.hidden){
5272                 c.style += ' display:none;';
5273             }
5274             
5275             if(typeof(config.dataIndex) != 'undefined'){
5276                 c.sort = config.dataIndex;
5277             }
5278             
5279             if(typeof(config.sortable) != 'undefined' && config.sortable){
5280                 c.cls = 'sortable';
5281             }
5282             
5283             if(typeof(config.align) != 'undefined' && config.align.length){
5284                 c.style += ' text-align:' + config.align + ';';
5285             }
5286             
5287             if(typeof(config.width) != 'undefined'){
5288                 c.style += ' width:' + config.width + 'px;';
5289             }
5290             
5291             header.cn.push(c)
5292         }
5293         
5294         return header;
5295     },
5296     
5297     renderBody : function()
5298     {
5299         var body = {
5300             tag: 'tbody',
5301             cn : [
5302                 {
5303                     tag: 'tr',
5304                     cn : [
5305                         {
5306                             tag : 'td',
5307                             colspan :  this.cm.getColumnCount()
5308                         }
5309                     ]
5310                 }
5311             ]
5312         };
5313         
5314         return body;
5315     },
5316     
5317     renderFooter : function()
5318     {
5319         var footer = {
5320             tag: 'tfoot',
5321             cn : [
5322                 {
5323                     tag: 'tr',
5324                     cn : [
5325                         {
5326                             tag : 'td',
5327                             colspan :  this.cm.getColumnCount()
5328                         }
5329                     ]
5330                 }
5331             ]
5332         };
5333         
5334         return footer;
5335     },
5336     
5337     
5338     
5339     onLoad : function()
5340     {
5341         Roo.log('ds onload');
5342         this.clear();
5343         
5344         var _this = this;
5345         var cm = this.cm;
5346         var ds = this.store;
5347         
5348         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5349             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5350             
5351             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5352                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5353             }
5354             
5355             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5356                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5357             }
5358         });
5359         
5360         var tbody =  this.mainBody;
5361               
5362         if(ds.getCount() > 0){
5363             ds.data.each(function(d,rowIndex){
5364                 var row =  this.renderRow(cm, ds, rowIndex);
5365                 
5366                 tbody.createChild(row);
5367                 
5368                 var _this = this;
5369                 
5370                 if(row.cellObjects.length){
5371                     Roo.each(row.cellObjects, function(r){
5372                         _this.renderCellObject(r);
5373                     })
5374                 }
5375                 
5376             }, this);
5377         }
5378         
5379         Roo.each(this.el.select('tbody td', true).elements, function(e){
5380             e.on('mouseover', _this.onMouseover, _this);
5381         });
5382         
5383         Roo.each(this.el.select('tbody td', true).elements, function(e){
5384             e.on('mouseout', _this.onMouseout, _this);
5385         });
5386
5387         //if(this.loadMask){
5388         //    this.maskEl.hide();
5389         //}
5390     },
5391     
5392     
5393     onUpdate : function(ds,record)
5394     {
5395         this.refreshRow(record);
5396     },
5397     onRemove : function(ds, record, index, isUpdate){
5398         if(isUpdate !== true){
5399             this.fireEvent("beforerowremoved", this, index, record);
5400         }
5401         var bt = this.mainBody.dom;
5402         if(bt.rows[index]){
5403             bt.removeChild(bt.rows[index]);
5404         }
5405         
5406         if(isUpdate !== true){
5407             //this.stripeRows(index);
5408             //this.syncRowHeights(index, index);
5409             //this.layout();
5410             this.fireEvent("rowremoved", this, index, record);
5411         }
5412     },
5413     
5414     
5415     refreshRow : function(record){
5416         var ds = this.store, index;
5417         if(typeof record == 'number'){
5418             index = record;
5419             record = ds.getAt(index);
5420         }else{
5421             index = ds.indexOf(record);
5422         }
5423         this.insertRow(ds, index, true);
5424         this.onRemove(ds, record, index+1, true);
5425         //this.syncRowHeights(index, index);
5426         //this.layout();
5427         this.fireEvent("rowupdated", this, index, record);
5428     },
5429     
5430     insertRow : function(dm, rowIndex, isUpdate){
5431         
5432         if(!isUpdate){
5433             this.fireEvent("beforerowsinserted", this, rowIndex);
5434         }
5435             //var s = this.getScrollState();
5436         var row = this.renderRow(this.cm, this.store, rowIndex);
5437         // insert before rowIndex..
5438         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5439         
5440         var _this = this;
5441                 
5442         if(row.cellObjects.length){
5443             Roo.each(row.cellObjects, function(r){
5444                 _this.renderCellObject(r);
5445             })
5446         }
5447             
5448         if(!isUpdate){
5449             this.fireEvent("rowsinserted", this, rowIndex);
5450             //this.syncRowHeights(firstRow, lastRow);
5451             //this.stripeRows(firstRow);
5452             //this.layout();
5453         }
5454         
5455     },
5456     
5457     
5458     getRowDom : function(rowIndex)
5459     {
5460         // not sure if I need to check this.. but let's do it anyway..
5461         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5462                 this.mainBody.dom.rows[rowIndex] : false
5463     },
5464     // returns the object tree for a tr..
5465   
5466     
5467     renderRow : function(cm, ds, rowIndex) {
5468         
5469         var d = ds.getAt(rowIndex);
5470         
5471         var row = {
5472             tag : 'tr',
5473             cn : []
5474         };
5475             
5476         var cellObjects = [];
5477         
5478         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5479             var config = cm.config[i];
5480             
5481             var renderer = cm.getRenderer(i);
5482             var value = '';
5483             var id = false;
5484             
5485             if(typeof(renderer) !== 'undefined'){
5486                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5487             }
5488             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5489             // and are rendered into the cells after the row is rendered - using the id for the element.
5490             
5491             if(typeof(value) === 'object'){
5492                 id = Roo.id();
5493                 cellObjects.push({
5494                     container : id,
5495                     cfg : value 
5496                 })
5497             }
5498             
5499             var rowcfg = {
5500                 record: d,
5501                 rowIndex : rowIndex,
5502                 colIndex : i,
5503                 rowClass : ''
5504             }
5505
5506             this.fireEvent('rowclass', this, rowcfg);
5507             
5508             var td = {
5509                 tag: 'td',
5510                 cls : rowcfg.rowClass,
5511                 style: '',
5512                 html: (typeof(value) === 'object') ? '' : value
5513             };
5514             
5515             if (id) {
5516                 td.id = id;
5517             }
5518             
5519             if(typeof(config.hidden) != 'undefined' && config.hidden){
5520                 td.style += ' display:none;';
5521             }
5522             
5523             if(typeof(config.align) != 'undefined' && config.align.length){
5524                 td.style += ' text-align:' + config.align + ';';
5525             }
5526             
5527             if(typeof(config.width) != 'undefined'){
5528                 td.style += ' width:' +  config.width + 'px;';
5529             }
5530              
5531             row.cn.push(td);
5532            
5533         }
5534         
5535         row.cellObjects = cellObjects;
5536         
5537         return row;
5538           
5539     },
5540     
5541     
5542     
5543     onBeforeLoad : function()
5544     {
5545         //Roo.log('ds onBeforeLoad');
5546         
5547         //this.clear();
5548         
5549         //if(this.loadMask){
5550         //    this.maskEl.show();
5551         //}
5552     },
5553     
5554     clear : function()
5555     {
5556         this.el.select('tbody', true).first().dom.innerHTML = '';
5557     },
5558     
5559     getSelectionModel : function(){
5560         if(!this.selModel){
5561             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5562         }
5563         return this.selModel;
5564     },
5565     /*
5566      * Render the Roo.bootstrap object from renderder
5567      */
5568     renderCellObject : function(r)
5569     {
5570         var _this = this;
5571         
5572         var t = r.cfg.render(r.container);
5573         
5574         if(r.cfg.cn){
5575             Roo.each(r.cfg.cn, function(c){
5576                 var child = {
5577                     container: t.getChildContainer(),
5578                     cfg: c
5579                 }
5580                 _this.renderCellObject(child);
5581             })
5582         }
5583     }
5584    
5585 });
5586
5587  
5588
5589  /*
5590  * - LGPL
5591  *
5592  * table cell
5593  * 
5594  */
5595
5596 /**
5597  * @class Roo.bootstrap.TableCell
5598  * @extends Roo.bootstrap.Component
5599  * Bootstrap TableCell class
5600  * @cfg {String} html cell contain text
5601  * @cfg {String} cls cell class
5602  * @cfg {String} tag cell tag (td|th) default td
5603  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5604  * @cfg {String} align Aligns the content in a cell
5605  * @cfg {String} axis Categorizes cells
5606  * @cfg {String} bgcolor Specifies the background color of a cell
5607  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5608  * @cfg {Number} colspan Specifies the number of columns a cell should span
5609  * @cfg {String} headers Specifies one or more header cells a cell is related to
5610  * @cfg {Number} height Sets the height of a cell
5611  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5612  * @cfg {Number} rowspan Sets the number of rows a cell should span
5613  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5614  * @cfg {String} valign Vertical aligns the content in a cell
5615  * @cfg {Number} width Specifies the width of a cell
5616  * 
5617  * @constructor
5618  * Create a new TableCell
5619  * @param {Object} config The config object
5620  */
5621
5622 Roo.bootstrap.TableCell = function(config){
5623     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5624 };
5625
5626 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5627     
5628     html: false,
5629     cls: false,
5630     tag: false,
5631     abbr: false,
5632     align: false,
5633     axis: false,
5634     bgcolor: false,
5635     charoff: false,
5636     colspan: false,
5637     headers: false,
5638     height: false,
5639     nowrap: false,
5640     rowspan: false,
5641     scope: false,
5642     valign: false,
5643     width: false,
5644     
5645     
5646     getAutoCreate : function(){
5647         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5648         
5649         cfg = {
5650             tag: 'td'
5651         }
5652         
5653         if(this.tag){
5654             cfg.tag = this.tag;
5655         }
5656         
5657         if (this.html) {
5658             cfg.html=this.html
5659         }
5660         if (this.cls) {
5661             cfg.cls=this.cls
5662         }
5663         if (this.abbr) {
5664             cfg.abbr=this.abbr
5665         }
5666         if (this.align) {
5667             cfg.align=this.align
5668         }
5669         if (this.axis) {
5670             cfg.axis=this.axis
5671         }
5672         if (this.bgcolor) {
5673             cfg.bgcolor=this.bgcolor
5674         }
5675         if (this.charoff) {
5676             cfg.charoff=this.charoff
5677         }
5678         if (this.colspan) {
5679             cfg.colspan=this.colspan
5680         }
5681         if (this.headers) {
5682             cfg.headers=this.headers
5683         }
5684         if (this.height) {
5685             cfg.height=this.height
5686         }
5687         if (this.nowrap) {
5688             cfg.nowrap=this.nowrap
5689         }
5690         if (this.rowspan) {
5691             cfg.rowspan=this.rowspan
5692         }
5693         if (this.scope) {
5694             cfg.scope=this.scope
5695         }
5696         if (this.valign) {
5697             cfg.valign=this.valign
5698         }
5699         if (this.width) {
5700             cfg.width=this.width
5701         }
5702         
5703         
5704         return cfg;
5705     }
5706    
5707 });
5708
5709  
5710
5711  /*
5712  * - LGPL
5713  *
5714  * table row
5715  * 
5716  */
5717
5718 /**
5719  * @class Roo.bootstrap.TableRow
5720  * @extends Roo.bootstrap.Component
5721  * Bootstrap TableRow class
5722  * @cfg {String} cls row class
5723  * @cfg {String} align Aligns the content in a table row
5724  * @cfg {String} bgcolor Specifies a background color for a table row
5725  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5726  * @cfg {String} valign Vertical aligns the content in a table row
5727  * 
5728  * @constructor
5729  * Create a new TableRow
5730  * @param {Object} config The config object
5731  */
5732
5733 Roo.bootstrap.TableRow = function(config){
5734     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5735 };
5736
5737 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5738     
5739     cls: false,
5740     align: false,
5741     bgcolor: false,
5742     charoff: false,
5743     valign: false,
5744     
5745     getAutoCreate : function(){
5746         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5747         
5748         cfg = {
5749             tag: 'tr'
5750         }
5751             
5752         if(this.cls){
5753             cfg.cls = this.cls;
5754         }
5755         if(this.align){
5756             cfg.align = this.align;
5757         }
5758         if(this.bgcolor){
5759             cfg.bgcolor = this.bgcolor;
5760         }
5761         if(this.charoff){
5762             cfg.charoff = this.charoff;
5763         }
5764         if(this.valign){
5765             cfg.valign = this.valign;
5766         }
5767         
5768         return cfg;
5769     }
5770    
5771 });
5772
5773  
5774
5775  /*
5776  * - LGPL
5777  *
5778  * table body
5779  * 
5780  */
5781
5782 /**
5783  * @class Roo.bootstrap.TableBody
5784  * @extends Roo.bootstrap.Component
5785  * Bootstrap TableBody class
5786  * @cfg {String} cls element class
5787  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5788  * @cfg {String} align Aligns the content inside the element
5789  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5790  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5791  * 
5792  * @constructor
5793  * Create a new TableBody
5794  * @param {Object} config The config object
5795  */
5796
5797 Roo.bootstrap.TableBody = function(config){
5798     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5799 };
5800
5801 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5802     
5803     cls: false,
5804     tag: false,
5805     align: false,
5806     charoff: false,
5807     valign: false,
5808     
5809     getAutoCreate : function(){
5810         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5811         
5812         cfg = {
5813             tag: 'tbody'
5814         }
5815             
5816         if (this.cls) {
5817             cfg.cls=this.cls
5818         }
5819         if(this.tag){
5820             cfg.tag = this.tag;
5821         }
5822         
5823         if(this.align){
5824             cfg.align = this.align;
5825         }
5826         if(this.charoff){
5827             cfg.charoff = this.charoff;
5828         }
5829         if(this.valign){
5830             cfg.valign = this.valign;
5831         }
5832         
5833         return cfg;
5834     }
5835     
5836     
5837 //    initEvents : function()
5838 //    {
5839 //        
5840 //        if(!this.store){
5841 //            return;
5842 //        }
5843 //        
5844 //        this.store = Roo.factory(this.store, Roo.data);
5845 //        this.store.on('load', this.onLoad, this);
5846 //        
5847 //        this.store.load();
5848 //        
5849 //    },
5850 //    
5851 //    onLoad: function () 
5852 //    {   
5853 //        this.fireEvent('load', this);
5854 //    }
5855 //    
5856 //   
5857 });
5858
5859  
5860
5861  /*
5862  * Based on:
5863  * Ext JS Library 1.1.1
5864  * Copyright(c) 2006-2007, Ext JS, LLC.
5865  *
5866  * Originally Released Under LGPL - original licence link has changed is not relivant.
5867  *
5868  * Fork - LGPL
5869  * <script type="text/javascript">
5870  */
5871
5872 // as we use this in bootstrap.
5873 Roo.namespace('Roo.form');
5874  /**
5875  * @class Roo.form.Action
5876  * Internal Class used to handle form actions
5877  * @constructor
5878  * @param {Roo.form.BasicForm} el The form element or its id
5879  * @param {Object} config Configuration options
5880  */
5881
5882  
5883  
5884 // define the action interface
5885 Roo.form.Action = function(form, options){
5886     this.form = form;
5887     this.options = options || {};
5888 };
5889 /**
5890  * Client Validation Failed
5891  * @const 
5892  */
5893 Roo.form.Action.CLIENT_INVALID = 'client';
5894 /**
5895  * Server Validation Failed
5896  * @const 
5897  */
5898 Roo.form.Action.SERVER_INVALID = 'server';
5899  /**
5900  * Connect to Server Failed
5901  * @const 
5902  */
5903 Roo.form.Action.CONNECT_FAILURE = 'connect';
5904 /**
5905  * Reading Data from Server Failed
5906  * @const 
5907  */
5908 Roo.form.Action.LOAD_FAILURE = 'load';
5909
5910 Roo.form.Action.prototype = {
5911     type : 'default',
5912     failureType : undefined,
5913     response : undefined,
5914     result : undefined,
5915
5916     // interface method
5917     run : function(options){
5918
5919     },
5920
5921     // interface method
5922     success : function(response){
5923
5924     },
5925
5926     // interface method
5927     handleResponse : function(response){
5928
5929     },
5930
5931     // default connection failure
5932     failure : function(response){
5933         
5934         this.response = response;
5935         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5936         this.form.afterAction(this, false);
5937     },
5938
5939     processResponse : function(response){
5940         this.response = response;
5941         if(!response.responseText){
5942             return true;
5943         }
5944         this.result = this.handleResponse(response);
5945         return this.result;
5946     },
5947
5948     // utility functions used internally
5949     getUrl : function(appendParams){
5950         var url = this.options.url || this.form.url || this.form.el.dom.action;
5951         if(appendParams){
5952             var p = this.getParams();
5953             if(p){
5954                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5955             }
5956         }
5957         return url;
5958     },
5959
5960     getMethod : function(){
5961         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5962     },
5963
5964     getParams : function(){
5965         var bp = this.form.baseParams;
5966         var p = this.options.params;
5967         if(p){
5968             if(typeof p == "object"){
5969                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5970             }else if(typeof p == 'string' && bp){
5971                 p += '&' + Roo.urlEncode(bp);
5972             }
5973         }else if(bp){
5974             p = Roo.urlEncode(bp);
5975         }
5976         return p;
5977     },
5978
5979     createCallback : function(){
5980         return {
5981             success: this.success,
5982             failure: this.failure,
5983             scope: this,
5984             timeout: (this.form.timeout*1000),
5985             upload: this.form.fileUpload ? this.success : undefined
5986         };
5987     }
5988 };
5989
5990 Roo.form.Action.Submit = function(form, options){
5991     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5992 };
5993
5994 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5995     type : 'submit',
5996
5997     haveProgress : false,
5998     uploadComplete : false,
5999     
6000     // uploadProgress indicator.
6001     uploadProgress : function()
6002     {
6003         if (!this.form.progressUrl) {
6004             return;
6005         }
6006         
6007         if (!this.haveProgress) {
6008             Roo.MessageBox.progress("Uploading", "Uploading");
6009         }
6010         if (this.uploadComplete) {
6011            Roo.MessageBox.hide();
6012            return;
6013         }
6014         
6015         this.haveProgress = true;
6016    
6017         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6018         
6019         var c = new Roo.data.Connection();
6020         c.request({
6021             url : this.form.progressUrl,
6022             params: {
6023                 id : uid
6024             },
6025             method: 'GET',
6026             success : function(req){
6027                //console.log(data);
6028                 var rdata = false;
6029                 var edata;
6030                 try  {
6031                    rdata = Roo.decode(req.responseText)
6032                 } catch (e) {
6033                     Roo.log("Invalid data from server..");
6034                     Roo.log(edata);
6035                     return;
6036                 }
6037                 if (!rdata || !rdata.success) {
6038                     Roo.log(rdata);
6039                     Roo.MessageBox.alert(Roo.encode(rdata));
6040                     return;
6041                 }
6042                 var data = rdata.data;
6043                 
6044                 if (this.uploadComplete) {
6045                    Roo.MessageBox.hide();
6046                    return;
6047                 }
6048                    
6049                 if (data){
6050                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6051                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6052                     );
6053                 }
6054                 this.uploadProgress.defer(2000,this);
6055             },
6056        
6057             failure: function(data) {
6058                 Roo.log('progress url failed ');
6059                 Roo.log(data);
6060             },
6061             scope : this
6062         });
6063            
6064     },
6065     
6066     
6067     run : function()
6068     {
6069         // run get Values on the form, so it syncs any secondary forms.
6070         this.form.getValues();
6071         
6072         var o = this.options;
6073         var method = this.getMethod();
6074         var isPost = method == 'POST';
6075         if(o.clientValidation === false || this.form.isValid()){
6076             
6077             if (this.form.progressUrl) {
6078                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6079                     (new Date() * 1) + '' + Math.random());
6080                     
6081             } 
6082             
6083             
6084             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6085                 form:this.form.el.dom,
6086                 url:this.getUrl(!isPost),
6087                 method: method,
6088                 params:isPost ? this.getParams() : null,
6089                 isUpload: this.form.fileUpload
6090             }));
6091             
6092             this.uploadProgress();
6093
6094         }else if (o.clientValidation !== false){ // client validation failed
6095             this.failureType = Roo.form.Action.CLIENT_INVALID;
6096             this.form.afterAction(this, false);
6097         }
6098     },
6099
6100     success : function(response)
6101     {
6102         this.uploadComplete= true;
6103         if (this.haveProgress) {
6104             Roo.MessageBox.hide();
6105         }
6106         
6107         
6108         var result = this.processResponse(response);
6109         if(result === true || result.success){
6110             this.form.afterAction(this, true);
6111             return;
6112         }
6113         if(result.errors){
6114             this.form.markInvalid(result.errors);
6115             this.failureType = Roo.form.Action.SERVER_INVALID;
6116         }
6117         this.form.afterAction(this, false);
6118     },
6119     failure : function(response)
6120     {
6121         this.uploadComplete= true;
6122         if (this.haveProgress) {
6123             Roo.MessageBox.hide();
6124         }
6125         
6126         this.response = response;
6127         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6128         this.form.afterAction(this, false);
6129     },
6130     
6131     handleResponse : function(response){
6132         if(this.form.errorReader){
6133             var rs = this.form.errorReader.read(response);
6134             var errors = [];
6135             if(rs.records){
6136                 for(var i = 0, len = rs.records.length; i < len; i++) {
6137                     var r = rs.records[i];
6138                     errors[i] = r.data;
6139                 }
6140             }
6141             if(errors.length < 1){
6142                 errors = null;
6143             }
6144             return {
6145                 success : rs.success,
6146                 errors : errors
6147             };
6148         }
6149         var ret = false;
6150         try {
6151             ret = Roo.decode(response.responseText);
6152         } catch (e) {
6153             ret = {
6154                 success: false,
6155                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6156                 errors : []
6157             };
6158         }
6159         return ret;
6160         
6161     }
6162 });
6163
6164
6165 Roo.form.Action.Load = function(form, options){
6166     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6167     this.reader = this.form.reader;
6168 };
6169
6170 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6171     type : 'load',
6172
6173     run : function(){
6174         
6175         Roo.Ajax.request(Roo.apply(
6176                 this.createCallback(), {
6177                     method:this.getMethod(),
6178                     url:this.getUrl(false),
6179                     params:this.getParams()
6180         }));
6181     },
6182
6183     success : function(response){
6184         
6185         var result = this.processResponse(response);
6186         if(result === true || !result.success || !result.data){
6187             this.failureType = Roo.form.Action.LOAD_FAILURE;
6188             this.form.afterAction(this, false);
6189             return;
6190         }
6191         this.form.clearInvalid();
6192         this.form.setValues(result.data);
6193         this.form.afterAction(this, true);
6194     },
6195
6196     handleResponse : function(response){
6197         if(this.form.reader){
6198             var rs = this.form.reader.read(response);
6199             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6200             return {
6201                 success : rs.success,
6202                 data : data
6203             };
6204         }
6205         return Roo.decode(response.responseText);
6206     }
6207 });
6208
6209 Roo.form.Action.ACTION_TYPES = {
6210     'load' : Roo.form.Action.Load,
6211     'submit' : Roo.form.Action.Submit
6212 };/*
6213  * - LGPL
6214  *
6215  * form
6216  * 
6217  */
6218
6219 /**
6220  * @class Roo.bootstrap.Form
6221  * @extends Roo.bootstrap.Component
6222  * Bootstrap Form class
6223  * @cfg {String} method  GET | POST (default POST)
6224  * @cfg {String} labelAlign top | left (default top)
6225  * @cfg {String} align left  | right - for navbars
6226  * @cfg {Boolean} loadMask load mask when submit (default true)
6227
6228  * 
6229  * @constructor
6230  * Create a new Form
6231  * @param {Object} config The config object
6232  */
6233
6234
6235 Roo.bootstrap.Form = function(config){
6236     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6237     this.addEvents({
6238         /**
6239          * @event clientvalidation
6240          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6241          * @param {Form} this
6242          * @param {Boolean} valid true if the form has passed client-side validation
6243          */
6244         clientvalidation: true,
6245         /**
6246          * @event beforeaction
6247          * Fires before any action is performed. Return false to cancel the action.
6248          * @param {Form} this
6249          * @param {Action} action The action to be performed
6250          */
6251         beforeaction: true,
6252         /**
6253          * @event actionfailed
6254          * Fires when an action fails.
6255          * @param {Form} this
6256          * @param {Action} action The action that failed
6257          */
6258         actionfailed : true,
6259         /**
6260          * @event actioncomplete
6261          * Fires when an action is completed.
6262          * @param {Form} this
6263          * @param {Action} action The action that completed
6264          */
6265         actioncomplete : true
6266     });
6267     
6268 };
6269
6270 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6271       
6272      /**
6273      * @cfg {String} method
6274      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6275      */
6276     method : 'POST',
6277     /**
6278      * @cfg {String} url
6279      * The URL to use for form actions if one isn't supplied in the action options.
6280      */
6281     /**
6282      * @cfg {Boolean} fileUpload
6283      * Set to true if this form is a file upload.
6284      */
6285      
6286     /**
6287      * @cfg {Object} baseParams
6288      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6289      */
6290       
6291     /**
6292      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6293      */
6294     timeout: 30,
6295     /**
6296      * @cfg {Sting} align (left|right) for navbar forms
6297      */
6298     align : 'left',
6299
6300     // private
6301     activeAction : null,
6302  
6303     /**
6304      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6305      * element by passing it or its id or mask the form itself by passing in true.
6306      * @type Mixed
6307      */
6308     waitMsgTarget : false,
6309     
6310     loadMask : true,
6311     
6312     getAutoCreate : function(){
6313         
6314         var cfg = {
6315             tag: 'form',
6316             method : this.method || 'POST',
6317             id : this.id || Roo.id(),
6318             cls : ''
6319         }
6320         if (this.parent().xtype.match(/^Nav/)) {
6321             cfg.cls = 'navbar-form navbar-' + this.align;
6322             
6323         }
6324         
6325         if (this.labelAlign == 'left' ) {
6326             cfg.cls += ' form-horizontal';
6327         }
6328         
6329         
6330         return cfg;
6331     },
6332     initEvents : function()
6333     {
6334         this.el.on('submit', this.onSubmit, this);
6335         // this was added as random key presses on the form where triggering form submit.
6336         this.el.on('keypress', function(e) {
6337             if (e.getCharCode() != 13) {
6338                 return true;
6339             }
6340             // we might need to allow it for textareas.. and some other items.
6341             // check e.getTarget().
6342             
6343             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6344                 return true;
6345             }
6346         
6347             Roo.log("keypress blocked");
6348             
6349             e.preventDefault();
6350             return false;
6351         });
6352         
6353     },
6354     // private
6355     onSubmit : function(e){
6356         e.stopEvent();
6357     },
6358     
6359      /**
6360      * Returns true if client-side validation on the form is successful.
6361      * @return Boolean
6362      */
6363     isValid : function(){
6364         var items = this.getItems();
6365         var valid = true;
6366         items.each(function(f){
6367            if(!f.validate()){
6368                valid = false;
6369                
6370            }
6371         });
6372         return valid;
6373     },
6374     /**
6375      * Returns true if any fields in this form have changed since their original load.
6376      * @return Boolean
6377      */
6378     isDirty : function(){
6379         var dirty = false;
6380         var items = this.getItems();
6381         items.each(function(f){
6382            if(f.isDirty()){
6383                dirty = true;
6384                return false;
6385            }
6386            return true;
6387         });
6388         return dirty;
6389     },
6390      /**
6391      * Performs a predefined action (submit or load) or custom actions you define on this form.
6392      * @param {String} actionName The name of the action type
6393      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6394      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6395      * accept other config options):
6396      * <pre>
6397 Property          Type             Description
6398 ----------------  ---------------  ----------------------------------------------------------------------------------
6399 url               String           The url for the action (defaults to the form's url)
6400 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6401 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6402 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6403                                    validate the form on the client (defaults to false)
6404      * </pre>
6405      * @return {BasicForm} this
6406      */
6407     doAction : function(action, options){
6408         if(typeof action == 'string'){
6409             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6410         }
6411         if(this.fireEvent('beforeaction', this, action) !== false){
6412             this.beforeAction(action);
6413             action.run.defer(100, action);
6414         }
6415         return this;
6416     },
6417     
6418     // private
6419     beforeAction : function(action){
6420         var o = action.options;
6421         
6422         if(this.loadMask){
6423             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6424         }
6425         // not really supported yet.. ??
6426         
6427         //if(this.waitMsgTarget === true){
6428         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6429         //}else if(this.waitMsgTarget){
6430         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6431         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6432         //}else {
6433         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6434        // }
6435          
6436     },
6437
6438     // private
6439     afterAction : function(action, success){
6440         this.activeAction = null;
6441         var o = action.options;
6442         
6443         //if(this.waitMsgTarget === true){
6444             this.el.unmask();
6445         //}else if(this.waitMsgTarget){
6446         //    this.waitMsgTarget.unmask();
6447         //}else{
6448         //    Roo.MessageBox.updateProgress(1);
6449         //    Roo.MessageBox.hide();
6450        // }
6451         // 
6452         if(success){
6453             if(o.reset){
6454                 this.reset();
6455             }
6456             Roo.callback(o.success, o.scope, [this, action]);
6457             this.fireEvent('actioncomplete', this, action);
6458             
6459         }else{
6460             
6461             // failure condition..
6462             // we have a scenario where updates need confirming.
6463             // eg. if a locking scenario exists..
6464             // we look for { errors : { needs_confirm : true }} in the response.
6465             if (
6466                 (typeof(action.result) != 'undefined')  &&
6467                 (typeof(action.result.errors) != 'undefined')  &&
6468                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6469            ){
6470                 var _t = this;
6471                 Roo.log("not supported yet");
6472                  /*
6473                 
6474                 Roo.MessageBox.confirm(
6475                     "Change requires confirmation",
6476                     action.result.errorMsg,
6477                     function(r) {
6478                         if (r != 'yes') {
6479                             return;
6480                         }
6481                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6482                     }
6483                     
6484                 );
6485                 */
6486                 
6487                 
6488                 return;
6489             }
6490             
6491             Roo.callback(o.failure, o.scope, [this, action]);
6492             // show an error message if no failed handler is set..
6493             if (!this.hasListener('actionfailed')) {
6494                 Roo.log("need to add dialog support");
6495                 /*
6496                 Roo.MessageBox.alert("Error",
6497                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6498                         action.result.errorMsg :
6499                         "Saving Failed, please check your entries or try again"
6500                 );
6501                 */
6502             }
6503             
6504             this.fireEvent('actionfailed', this, action);
6505         }
6506         
6507     },
6508     /**
6509      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6510      * @param {String} id The value to search for
6511      * @return Field
6512      */
6513     findField : function(id){
6514         var items = this.getItems();
6515         var field = items.get(id);
6516         if(!field){
6517              items.each(function(f){
6518                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6519                     field = f;
6520                     return false;
6521                 }
6522                 return true;
6523             });
6524         }
6525         return field || null;
6526     },
6527      /**
6528      * Mark fields in this form invalid in bulk.
6529      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6530      * @return {BasicForm} this
6531      */
6532     markInvalid : function(errors){
6533         if(errors instanceof Array){
6534             for(var i = 0, len = errors.length; i < len; i++){
6535                 var fieldError = errors[i];
6536                 var f = this.findField(fieldError.id);
6537                 if(f){
6538                     f.markInvalid(fieldError.msg);
6539                 }
6540             }
6541         }else{
6542             var field, id;
6543             for(id in errors){
6544                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6545                     field.markInvalid(errors[id]);
6546                 }
6547             }
6548         }
6549         //Roo.each(this.childForms || [], function (f) {
6550         //    f.markInvalid(errors);
6551         //});
6552         
6553         return this;
6554     },
6555
6556     /**
6557      * Set values for fields in this form in bulk.
6558      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6559      * @return {BasicForm} this
6560      */
6561     setValues : function(values){
6562         if(values instanceof Array){ // array of objects
6563             for(var i = 0, len = values.length; i < len; i++){
6564                 var v = values[i];
6565                 var f = this.findField(v.id);
6566                 if(f){
6567                     f.setValue(v.value);
6568                     if(this.trackResetOnLoad){
6569                         f.originalValue = f.getValue();
6570                     }
6571                 }
6572             }
6573         }else{ // object hash
6574             var field, id;
6575             for(id in values){
6576                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6577                     
6578                     if (field.setFromData && 
6579                         field.valueField && 
6580                         field.displayField &&
6581                         // combos' with local stores can 
6582                         // be queried via setValue()
6583                         // to set their value..
6584                         (field.store && !field.store.isLocal)
6585                         ) {
6586                         // it's a combo
6587                         var sd = { };
6588                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6589                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6590                         field.setFromData(sd);
6591                         
6592                     } else {
6593                         field.setValue(values[id]);
6594                     }
6595                     
6596                     
6597                     if(this.trackResetOnLoad){
6598                         field.originalValue = field.getValue();
6599                     }
6600                 }
6601             }
6602         }
6603          
6604         //Roo.each(this.childForms || [], function (f) {
6605         //    f.setValues(values);
6606         //});
6607                 
6608         return this;
6609     },
6610
6611     /**
6612      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6613      * they are returned as an array.
6614      * @param {Boolean} asString
6615      * @return {Object}
6616      */
6617     getValues : function(asString){
6618         //if (this.childForms) {
6619             // copy values from the child forms
6620         //    Roo.each(this.childForms, function (f) {
6621         //        this.setValues(f.getValues());
6622         //    }, this);
6623         //}
6624         
6625         
6626         
6627         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6628         if(asString === true){
6629             return fs;
6630         }
6631         return Roo.urlDecode(fs);
6632     },
6633     
6634     /**
6635      * Returns the fields in this form as an object with key/value pairs. 
6636      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6637      * @return {Object}
6638      */
6639     getFieldValues : function(with_hidden)
6640     {
6641         var items = this.getItems();
6642         var ret = {};
6643         items.each(function(f){
6644             if (!f.getName()) {
6645                 return;
6646             }
6647             var v = f.getValue();
6648             if (f.inputType =='radio') {
6649                 if (typeof(ret[f.getName()]) == 'undefined') {
6650                     ret[f.getName()] = ''; // empty..
6651                 }
6652                 
6653                 if (!f.el.dom.checked) {
6654                     return;
6655                     
6656                 }
6657                 v = f.el.dom.value;
6658                 
6659             }
6660             
6661             // not sure if this supported any more..
6662             if ((typeof(v) == 'object') && f.getRawValue) {
6663                 v = f.getRawValue() ; // dates..
6664             }
6665             // combo boxes where name != hiddenName...
6666             if (f.name != f.getName()) {
6667                 ret[f.name] = f.getRawValue();
6668             }
6669             ret[f.getName()] = v;
6670         });
6671         
6672         return ret;
6673     },
6674
6675     /**
6676      * Clears all invalid messages in this form.
6677      * @return {BasicForm} this
6678      */
6679     clearInvalid : function(){
6680         var items = this.getItems();
6681         
6682         items.each(function(f){
6683            f.clearInvalid();
6684         });
6685         
6686         
6687         
6688         return this;
6689     },
6690
6691     /**
6692      * Resets this form.
6693      * @return {BasicForm} this
6694      */
6695     reset : function(){
6696         var items = this.getItems();
6697         items.each(function(f){
6698             f.reset();
6699         });
6700         
6701         Roo.each(this.childForms || [], function (f) {
6702             f.reset();
6703         });
6704        
6705         
6706         return this;
6707     },
6708     getItems : function()
6709     {
6710         var r=new Roo.util.MixedCollection(false, function(o){
6711             return o.id || (o.id = Roo.id());
6712         });
6713         var iter = function(el) {
6714             if (el.inputEl) {
6715                 r.add(el);
6716             }
6717             if (!el.items) {
6718                 return;
6719             }
6720             Roo.each(el.items,function(e) {
6721                 iter(e);
6722             });
6723             
6724             
6725         };
6726         iter(this);
6727         return r;
6728         
6729         
6730         
6731         
6732     }
6733     
6734 });
6735
6736  
6737 /*
6738  * Based on:
6739  * Ext JS Library 1.1.1
6740  * Copyright(c) 2006-2007, Ext JS, LLC.
6741  *
6742  * Originally Released Under LGPL - original licence link has changed is not relivant.
6743  *
6744  * Fork - LGPL
6745  * <script type="text/javascript">
6746  */
6747 /**
6748  * @class Roo.form.VTypes
6749  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6750  * @singleton
6751  */
6752 Roo.form.VTypes = function(){
6753     // closure these in so they are only created once.
6754     var alpha = /^[a-zA-Z_]+$/;
6755     var alphanum = /^[a-zA-Z0-9_]+$/;
6756     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6757     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6758
6759     // All these messages and functions are configurable
6760     return {
6761         /**
6762          * The function used to validate email addresses
6763          * @param {String} value The email address
6764          */
6765         'email' : function(v){
6766             return email.test(v);
6767         },
6768         /**
6769          * The error text to display when the email validation function returns false
6770          * @type String
6771          */
6772         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6773         /**
6774          * The keystroke filter mask to be applied on email input
6775          * @type RegExp
6776          */
6777         'emailMask' : /[a-z0-9_\.\-@]/i,
6778
6779         /**
6780          * The function used to validate URLs
6781          * @param {String} value The URL
6782          */
6783         'url' : function(v){
6784             return url.test(v);
6785         },
6786         /**
6787          * The error text to display when the url validation function returns false
6788          * @type String
6789          */
6790         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6791         
6792         /**
6793          * The function used to validate alpha values
6794          * @param {String} value The value
6795          */
6796         'alpha' : function(v){
6797             return alpha.test(v);
6798         },
6799         /**
6800          * The error text to display when the alpha validation function returns false
6801          * @type String
6802          */
6803         'alphaText' : 'This field should only contain letters and _',
6804         /**
6805          * The keystroke filter mask to be applied on alpha input
6806          * @type RegExp
6807          */
6808         'alphaMask' : /[a-z_]/i,
6809
6810         /**
6811          * The function used to validate alphanumeric values
6812          * @param {String} value The value
6813          */
6814         'alphanum' : function(v){
6815             return alphanum.test(v);
6816         },
6817         /**
6818          * The error text to display when the alphanumeric validation function returns false
6819          * @type String
6820          */
6821         'alphanumText' : 'This field should only contain letters, numbers and _',
6822         /**
6823          * The keystroke filter mask to be applied on alphanumeric input
6824          * @type RegExp
6825          */
6826         'alphanumMask' : /[a-z0-9_]/i
6827     };
6828 }();/*
6829  * - LGPL
6830  *
6831  * Input
6832  * 
6833  */
6834
6835 /**
6836  * @class Roo.bootstrap.Input
6837  * @extends Roo.bootstrap.Component
6838  * Bootstrap Input class
6839  * @cfg {Boolean} disabled is it disabled
6840  * @cfg {String} fieldLabel - the label associated
6841  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6842  * @cfg {String} name name of the input
6843  * @cfg {string} fieldLabel - the label associated
6844  * @cfg {string}  inputType - input / file submit ...
6845  * @cfg {string} placeholder - placeholder to put in text.
6846  * @cfg {string}  before - input group add on before
6847  * @cfg {string} after - input group add on after
6848  * @cfg {string} size - (lg|sm) or leave empty..
6849  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6850  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6851  * @cfg {Number} md colspan out of 12 for computer-sized screens
6852  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6853  * @cfg {string} value default value of the input
6854  * @cfg {Number} labelWidth set the width of label (0-12)
6855  * @cfg {String} labelAlign (top|left)
6856  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6857  * @cfg {String} align (left|center|right) Default left
6858  * 
6859  * 
6860  * @constructor
6861  * Create a new Input
6862  * @param {Object} config The config object
6863  */
6864
6865 Roo.bootstrap.Input = function(config){
6866     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6867    
6868         this.addEvents({
6869             /**
6870              * @event focus
6871              * Fires when this field receives input focus.
6872              * @param {Roo.form.Field} this
6873              */
6874             focus : true,
6875             /**
6876              * @event blur
6877              * Fires when this field loses input focus.
6878              * @param {Roo.form.Field} this
6879              */
6880             blur : true,
6881             /**
6882              * @event specialkey
6883              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6884              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6885              * @param {Roo.form.Field} this
6886              * @param {Roo.EventObject} e The event object
6887              */
6888             specialkey : true,
6889             /**
6890              * @event change
6891              * Fires just before the field blurs if the field value has changed.
6892              * @param {Roo.form.Field} this
6893              * @param {Mixed} newValue The new value
6894              * @param {Mixed} oldValue The original value
6895              */
6896             change : true,
6897             /**
6898              * @event invalid
6899              * Fires after the field has been marked as invalid.
6900              * @param {Roo.form.Field} this
6901              * @param {String} msg The validation message
6902              */
6903             invalid : true,
6904             /**
6905              * @event valid
6906              * Fires after the field has been validated with no errors.
6907              * @param {Roo.form.Field} this
6908              */
6909             valid : true,
6910              /**
6911              * @event keyup
6912              * Fires after the key up
6913              * @param {Roo.form.Field} this
6914              * @param {Roo.EventObject}  e The event Object
6915              */
6916             keyup : true
6917         });
6918 };
6919
6920 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6921      /**
6922      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6923       automatic validation (defaults to "keyup").
6924      */
6925     validationEvent : "keyup",
6926      /**
6927      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6928      */
6929     validateOnBlur : true,
6930     /**
6931      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6932      */
6933     validationDelay : 250,
6934      /**
6935      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6936      */
6937     focusClass : "x-form-focus",  // not needed???
6938     
6939        
6940     /**
6941      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6942      */
6943     invalidClass : "has-error",
6944     
6945     /**
6946      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6947      */
6948     selectOnFocus : false,
6949     
6950      /**
6951      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6952      */
6953     maskRe : null,
6954        /**
6955      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6956      */
6957     vtype : null,
6958     
6959       /**
6960      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6961      */
6962     disableKeyFilter : false,
6963     
6964        /**
6965      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6966      */
6967     disabled : false,
6968      /**
6969      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6970      */
6971     allowBlank : true,
6972     /**
6973      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6974      */
6975     blankText : "This field is required",
6976     
6977      /**
6978      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6979      */
6980     minLength : 0,
6981     /**
6982      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6983      */
6984     maxLength : Number.MAX_VALUE,
6985     /**
6986      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6987      */
6988     minLengthText : "The minimum length for this field is {0}",
6989     /**
6990      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6991      */
6992     maxLengthText : "The maximum length for this field is {0}",
6993   
6994     
6995     /**
6996      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6997      * If available, this function will be called only after the basic validators all return true, and will be passed the
6998      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6999      */
7000     validator : null,
7001     /**
7002      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7003      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7004      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7005      */
7006     regex : null,
7007     /**
7008      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7009      */
7010     regexText : "",
7011     
7012     
7013     
7014     fieldLabel : '',
7015     inputType : 'text',
7016     
7017     name : false,
7018     placeholder: false,
7019     before : false,
7020     after : false,
7021     size : false,
7022     // private
7023     hasFocus : false,
7024     preventMark: false,
7025     isFormField : true,
7026     value : '',
7027     labelWidth : 2,
7028     labelAlign : false,
7029     readOnly : false,
7030     align : false,
7031     formatedValue : false,
7032     
7033     parentLabelAlign : function()
7034     {
7035         var parent = this;
7036         while (parent.parent()) {
7037             parent = parent.parent();
7038             if (typeof(parent.labelAlign) !='undefined') {
7039                 return parent.labelAlign;
7040             }
7041         }
7042         return 'left';
7043         
7044     },
7045     
7046     getAutoCreate : function(){
7047         
7048         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7049         
7050         var id = Roo.id();
7051         
7052         var cfg = {};
7053         
7054         if(this.inputType != 'hidden'){
7055             cfg.cls = 'form-group' //input-group
7056         }
7057         
7058         var input =  {
7059             tag: 'input',
7060             id : id,
7061             type : this.inputType,
7062             value : this.value,
7063             cls : 'form-control',
7064             placeholder : this.placeholder || ''
7065             
7066         };
7067         
7068         if(this.align){
7069             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7070         }
7071         
7072         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7073             input.maxLength = this.maxLength;
7074         }
7075         
7076         if (this.disabled) {
7077             input.disabled=true;
7078         }
7079         
7080         if (this.readOnly) {
7081             input.readonly=true;
7082         }
7083         
7084         if (this.name) {
7085             input.name = this.name;
7086         }
7087         if (this.size) {
7088             input.cls += ' input-' + this.size;
7089         }
7090         var settings=this;
7091         ['xs','sm','md','lg'].map(function(size){
7092             if (settings[size]) {
7093                 cfg.cls += ' col-' + size + '-' + settings[size];
7094             }
7095         });
7096         
7097         var inputblock = input;
7098         
7099         if (this.before || this.after) {
7100             
7101             inputblock = {
7102                 cls : 'input-group',
7103                 cn :  [] 
7104             };
7105             if (this.before && typeof(this.before) == 'string') {
7106                 
7107                 inputblock.cn.push({
7108                     tag :'span',
7109                     cls : 'roo-input-before input-group-addon',
7110                     html : this.before
7111                 });
7112             }
7113             if (this.before && typeof(this.before) == 'object') {
7114                 this.before = Roo.factory(this.before);
7115                 Roo.log(this.before);
7116                 inputblock.cn.push({
7117                     tag :'span',
7118                     cls : 'roo-input-before input-group-' +
7119                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7120                 });
7121             }
7122             
7123             inputblock.cn.push(input);
7124             
7125             if (this.after && typeof(this.after) == 'string') {
7126                 inputblock.cn.push({
7127                     tag :'span',
7128                     cls : 'roo-input-after input-group-addon',
7129                     html : this.after
7130                 });
7131             }
7132             if (this.after && typeof(this.after) == 'object') {
7133                 this.after = Roo.factory(this.after);
7134                 Roo.log(this.after);
7135                 inputblock.cn.push({
7136                     tag :'span',
7137                     cls : 'roo-input-after input-group-' +
7138                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7139                 });
7140             }
7141         };
7142         
7143         if (align ==='left' && this.fieldLabel.length) {
7144                 Roo.log("left and has label");
7145                 cfg.cn = [
7146                     
7147                     {
7148                         tag: 'label',
7149                         'for' :  id,
7150                         cls : 'control-label col-sm-' + this.labelWidth,
7151                         html : this.fieldLabel
7152                         
7153                     },
7154                     {
7155                         cls : "col-sm-" + (12 - this.labelWidth), 
7156                         cn: [
7157                             inputblock
7158                         ]
7159                     }
7160                     
7161                 ];
7162         } else if ( this.fieldLabel.length) {
7163                 Roo.log(" label");
7164                  cfg.cn = [
7165                    
7166                     {
7167                         tag: 'label',
7168                         //cls : 'input-group-addon',
7169                         html : this.fieldLabel
7170                         
7171                     },
7172                     
7173                     inputblock
7174                     
7175                 ];
7176
7177         } else {
7178             
7179                 Roo.log(" no label && no align");
7180                 cfg.cn = [
7181                     
7182                         inputblock
7183                     
7184                 ];
7185                 
7186                 
7187         };
7188         Roo.log('input-parentType: ' + this.parentType);
7189         
7190         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7191            cfg.cls += ' navbar-form';
7192            Roo.log(cfg);
7193         }
7194         
7195         return cfg;
7196         
7197     },
7198     /**
7199      * return the real input element.
7200      */
7201     inputEl: function ()
7202     {
7203         return this.el.select('input.form-control',true).first();
7204     },
7205     setDisabled : function(v)
7206     {
7207         var i  = this.inputEl().dom;
7208         if (!v) {
7209             i.removeAttribute('disabled');
7210             return;
7211             
7212         }
7213         i.setAttribute('disabled','true');
7214     },
7215     initEvents : function()
7216     {
7217         
7218         this.inputEl().on("keydown" , this.fireKey,  this);
7219         this.inputEl().on("focus", this.onFocus,  this);
7220         this.inputEl().on("blur", this.onBlur,  this);
7221         
7222         this.inputEl().relayEvent('keyup', this);
7223
7224         // reference to original value for reset
7225         this.originalValue = this.getValue();
7226         //Roo.form.TextField.superclass.initEvents.call(this);
7227         if(this.validationEvent == 'keyup'){
7228             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7229             this.inputEl().on('keyup', this.filterValidation, this);
7230         }
7231         else if(this.validationEvent !== false){
7232             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7233         }
7234         
7235         if(this.selectOnFocus){
7236             this.on("focus", this.preFocus, this);
7237             
7238         }
7239         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7240             this.inputEl().on("keypress", this.filterKeys, this);
7241         }
7242        /* if(this.grow){
7243             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7244             this.el.on("click", this.autoSize,  this);
7245         }
7246         */
7247         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7248             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7249         }
7250         
7251         if (typeof(this.before) == 'object') {
7252             this.before.render(this.el.select('.roo-input-before',true).first());
7253         }
7254         if (typeof(this.after) == 'object') {
7255             this.after.render(this.el.select('.roo-input-after',true).first());
7256         }
7257         
7258         
7259     },
7260     filterValidation : function(e){
7261         if(!e.isNavKeyPress()){
7262             this.validationTask.delay(this.validationDelay);
7263         }
7264     },
7265      /**
7266      * Validates the field value
7267      * @return {Boolean} True if the value is valid, else false
7268      */
7269     validate : function(){
7270         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7271         if(this.disabled || this.validateValue(this.getRawValue())){
7272             this.clearInvalid();
7273             return true;
7274         }
7275         return false;
7276     },
7277     
7278     
7279     /**
7280      * Validates a value according to the field's validation rules and marks the field as invalid
7281      * if the validation fails
7282      * @param {Mixed} value The value to validate
7283      * @return {Boolean} True if the value is valid, else false
7284      */
7285     validateValue : function(value){
7286         if(value.length < 1)  { // if it's blank
7287              if(this.allowBlank){
7288                 this.clearInvalid();
7289                 return true;
7290              }else{
7291                 this.markInvalid(this.blankText);
7292                 return false;
7293              }
7294         }
7295         if(value.length < this.minLength){
7296             this.markInvalid(String.format(this.minLengthText, this.minLength));
7297             return false;
7298         }
7299         if(value.length > this.maxLength){
7300             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7301             return false;
7302         }
7303         if(this.vtype){
7304             var vt = Roo.form.VTypes;
7305             if(!vt[this.vtype](value, this)){
7306                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7307                 return false;
7308             }
7309         }
7310         if(typeof this.validator == "function"){
7311             var msg = this.validator(value);
7312             if(msg !== true){
7313                 this.markInvalid(msg);
7314                 return false;
7315             }
7316         }
7317         if(this.regex && !this.regex.test(value)){
7318             this.markInvalid(this.regexText);
7319             return false;
7320         }
7321         return true;
7322     },
7323
7324     
7325     
7326      // private
7327     fireKey : function(e){
7328         //Roo.log('field ' + e.getKey());
7329         if(e.isNavKeyPress()){
7330             this.fireEvent("specialkey", this, e);
7331         }
7332     },
7333     focus : function (selectText){
7334         if(this.rendered){
7335             this.inputEl().focus();
7336             if(selectText === true){
7337                 this.inputEl().dom.select();
7338             }
7339         }
7340         return this;
7341     } ,
7342     
7343     onFocus : function(){
7344         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7345            // this.el.addClass(this.focusClass);
7346         }
7347         if(!this.hasFocus){
7348             this.hasFocus = true;
7349             this.startValue = this.getValue();
7350             this.fireEvent("focus", this);
7351         }
7352     },
7353     
7354     beforeBlur : Roo.emptyFn,
7355
7356     
7357     // private
7358     onBlur : function(){
7359         this.beforeBlur();
7360         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7361             //this.el.removeClass(this.focusClass);
7362         }
7363         this.hasFocus = false;
7364         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7365             this.validate();
7366         }
7367         var v = this.getValue();
7368         if(String(v) !== String(this.startValue)){
7369             this.fireEvent('change', this, v, this.startValue);
7370         }
7371         this.fireEvent("blur", this);
7372     },
7373     
7374     /**
7375      * Resets the current field value to the originally loaded value and clears any validation messages
7376      */
7377     reset : function(){
7378         this.setValue(this.originalValue);
7379         this.clearInvalid();
7380     },
7381      /**
7382      * Returns the name of the field
7383      * @return {Mixed} name The name field
7384      */
7385     getName: function(){
7386         return this.name;
7387     },
7388      /**
7389      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7390      * @return {Mixed} value The field value
7391      */
7392     getValue : function(){
7393         
7394         var v = this.inputEl().getValue();
7395         
7396         return v;
7397     },
7398     /**
7399      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7400      * @return {Mixed} value The field value
7401      */
7402     getRawValue : function(){
7403         var v = this.inputEl().getValue();
7404         
7405         return v;
7406     },
7407     
7408     /**
7409      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7410      * @param {Mixed} value The value to set
7411      */
7412     setRawValue : function(v){
7413         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7414     },
7415     
7416     selectText : function(start, end){
7417         var v = this.getRawValue();
7418         if(v.length > 0){
7419             start = start === undefined ? 0 : start;
7420             end = end === undefined ? v.length : end;
7421             var d = this.inputEl().dom;
7422             if(d.setSelectionRange){
7423                 d.setSelectionRange(start, end);
7424             }else if(d.createTextRange){
7425                 var range = d.createTextRange();
7426                 range.moveStart("character", start);
7427                 range.moveEnd("character", v.length-end);
7428                 range.select();
7429             }
7430         }
7431     },
7432     
7433     /**
7434      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7435      * @param {Mixed} value The value to set
7436      */
7437     setValue : function(v){
7438         this.value = v;
7439         if(this.rendered){
7440             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7441             this.validate();
7442         }
7443     },
7444     
7445     /*
7446     processValue : function(value){
7447         if(this.stripCharsRe){
7448             var newValue = value.replace(this.stripCharsRe, '');
7449             if(newValue !== value){
7450                 this.setRawValue(newValue);
7451                 return newValue;
7452             }
7453         }
7454         return value;
7455     },
7456   */
7457     preFocus : function(){
7458         
7459         if(this.selectOnFocus){
7460             this.inputEl().dom.select();
7461         }
7462     },
7463     filterKeys : function(e){
7464         var k = e.getKey();
7465         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7466             return;
7467         }
7468         var c = e.getCharCode(), cc = String.fromCharCode(c);
7469         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7470             return;
7471         }
7472         if(!this.maskRe.test(cc)){
7473             e.stopEvent();
7474         }
7475     },
7476      /**
7477      * Clear any invalid styles/messages for this field
7478      */
7479     clearInvalid : function(){
7480         
7481         if(!this.el || this.preventMark){ // not rendered
7482             return;
7483         }
7484         this.el.removeClass(this.invalidClass);
7485         /*
7486         switch(this.msgTarget){
7487             case 'qtip':
7488                 this.el.dom.qtip = '';
7489                 break;
7490             case 'title':
7491                 this.el.dom.title = '';
7492                 break;
7493             case 'under':
7494                 if(this.errorEl){
7495                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7496                 }
7497                 break;
7498             case 'side':
7499                 if(this.errorIcon){
7500                     this.errorIcon.dom.qtip = '';
7501                     this.errorIcon.hide();
7502                     this.un('resize', this.alignErrorIcon, this);
7503                 }
7504                 break;
7505             default:
7506                 var t = Roo.getDom(this.msgTarget);
7507                 t.innerHTML = '';
7508                 t.style.display = 'none';
7509                 break;
7510         }
7511         */
7512         this.fireEvent('valid', this);
7513     },
7514      /**
7515      * Mark this field as invalid
7516      * @param {String} msg The validation message
7517      */
7518     markInvalid : function(msg){
7519         if(!this.el  || this.preventMark){ // not rendered
7520             return;
7521         }
7522         this.el.addClass(this.invalidClass);
7523         /*
7524         msg = msg || this.invalidText;
7525         switch(this.msgTarget){
7526             case 'qtip':
7527                 this.el.dom.qtip = msg;
7528                 this.el.dom.qclass = 'x-form-invalid-tip';
7529                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7530                     Roo.QuickTips.enable();
7531                 }
7532                 break;
7533             case 'title':
7534                 this.el.dom.title = msg;
7535                 break;
7536             case 'under':
7537                 if(!this.errorEl){
7538                     var elp = this.el.findParent('.x-form-element', 5, true);
7539                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7540                     this.errorEl.setWidth(elp.getWidth(true)-20);
7541                 }
7542                 this.errorEl.update(msg);
7543                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7544                 break;
7545             case 'side':
7546                 if(!this.errorIcon){
7547                     var elp = this.el.findParent('.x-form-element', 5, true);
7548                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7549                 }
7550                 this.alignErrorIcon();
7551                 this.errorIcon.dom.qtip = msg;
7552                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7553                 this.errorIcon.show();
7554                 this.on('resize', this.alignErrorIcon, this);
7555                 break;
7556             default:
7557                 var t = Roo.getDom(this.msgTarget);
7558                 t.innerHTML = msg;
7559                 t.style.display = this.msgDisplay;
7560                 break;
7561         }
7562         */
7563         this.fireEvent('invalid', this, msg);
7564     },
7565     // private
7566     SafariOnKeyDown : function(event)
7567     {
7568         // this is a workaround for a password hang bug on chrome/ webkit.
7569         
7570         var isSelectAll = false;
7571         
7572         if(this.inputEl().dom.selectionEnd > 0){
7573             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7574         }
7575         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7576             event.preventDefault();
7577             this.setValue('');
7578             return;
7579         }
7580         
7581         if(isSelectAll){ // backspace and delete key
7582             
7583             event.preventDefault();
7584             // this is very hacky as keydown always get's upper case.
7585             //
7586             var cc = String.fromCharCode(event.getCharCode());
7587             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7588             
7589         }
7590     },
7591     adjustWidth : function(tag, w){
7592         tag = tag.toLowerCase();
7593         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7594             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7595                 if(tag == 'input'){
7596                     return w + 2;
7597                 }
7598                 if(tag == 'textarea'){
7599                     return w-2;
7600                 }
7601             }else if(Roo.isOpera){
7602                 if(tag == 'input'){
7603                     return w + 2;
7604                 }
7605                 if(tag == 'textarea'){
7606                     return w-2;
7607                 }
7608             }
7609         }
7610         return w;
7611     }
7612     
7613 });
7614
7615  
7616 /*
7617  * - LGPL
7618  *
7619  * Input
7620  * 
7621  */
7622
7623 /**
7624  * @class Roo.bootstrap.TextArea
7625  * @extends Roo.bootstrap.Input
7626  * Bootstrap TextArea class
7627  * @cfg {Number} cols Specifies the visible width of a text area
7628  * @cfg {Number} rows Specifies the visible number of lines in a text area
7629  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7630  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7631  * @cfg {string} html text
7632  * 
7633  * @constructor
7634  * Create a new TextArea
7635  * @param {Object} config The config object
7636  */
7637
7638 Roo.bootstrap.TextArea = function(config){
7639     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7640    
7641 };
7642
7643 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7644      
7645     cols : false,
7646     rows : 5,
7647     readOnly : false,
7648     warp : 'soft',
7649     resize : false,
7650     value: false,
7651     html: false,
7652     
7653     getAutoCreate : function(){
7654         
7655         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7656         
7657         var id = Roo.id();
7658         
7659         var cfg = {};
7660         
7661         var input =  {
7662             tag: 'textarea',
7663             id : id,
7664             warp : this.warp,
7665             rows : this.rows,
7666             value : this.value || '',
7667             html: this.html || '',
7668             cls : 'form-control',
7669             placeholder : this.placeholder || '' 
7670             
7671         };
7672         
7673         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7674             input.maxLength = this.maxLength;
7675         }
7676         
7677         if(this.resize){
7678             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7679         }
7680         
7681         if(this.cols){
7682             input.cols = this.cols;
7683         }
7684         
7685         if (this.readOnly) {
7686             input.readonly = true;
7687         }
7688         
7689         if (this.name) {
7690             input.name = this.name;
7691         }
7692         
7693         if (this.size) {
7694             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7695         }
7696         
7697         var settings=this;
7698         ['xs','sm','md','lg'].map(function(size){
7699             if (settings[size]) {
7700                 cfg.cls += ' col-' + size + '-' + settings[size];
7701             }
7702         });
7703         
7704         var inputblock = input;
7705         
7706         if (this.before || this.after) {
7707             
7708             inputblock = {
7709                 cls : 'input-group',
7710                 cn :  [] 
7711             };
7712             if (this.before) {
7713                 inputblock.cn.push({
7714                     tag :'span',
7715                     cls : 'input-group-addon',
7716                     html : this.before
7717                 });
7718             }
7719             inputblock.cn.push(input);
7720             if (this.after) {
7721                 inputblock.cn.push({
7722                     tag :'span',
7723                     cls : 'input-group-addon',
7724                     html : this.after
7725                 });
7726             }
7727             
7728         }
7729         
7730         if (align ==='left' && this.fieldLabel.length) {
7731                 Roo.log("left and has label");
7732                 cfg.cn = [
7733                     
7734                     {
7735                         tag: 'label',
7736                         'for' :  id,
7737                         cls : 'control-label col-sm-' + this.labelWidth,
7738                         html : this.fieldLabel
7739                         
7740                     },
7741                     {
7742                         cls : "col-sm-" + (12 - this.labelWidth), 
7743                         cn: [
7744                             inputblock
7745                         ]
7746                     }
7747                     
7748                 ];
7749         } else if ( this.fieldLabel.length) {
7750                 Roo.log(" label");
7751                  cfg.cn = [
7752                    
7753                     {
7754                         tag: 'label',
7755                         //cls : 'input-group-addon',
7756                         html : this.fieldLabel
7757                         
7758                     },
7759                     
7760                     inputblock
7761                     
7762                 ];
7763
7764         } else {
7765             
7766                    Roo.log(" no label && no align");
7767                 cfg.cn = [
7768                     
7769                         inputblock
7770                     
7771                 ];
7772                 
7773                 
7774         }
7775         
7776         if (this.disabled) {
7777             input.disabled=true;
7778         }
7779         
7780         return cfg;
7781         
7782     },
7783     /**
7784      * return the real textarea element.
7785      */
7786     inputEl: function ()
7787     {
7788         return this.el.select('textarea.form-control',true).first();
7789     }
7790 });
7791
7792  
7793 /*
7794  * - LGPL
7795  *
7796  * trigger field - base class for combo..
7797  * 
7798  */
7799  
7800 /**
7801  * @class Roo.bootstrap.TriggerField
7802  * @extends Roo.bootstrap.Input
7803  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7804  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7805  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7806  * for which you can provide a custom implementation.  For example:
7807  * <pre><code>
7808 var trigger = new Roo.bootstrap.TriggerField();
7809 trigger.onTriggerClick = myTriggerFn;
7810 trigger.applyTo('my-field');
7811 </code></pre>
7812  *
7813  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7814  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7815  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7816  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7817  * @constructor
7818  * Create a new TriggerField.
7819  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7820  * to the base TextField)
7821  */
7822 Roo.bootstrap.TriggerField = function(config){
7823     this.mimicing = false;
7824     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7825 };
7826
7827 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7828     /**
7829      * @cfg {String} triggerClass A CSS class to apply to the trigger
7830      */
7831      /**
7832      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7833      */
7834     hideTrigger:false,
7835
7836     /** @cfg {Boolean} grow @hide */
7837     /** @cfg {Number} growMin @hide */
7838     /** @cfg {Number} growMax @hide */
7839
7840     /**
7841      * @hide 
7842      * @method
7843      */
7844     autoSize: Roo.emptyFn,
7845     // private
7846     monitorTab : true,
7847     // private
7848     deferHeight : true,
7849
7850     
7851     actionMode : 'wrap',
7852     
7853     
7854     
7855     getAutoCreate : function(){
7856        
7857         var align = this.labelAlign || this.parentLabelAlign();
7858         
7859         var id = Roo.id();
7860         
7861         var cfg = {
7862             cls: 'form-group' //input-group
7863         };
7864         
7865         
7866         var input =  {
7867             tag: 'input',
7868             id : id,
7869             type : this.inputType,
7870             cls : 'form-control',
7871             autocomplete: 'off',
7872             placeholder : this.placeholder || '' 
7873             
7874         };
7875         if (this.name) {
7876             input.name = this.name;
7877         }
7878         if (this.size) {
7879             input.cls += ' input-' + this.size;
7880         }
7881         
7882         if (this.disabled) {
7883             input.disabled=true;
7884         }
7885         
7886         var inputblock = input;
7887         
7888         if (this.before || this.after) {
7889             
7890             inputblock = {
7891                 cls : 'input-group',
7892                 cn :  [] 
7893             };
7894             if (this.before) {
7895                 inputblock.cn.push({
7896                     tag :'span',
7897                     cls : 'input-group-addon',
7898                     html : this.before
7899                 });
7900             }
7901             inputblock.cn.push(input);
7902             if (this.after) {
7903                 inputblock.cn.push({
7904                     tag :'span',
7905                     cls : 'input-group-addon',
7906                     html : this.after
7907                 });
7908             }
7909             
7910         };
7911         
7912         var box = {
7913             tag: 'div',
7914             cn: [
7915                 {
7916                     tag: 'input',
7917                     type : 'hidden',
7918                     cls: 'form-hidden-field'
7919                 },
7920                 inputblock
7921             ]
7922             
7923         };
7924         
7925         if(this.multiple){
7926             Roo.log('multiple');
7927             
7928             box = {
7929                 tag: 'div',
7930                 cn: [
7931                     {
7932                         tag: 'input',
7933                         type : 'hidden',
7934                         cls: 'form-hidden-field'
7935                     },
7936                     {
7937                         tag: 'ul',
7938                         cls: 'select2-choices',
7939                         cn:[
7940                             {
7941                                 tag: 'li',
7942                                 cls: 'select2-search-field',
7943                                 cn: [
7944
7945                                     inputblock
7946                                 ]
7947                             }
7948                         ]
7949                     }
7950                 ]
7951             }
7952         };
7953         
7954         var combobox = {
7955             cls: 'select2-container input-group',
7956             cn: [
7957                 box
7958 //                {
7959 //                    tag: 'ul',
7960 //                    cls: 'typeahead typeahead-long dropdown-menu',
7961 //                    style: 'display:none'
7962 //                }
7963             ]
7964         };
7965         
7966         if(!this.multiple && this.showToggleBtn){
7967             combobox.cn.push({
7968                 tag :'span',
7969                 cls : 'input-group-addon btn dropdown-toggle',
7970                 cn : [
7971                     {
7972                         tag: 'span',
7973                         cls: 'caret'
7974                     },
7975                     {
7976                         tag: 'span',
7977                         cls: 'combobox-clear',
7978                         cn  : [
7979                             {
7980                                 tag : 'i',
7981                                 cls: 'icon-remove'
7982                             }
7983                         ]
7984                     }
7985                 ]
7986
7987             })
7988         }
7989         
7990         if(this.multiple){
7991             combobox.cls += ' select2-container-multi';
7992         }
7993         
7994         if (align ==='left' && this.fieldLabel.length) {
7995             
7996                 Roo.log("left and has label");
7997                 cfg.cn = [
7998                     
7999                     {
8000                         tag: 'label',
8001                         'for' :  id,
8002                         cls : 'control-label col-sm-' + this.labelWidth,
8003                         html : this.fieldLabel
8004                         
8005                     },
8006                     {
8007                         cls : "col-sm-" + (12 - this.labelWidth), 
8008                         cn: [
8009                             combobox
8010                         ]
8011                     }
8012                     
8013                 ];
8014         } else if ( this.fieldLabel.length) {
8015                 Roo.log(" label");
8016                  cfg.cn = [
8017                    
8018                     {
8019                         tag: 'label',
8020                         //cls : 'input-group-addon',
8021                         html : this.fieldLabel
8022                         
8023                     },
8024                     
8025                     combobox
8026                     
8027                 ];
8028
8029         } else {
8030             
8031                 Roo.log(" no label && no align");
8032                 cfg = combobox
8033                      
8034                 
8035         }
8036          
8037         var settings=this;
8038         ['xs','sm','md','lg'].map(function(size){
8039             if (settings[size]) {
8040                 cfg.cls += ' col-' + size + '-' + settings[size];
8041             }
8042         });
8043         
8044         return cfg;
8045         
8046     },
8047     
8048     
8049     
8050     // private
8051     onResize : function(w, h){
8052 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8053 //        if(typeof w == 'number'){
8054 //            var x = w - this.trigger.getWidth();
8055 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8056 //            this.trigger.setStyle('left', x+'px');
8057 //        }
8058     },
8059
8060     // private
8061     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8062
8063     // private
8064     getResizeEl : function(){
8065         return this.inputEl();
8066     },
8067
8068     // private
8069     getPositionEl : function(){
8070         return this.inputEl();
8071     },
8072
8073     // private
8074     alignErrorIcon : function(){
8075         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8076     },
8077
8078     // private
8079     initEvents : function(){
8080         
8081         this.createList();
8082         
8083         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8084         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8085         if(!this.multiple && this.showToggleBtn){
8086             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8087             if(this.hideTrigger){
8088                 this.trigger.setDisplayed(false);
8089             }
8090             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8091         }
8092         
8093         if(this.multiple){
8094             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8095         }
8096         
8097         //this.trigger.addClassOnOver('x-form-trigger-over');
8098         //this.trigger.addClassOnClick('x-form-trigger-click');
8099         
8100         //if(!this.width){
8101         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8102         //}
8103     },
8104     
8105     createList : function()
8106     {
8107         this.list = Roo.get(document.body).createChild({
8108             tag: 'ul',
8109             cls: 'typeahead typeahead-long dropdown-menu',
8110             style: 'display:none'
8111         });
8112         
8113         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8114         
8115     },
8116
8117     // private
8118     initTrigger : function(){
8119        
8120     },
8121
8122     // private
8123     onDestroy : function(){
8124         if(this.trigger){
8125             this.trigger.removeAllListeners();
8126           //  this.trigger.remove();
8127         }
8128         //if(this.wrap){
8129         //    this.wrap.remove();
8130         //}
8131         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8132     },
8133
8134     // private
8135     onFocus : function(){
8136         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8137         /*
8138         if(!this.mimicing){
8139             this.wrap.addClass('x-trigger-wrap-focus');
8140             this.mimicing = true;
8141             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8142             if(this.monitorTab){
8143                 this.el.on("keydown", this.checkTab, this);
8144             }
8145         }
8146         */
8147     },
8148
8149     // private
8150     checkTab : function(e){
8151         if(e.getKey() == e.TAB){
8152             this.triggerBlur();
8153         }
8154     },
8155
8156     // private
8157     onBlur : function(){
8158         // do nothing
8159     },
8160
8161     // private
8162     mimicBlur : function(e, t){
8163         /*
8164         if(!this.wrap.contains(t) && this.validateBlur()){
8165             this.triggerBlur();
8166         }
8167         */
8168     },
8169
8170     // private
8171     triggerBlur : function(){
8172         this.mimicing = false;
8173         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8174         if(this.monitorTab){
8175             this.el.un("keydown", this.checkTab, this);
8176         }
8177         //this.wrap.removeClass('x-trigger-wrap-focus');
8178         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8179     },
8180
8181     // private
8182     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8183     validateBlur : function(e, t){
8184         return true;
8185     },
8186
8187     // private
8188     onDisable : function(){
8189         this.inputEl().dom.disabled = true;
8190         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8191         //if(this.wrap){
8192         //    this.wrap.addClass('x-item-disabled');
8193         //}
8194     },
8195
8196     // private
8197     onEnable : function(){
8198         this.inputEl().dom.disabled = false;
8199         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8200         //if(this.wrap){
8201         //    this.el.removeClass('x-item-disabled');
8202         //}
8203     },
8204
8205     // private
8206     onShow : function(){
8207         var ae = this.getActionEl();
8208         
8209         if(ae){
8210             ae.dom.style.display = '';
8211             ae.dom.style.visibility = 'visible';
8212         }
8213     },
8214
8215     // private
8216     
8217     onHide : function(){
8218         var ae = this.getActionEl();
8219         ae.dom.style.display = 'none';
8220     },
8221
8222     /**
8223      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8224      * by an implementing function.
8225      * @method
8226      * @param {EventObject} e
8227      */
8228     onTriggerClick : Roo.emptyFn
8229 });
8230  /*
8231  * Based on:
8232  * Ext JS Library 1.1.1
8233  * Copyright(c) 2006-2007, Ext JS, LLC.
8234  *
8235  * Originally Released Under LGPL - original licence link has changed is not relivant.
8236  *
8237  * Fork - LGPL
8238  * <script type="text/javascript">
8239  */
8240
8241
8242 /**
8243  * @class Roo.data.SortTypes
8244  * @singleton
8245  * Defines the default sorting (casting?) comparison functions used when sorting data.
8246  */
8247 Roo.data.SortTypes = {
8248     /**
8249      * Default sort that does nothing
8250      * @param {Mixed} s The value being converted
8251      * @return {Mixed} The comparison value
8252      */
8253     none : function(s){
8254         return s;
8255     },
8256     
8257     /**
8258      * The regular expression used to strip tags
8259      * @type {RegExp}
8260      * @property
8261      */
8262     stripTagsRE : /<\/?[^>]+>/gi,
8263     
8264     /**
8265      * Strips all HTML tags to sort on text only
8266      * @param {Mixed} s The value being converted
8267      * @return {String} The comparison value
8268      */
8269     asText : function(s){
8270         return String(s).replace(this.stripTagsRE, "");
8271     },
8272     
8273     /**
8274      * Strips all HTML tags to sort on text only - Case insensitive
8275      * @param {Mixed} s The value being converted
8276      * @return {String} The comparison value
8277      */
8278     asUCText : function(s){
8279         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8280     },
8281     
8282     /**
8283      * Case insensitive string
8284      * @param {Mixed} s The value being converted
8285      * @return {String} The comparison value
8286      */
8287     asUCString : function(s) {
8288         return String(s).toUpperCase();
8289     },
8290     
8291     /**
8292      * Date sorting
8293      * @param {Mixed} s The value being converted
8294      * @return {Number} The comparison value
8295      */
8296     asDate : function(s) {
8297         if(!s){
8298             return 0;
8299         }
8300         if(s instanceof Date){
8301             return s.getTime();
8302         }
8303         return Date.parse(String(s));
8304     },
8305     
8306     /**
8307      * Float sorting
8308      * @param {Mixed} s The value being converted
8309      * @return {Float} The comparison value
8310      */
8311     asFloat : function(s) {
8312         var val = parseFloat(String(s).replace(/,/g, ""));
8313         if(isNaN(val)) val = 0;
8314         return val;
8315     },
8316     
8317     /**
8318      * Integer sorting
8319      * @param {Mixed} s The value being converted
8320      * @return {Number} The comparison value
8321      */
8322     asInt : function(s) {
8323         var val = parseInt(String(s).replace(/,/g, ""));
8324         if(isNaN(val)) val = 0;
8325         return val;
8326     }
8327 };/*
8328  * Based on:
8329  * Ext JS Library 1.1.1
8330  * Copyright(c) 2006-2007, Ext JS, LLC.
8331  *
8332  * Originally Released Under LGPL - original licence link has changed is not relivant.
8333  *
8334  * Fork - LGPL
8335  * <script type="text/javascript">
8336  */
8337
8338 /**
8339 * @class Roo.data.Record
8340  * Instances of this class encapsulate both record <em>definition</em> information, and record
8341  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8342  * to access Records cached in an {@link Roo.data.Store} object.<br>
8343  * <p>
8344  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8345  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8346  * objects.<br>
8347  * <p>
8348  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8349  * @constructor
8350  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8351  * {@link #create}. The parameters are the same.
8352  * @param {Array} data An associative Array of data values keyed by the field name.
8353  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8354  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8355  * not specified an integer id is generated.
8356  */
8357 Roo.data.Record = function(data, id){
8358     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8359     this.data = data;
8360 };
8361
8362 /**
8363  * Generate a constructor for a specific record layout.
8364  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8365  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8366  * Each field definition object may contain the following properties: <ul>
8367  * <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,
8368  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8369  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8370  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8371  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8372  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8373  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8374  * this may be omitted.</p></li>
8375  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8376  * <ul><li>auto (Default, implies no conversion)</li>
8377  * <li>string</li>
8378  * <li>int</li>
8379  * <li>float</li>
8380  * <li>boolean</li>
8381  * <li>date</li></ul></p></li>
8382  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8383  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8384  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8385  * by the Reader into an object that will be stored in the Record. It is passed the
8386  * following parameters:<ul>
8387  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8388  * </ul></p></li>
8389  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8390  * </ul>
8391  * <br>usage:<br><pre><code>
8392 var TopicRecord = Roo.data.Record.create(
8393     {name: 'title', mapping: 'topic_title'},
8394     {name: 'author', mapping: 'username'},
8395     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8396     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8397     {name: 'lastPoster', mapping: 'user2'},
8398     {name: 'excerpt', mapping: 'post_text'}
8399 );
8400
8401 var myNewRecord = new TopicRecord({
8402     title: 'Do my job please',
8403     author: 'noobie',
8404     totalPosts: 1,
8405     lastPost: new Date(),
8406     lastPoster: 'Animal',
8407     excerpt: 'No way dude!'
8408 });
8409 myStore.add(myNewRecord);
8410 </code></pre>
8411  * @method create
8412  * @static
8413  */
8414 Roo.data.Record.create = function(o){
8415     var f = function(){
8416         f.superclass.constructor.apply(this, arguments);
8417     };
8418     Roo.extend(f, Roo.data.Record);
8419     var p = f.prototype;
8420     p.fields = new Roo.util.MixedCollection(false, function(field){
8421         return field.name;
8422     });
8423     for(var i = 0, len = o.length; i < len; i++){
8424         p.fields.add(new Roo.data.Field(o[i]));
8425     }
8426     f.getField = function(name){
8427         return p.fields.get(name);  
8428     };
8429     return f;
8430 };
8431
8432 Roo.data.Record.AUTO_ID = 1000;
8433 Roo.data.Record.EDIT = 'edit';
8434 Roo.data.Record.REJECT = 'reject';
8435 Roo.data.Record.COMMIT = 'commit';
8436
8437 Roo.data.Record.prototype = {
8438     /**
8439      * Readonly flag - true if this record has been modified.
8440      * @type Boolean
8441      */
8442     dirty : false,
8443     editing : false,
8444     error: null,
8445     modified: null,
8446
8447     // private
8448     join : function(store){
8449         this.store = store;
8450     },
8451
8452     /**
8453      * Set the named field to the specified value.
8454      * @param {String} name The name of the field to set.
8455      * @param {Object} value The value to set the field to.
8456      */
8457     set : function(name, value){
8458         if(this.data[name] == value){
8459             return;
8460         }
8461         this.dirty = true;
8462         if(!this.modified){
8463             this.modified = {};
8464         }
8465         if(typeof this.modified[name] == 'undefined'){
8466             this.modified[name] = this.data[name];
8467         }
8468         this.data[name] = value;
8469         if(!this.editing && this.store){
8470             this.store.afterEdit(this);
8471         }       
8472     },
8473
8474     /**
8475      * Get the value of the named field.
8476      * @param {String} name The name of the field to get the value of.
8477      * @return {Object} The value of the field.
8478      */
8479     get : function(name){
8480         return this.data[name]; 
8481     },
8482
8483     // private
8484     beginEdit : function(){
8485         this.editing = true;
8486         this.modified = {}; 
8487     },
8488
8489     // private
8490     cancelEdit : function(){
8491         this.editing = false;
8492         delete this.modified;
8493     },
8494
8495     // private
8496     endEdit : function(){
8497         this.editing = false;
8498         if(this.dirty && this.store){
8499             this.store.afterEdit(this);
8500         }
8501     },
8502
8503     /**
8504      * Usually called by the {@link Roo.data.Store} which owns the Record.
8505      * Rejects all changes made to the Record since either creation, or the last commit operation.
8506      * Modified fields are reverted to their original values.
8507      * <p>
8508      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8509      * of reject operations.
8510      */
8511     reject : function(){
8512         var m = this.modified;
8513         for(var n in m){
8514             if(typeof m[n] != "function"){
8515                 this.data[n] = m[n];
8516             }
8517         }
8518         this.dirty = false;
8519         delete this.modified;
8520         this.editing = false;
8521         if(this.store){
8522             this.store.afterReject(this);
8523         }
8524     },
8525
8526     /**
8527      * Usually called by the {@link Roo.data.Store} which owns the Record.
8528      * Commits all changes made to the Record since either creation, or the last commit operation.
8529      * <p>
8530      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8531      * of commit operations.
8532      */
8533     commit : function(){
8534         this.dirty = false;
8535         delete this.modified;
8536         this.editing = false;
8537         if(this.store){
8538             this.store.afterCommit(this);
8539         }
8540     },
8541
8542     // private
8543     hasError : function(){
8544         return this.error != null;
8545     },
8546
8547     // private
8548     clearError : function(){
8549         this.error = null;
8550     },
8551
8552     /**
8553      * Creates a copy of this record.
8554      * @param {String} id (optional) A new record id if you don't want to use this record's id
8555      * @return {Record}
8556      */
8557     copy : function(newId) {
8558         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8559     }
8560 };/*
8561  * Based on:
8562  * Ext JS Library 1.1.1
8563  * Copyright(c) 2006-2007, Ext JS, LLC.
8564  *
8565  * Originally Released Under LGPL - original licence link has changed is not relivant.
8566  *
8567  * Fork - LGPL
8568  * <script type="text/javascript">
8569  */
8570
8571
8572
8573 /**
8574  * @class Roo.data.Store
8575  * @extends Roo.util.Observable
8576  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8577  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8578  * <p>
8579  * 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
8580  * has no knowledge of the format of the data returned by the Proxy.<br>
8581  * <p>
8582  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8583  * instances from the data object. These records are cached and made available through accessor functions.
8584  * @constructor
8585  * Creates a new Store.
8586  * @param {Object} config A config object containing the objects needed for the Store to access data,
8587  * and read the data into Records.
8588  */
8589 Roo.data.Store = function(config){
8590     this.data = new Roo.util.MixedCollection(false);
8591     this.data.getKey = function(o){
8592         return o.id;
8593     };
8594     this.baseParams = {};
8595     // private
8596     this.paramNames = {
8597         "start" : "start",
8598         "limit" : "limit",
8599         "sort" : "sort",
8600         "dir" : "dir",
8601         "multisort" : "_multisort"
8602     };
8603
8604     if(config && config.data){
8605         this.inlineData = config.data;
8606         delete config.data;
8607     }
8608
8609     Roo.apply(this, config);
8610     
8611     if(this.reader){ // reader passed
8612         this.reader = Roo.factory(this.reader, Roo.data);
8613         this.reader.xmodule = this.xmodule || false;
8614         if(!this.recordType){
8615             this.recordType = this.reader.recordType;
8616         }
8617         if(this.reader.onMetaChange){
8618             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8619         }
8620     }
8621
8622     if(this.recordType){
8623         this.fields = this.recordType.prototype.fields;
8624     }
8625     this.modified = [];
8626
8627     this.addEvents({
8628         /**
8629          * @event datachanged
8630          * Fires when the data cache has changed, and a widget which is using this Store
8631          * as a Record cache should refresh its view.
8632          * @param {Store} this
8633          */
8634         datachanged : true,
8635         /**
8636          * @event metachange
8637          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8638          * @param {Store} this
8639          * @param {Object} meta The JSON metadata
8640          */
8641         metachange : true,
8642         /**
8643          * @event add
8644          * Fires when Records have been added to the Store
8645          * @param {Store} this
8646          * @param {Roo.data.Record[]} records The array of Records added
8647          * @param {Number} index The index at which the record(s) were added
8648          */
8649         add : true,
8650         /**
8651          * @event remove
8652          * Fires when a Record has been removed from the Store
8653          * @param {Store} this
8654          * @param {Roo.data.Record} record The Record that was removed
8655          * @param {Number} index The index at which the record was removed
8656          */
8657         remove : true,
8658         /**
8659          * @event update
8660          * Fires when a Record has been updated
8661          * @param {Store} this
8662          * @param {Roo.data.Record} record The Record that was updated
8663          * @param {String} operation The update operation being performed.  Value may be one of:
8664          * <pre><code>
8665  Roo.data.Record.EDIT
8666  Roo.data.Record.REJECT
8667  Roo.data.Record.COMMIT
8668          * </code></pre>
8669          */
8670         update : true,
8671         /**
8672          * @event clear
8673          * Fires when the data cache has been cleared.
8674          * @param {Store} this
8675          */
8676         clear : true,
8677         /**
8678          * @event beforeload
8679          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8680          * the load action will be canceled.
8681          * @param {Store} this
8682          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8683          */
8684         beforeload : true,
8685         /**
8686          * @event beforeloadadd
8687          * Fires after a new set of Records has been loaded.
8688          * @param {Store} this
8689          * @param {Roo.data.Record[]} records The Records that were loaded
8690          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8691          */
8692         beforeloadadd : true,
8693         /**
8694          * @event load
8695          * Fires after a new set of Records has been loaded, before they are added to the store.
8696          * @param {Store} this
8697          * @param {Roo.data.Record[]} records The Records that were loaded
8698          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8699          * @params {Object} return from reader
8700          */
8701         load : true,
8702         /**
8703          * @event loadexception
8704          * Fires if an exception occurs in the Proxy during loading.
8705          * Called with the signature of the Proxy's "loadexception" event.
8706          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8707          * 
8708          * @param {Proxy} 
8709          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8710          * @param {Object} load options 
8711          * @param {Object} jsonData from your request (normally this contains the Exception)
8712          */
8713         loadexception : true
8714     });
8715     
8716     if(this.proxy){
8717         this.proxy = Roo.factory(this.proxy, Roo.data);
8718         this.proxy.xmodule = this.xmodule || false;
8719         this.relayEvents(this.proxy,  ["loadexception"]);
8720     }
8721     this.sortToggle = {};
8722     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8723
8724     Roo.data.Store.superclass.constructor.call(this);
8725
8726     if(this.inlineData){
8727         this.loadData(this.inlineData);
8728         delete this.inlineData;
8729     }
8730 };
8731
8732 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8733      /**
8734     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8735     * without a remote query - used by combo/forms at present.
8736     */
8737     
8738     /**
8739     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8740     */
8741     /**
8742     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8743     */
8744     /**
8745     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8746     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8747     */
8748     /**
8749     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8750     * on any HTTP request
8751     */
8752     /**
8753     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8754     */
8755     /**
8756     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8757     */
8758     multiSort: false,
8759     /**
8760     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8761     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8762     */
8763     remoteSort : false,
8764
8765     /**
8766     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8767      * loaded or when a record is removed. (defaults to false).
8768     */
8769     pruneModifiedRecords : false,
8770
8771     // private
8772     lastOptions : null,
8773
8774     /**
8775      * Add Records to the Store and fires the add event.
8776      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8777      */
8778     add : function(records){
8779         records = [].concat(records);
8780         for(var i = 0, len = records.length; i < len; i++){
8781             records[i].join(this);
8782         }
8783         var index = this.data.length;
8784         this.data.addAll(records);
8785         this.fireEvent("add", this, records, index);
8786     },
8787
8788     /**
8789      * Remove a Record from the Store and fires the remove event.
8790      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8791      */
8792     remove : function(record){
8793         var index = this.data.indexOf(record);
8794         this.data.removeAt(index);
8795         if(this.pruneModifiedRecords){
8796             this.modified.remove(record);
8797         }
8798         this.fireEvent("remove", this, record, index);
8799     },
8800
8801     /**
8802      * Remove all Records from the Store and fires the clear event.
8803      */
8804     removeAll : function(){
8805         this.data.clear();
8806         if(this.pruneModifiedRecords){
8807             this.modified = [];
8808         }
8809         this.fireEvent("clear", this);
8810     },
8811
8812     /**
8813      * Inserts Records to the Store at the given index and fires the add event.
8814      * @param {Number} index The start index at which to insert the passed Records.
8815      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8816      */
8817     insert : function(index, records){
8818         records = [].concat(records);
8819         for(var i = 0, len = records.length; i < len; i++){
8820             this.data.insert(index, records[i]);
8821             records[i].join(this);
8822         }
8823         this.fireEvent("add", this, records, index);
8824     },
8825
8826     /**
8827      * Get the index within the cache of the passed Record.
8828      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8829      * @return {Number} The index of the passed Record. Returns -1 if not found.
8830      */
8831     indexOf : function(record){
8832         return this.data.indexOf(record);
8833     },
8834
8835     /**
8836      * Get the index within the cache of the Record with the passed id.
8837      * @param {String} id The id of the Record to find.
8838      * @return {Number} The index of the Record. Returns -1 if not found.
8839      */
8840     indexOfId : function(id){
8841         return this.data.indexOfKey(id);
8842     },
8843
8844     /**
8845      * Get the Record with the specified id.
8846      * @param {String} id The id of the Record to find.
8847      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8848      */
8849     getById : function(id){
8850         return this.data.key(id);
8851     },
8852
8853     /**
8854      * Get the Record at the specified index.
8855      * @param {Number} index The index of the Record to find.
8856      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8857      */
8858     getAt : function(index){
8859         return this.data.itemAt(index);
8860     },
8861
8862     /**
8863      * Returns a range of Records between specified indices.
8864      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8865      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8866      * @return {Roo.data.Record[]} An array of Records
8867      */
8868     getRange : function(start, end){
8869         return this.data.getRange(start, end);
8870     },
8871
8872     // private
8873     storeOptions : function(o){
8874         o = Roo.apply({}, o);
8875         delete o.callback;
8876         delete o.scope;
8877         this.lastOptions = o;
8878     },
8879
8880     /**
8881      * Loads the Record cache from the configured Proxy using the configured Reader.
8882      * <p>
8883      * If using remote paging, then the first load call must specify the <em>start</em>
8884      * and <em>limit</em> properties in the options.params property to establish the initial
8885      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8886      * <p>
8887      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8888      * and this call will return before the new data has been loaded. Perform any post-processing
8889      * in a callback function, or in a "load" event handler.</strong>
8890      * <p>
8891      * @param {Object} options An object containing properties which control loading options:<ul>
8892      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8893      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8894      * passed the following arguments:<ul>
8895      * <li>r : Roo.data.Record[]</li>
8896      * <li>options: Options object from the load call</li>
8897      * <li>success: Boolean success indicator</li></ul></li>
8898      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8899      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8900      * </ul>
8901      */
8902     load : function(options){
8903         options = options || {};
8904         if(this.fireEvent("beforeload", this, options) !== false){
8905             this.storeOptions(options);
8906             var p = Roo.apply(options.params || {}, this.baseParams);
8907             // if meta was not loaded from remote source.. try requesting it.
8908             if (!this.reader.metaFromRemote) {
8909                 p._requestMeta = 1;
8910             }
8911             if(this.sortInfo && this.remoteSort){
8912                 var pn = this.paramNames;
8913                 p[pn["sort"]] = this.sortInfo.field;
8914                 p[pn["dir"]] = this.sortInfo.direction;
8915             }
8916             if (this.multiSort) {
8917                 var pn = this.paramNames;
8918                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8919             }
8920             
8921             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8922         }
8923     },
8924
8925     /**
8926      * Reloads the Record cache from the configured Proxy using the configured Reader and
8927      * the options from the last load operation performed.
8928      * @param {Object} options (optional) An object containing properties which may override the options
8929      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8930      * the most recently used options are reused).
8931      */
8932     reload : function(options){
8933         this.load(Roo.applyIf(options||{}, this.lastOptions));
8934     },
8935
8936     // private
8937     // Called as a callback by the Reader during a load operation.
8938     loadRecords : function(o, options, success){
8939         if(!o || success === false){
8940             if(success !== false){
8941                 this.fireEvent("load", this, [], options, o);
8942             }
8943             if(options.callback){
8944                 options.callback.call(options.scope || this, [], options, false);
8945             }
8946             return;
8947         }
8948         // if data returned failure - throw an exception.
8949         if (o.success === false) {
8950             // show a message if no listener is registered.
8951             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8952                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8953             }
8954             // loadmask wil be hooked into this..
8955             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8956             return;
8957         }
8958         var r = o.records, t = o.totalRecords || r.length;
8959         
8960         this.fireEvent("beforeloadadd", this, r, options, o);
8961         
8962         if(!options || options.add !== true){
8963             if(this.pruneModifiedRecords){
8964                 this.modified = [];
8965             }
8966             for(var i = 0, len = r.length; i < len; i++){
8967                 r[i].join(this);
8968             }
8969             if(this.snapshot){
8970                 this.data = this.snapshot;
8971                 delete this.snapshot;
8972             }
8973             this.data.clear();
8974             this.data.addAll(r);
8975             this.totalLength = t;
8976             this.applySort();
8977             this.fireEvent("datachanged", this);
8978         }else{
8979             this.totalLength = Math.max(t, this.data.length+r.length);
8980             this.add(r);
8981         }
8982         this.fireEvent("load", this, r, options, o);
8983         if(options.callback){
8984             options.callback.call(options.scope || this, r, options, true);
8985         }
8986     },
8987
8988
8989     /**
8990      * Loads data from a passed data block. A Reader which understands the format of the data
8991      * must have been configured in the constructor.
8992      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8993      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8994      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8995      */
8996     loadData : function(o, append){
8997         var r = this.reader.readRecords(o);
8998         this.loadRecords(r, {add: append}, true);
8999     },
9000
9001     /**
9002      * Gets the number of cached records.
9003      * <p>
9004      * <em>If using paging, this may not be the total size of the dataset. If the data object
9005      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9006      * the data set size</em>
9007      */
9008     getCount : function(){
9009         return this.data.length || 0;
9010     },
9011
9012     /**
9013      * Gets the total number of records in the dataset as returned by the server.
9014      * <p>
9015      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9016      * the dataset size</em>
9017      */
9018     getTotalCount : function(){
9019         return this.totalLength || 0;
9020     },
9021
9022     /**
9023      * Returns the sort state of the Store as an object with two properties:
9024      * <pre><code>
9025  field {String} The name of the field by which the Records are sorted
9026  direction {String} The sort order, "ASC" or "DESC"
9027      * </code></pre>
9028      */
9029     getSortState : function(){
9030         return this.sortInfo;
9031     },
9032
9033     // private
9034     applySort : function(){
9035         if(this.sortInfo && !this.remoteSort){
9036             var s = this.sortInfo, f = s.field;
9037             var st = this.fields.get(f).sortType;
9038             var fn = function(r1, r2){
9039                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9040                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9041             };
9042             this.data.sort(s.direction, fn);
9043             if(this.snapshot && this.snapshot != this.data){
9044                 this.snapshot.sort(s.direction, fn);
9045             }
9046         }
9047     },
9048
9049     /**
9050      * Sets the default sort column and order to be used by the next load operation.
9051      * @param {String} fieldName The name of the field to sort by.
9052      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9053      */
9054     setDefaultSort : function(field, dir){
9055         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9056     },
9057
9058     /**
9059      * Sort the Records.
9060      * If remote sorting is used, the sort is performed on the server, and the cache is
9061      * reloaded. If local sorting is used, the cache is sorted internally.
9062      * @param {String} fieldName The name of the field to sort by.
9063      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9064      */
9065     sort : function(fieldName, dir){
9066         var f = this.fields.get(fieldName);
9067         if(!dir){
9068             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9069             
9070             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9071                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9072             }else{
9073                 dir = f.sortDir;
9074             }
9075         }
9076         this.sortToggle[f.name] = dir;
9077         this.sortInfo = {field: f.name, direction: dir};
9078         if(!this.remoteSort){
9079             this.applySort();
9080             this.fireEvent("datachanged", this);
9081         }else{
9082             this.load(this.lastOptions);
9083         }
9084     },
9085
9086     /**
9087      * Calls the specified function for each of the Records in the cache.
9088      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9089      * Returning <em>false</em> aborts and exits the iteration.
9090      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9091      */
9092     each : function(fn, scope){
9093         this.data.each(fn, scope);
9094     },
9095
9096     /**
9097      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9098      * (e.g., during paging).
9099      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9100      */
9101     getModifiedRecords : function(){
9102         return this.modified;
9103     },
9104
9105     // private
9106     createFilterFn : function(property, value, anyMatch){
9107         if(!value.exec){ // not a regex
9108             value = String(value);
9109             if(value.length == 0){
9110                 return false;
9111             }
9112             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9113         }
9114         return function(r){
9115             return value.test(r.data[property]);
9116         };
9117     },
9118
9119     /**
9120      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9121      * @param {String} property A field on your records
9122      * @param {Number} start The record index to start at (defaults to 0)
9123      * @param {Number} end The last record index to include (defaults to length - 1)
9124      * @return {Number} The sum
9125      */
9126     sum : function(property, start, end){
9127         var rs = this.data.items, v = 0;
9128         start = start || 0;
9129         end = (end || end === 0) ? end : rs.length-1;
9130
9131         for(var i = start; i <= end; i++){
9132             v += (rs[i].data[property] || 0);
9133         }
9134         return v;
9135     },
9136
9137     /**
9138      * Filter the records by a specified property.
9139      * @param {String} field A field on your records
9140      * @param {String/RegExp} value Either a string that the field
9141      * should start with or a RegExp to test against the field
9142      * @param {Boolean} anyMatch True to match any part not just the beginning
9143      */
9144     filter : function(property, value, anyMatch){
9145         var fn = this.createFilterFn(property, value, anyMatch);
9146         return fn ? this.filterBy(fn) : this.clearFilter();
9147     },
9148
9149     /**
9150      * Filter by a function. The specified function will be called with each
9151      * record in this data source. If the function returns true the record is included,
9152      * otherwise it is filtered.
9153      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9154      * @param {Object} scope (optional) The scope of the function (defaults to this)
9155      */
9156     filterBy : function(fn, scope){
9157         this.snapshot = this.snapshot || this.data;
9158         this.data = this.queryBy(fn, scope||this);
9159         this.fireEvent("datachanged", this);
9160     },
9161
9162     /**
9163      * Query the records by a specified property.
9164      * @param {String} field A field on your records
9165      * @param {String/RegExp} value Either a string that the field
9166      * should start with or a RegExp to test against the field
9167      * @param {Boolean} anyMatch True to match any part not just the beginning
9168      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9169      */
9170     query : function(property, value, anyMatch){
9171         var fn = this.createFilterFn(property, value, anyMatch);
9172         return fn ? this.queryBy(fn) : this.data.clone();
9173     },
9174
9175     /**
9176      * Query by a function. The specified function will be called with each
9177      * record in this data source. If the function returns true the record is included
9178      * in the results.
9179      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9180      * @param {Object} scope (optional) The scope of the function (defaults to this)
9181       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9182      **/
9183     queryBy : function(fn, scope){
9184         var data = this.snapshot || this.data;
9185         return data.filterBy(fn, scope||this);
9186     },
9187
9188     /**
9189      * Collects unique values for a particular dataIndex from this store.
9190      * @param {String} dataIndex The property to collect
9191      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9192      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9193      * @return {Array} An array of the unique values
9194      **/
9195     collect : function(dataIndex, allowNull, bypassFilter){
9196         var d = (bypassFilter === true && this.snapshot) ?
9197                 this.snapshot.items : this.data.items;
9198         var v, sv, r = [], l = {};
9199         for(var i = 0, len = d.length; i < len; i++){
9200             v = d[i].data[dataIndex];
9201             sv = String(v);
9202             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9203                 l[sv] = true;
9204                 r[r.length] = v;
9205             }
9206         }
9207         return r;
9208     },
9209
9210     /**
9211      * Revert to a view of the Record cache with no filtering applied.
9212      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9213      */
9214     clearFilter : function(suppressEvent){
9215         if(this.snapshot && this.snapshot != this.data){
9216             this.data = this.snapshot;
9217             delete this.snapshot;
9218             if(suppressEvent !== true){
9219                 this.fireEvent("datachanged", this);
9220             }
9221         }
9222     },
9223
9224     // private
9225     afterEdit : function(record){
9226         if(this.modified.indexOf(record) == -1){
9227             this.modified.push(record);
9228         }
9229         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9230     },
9231     
9232     // private
9233     afterReject : function(record){
9234         this.modified.remove(record);
9235         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9236     },
9237
9238     // private
9239     afterCommit : function(record){
9240         this.modified.remove(record);
9241         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9242     },
9243
9244     /**
9245      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9246      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9247      */
9248     commitChanges : function(){
9249         var m = this.modified.slice(0);
9250         this.modified = [];
9251         for(var i = 0, len = m.length; i < len; i++){
9252             m[i].commit();
9253         }
9254     },
9255
9256     /**
9257      * Cancel outstanding changes on all changed records.
9258      */
9259     rejectChanges : function(){
9260         var m = this.modified.slice(0);
9261         this.modified = [];
9262         for(var i = 0, len = m.length; i < len; i++){
9263             m[i].reject();
9264         }
9265     },
9266
9267     onMetaChange : function(meta, rtype, o){
9268         this.recordType = rtype;
9269         this.fields = rtype.prototype.fields;
9270         delete this.snapshot;
9271         this.sortInfo = meta.sortInfo || this.sortInfo;
9272         this.modified = [];
9273         this.fireEvent('metachange', this, this.reader.meta);
9274     },
9275     
9276     moveIndex : function(data, type)
9277     {
9278         var index = this.indexOf(data);
9279         
9280         var newIndex = index + type;
9281         
9282         this.remove(data);
9283         
9284         this.insert(newIndex, data);
9285         
9286     }
9287 });/*
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  * @class Roo.data.SimpleStore
9300  * @extends Roo.data.Store
9301  * Small helper class to make creating Stores from Array data easier.
9302  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9303  * @cfg {Array} fields An array of field definition objects, or field name strings.
9304  * @cfg {Array} data The multi-dimensional array of data
9305  * @constructor
9306  * @param {Object} config
9307  */
9308 Roo.data.SimpleStore = function(config){
9309     Roo.data.SimpleStore.superclass.constructor.call(this, {
9310         isLocal : true,
9311         reader: new Roo.data.ArrayReader({
9312                 id: config.id
9313             },
9314             Roo.data.Record.create(config.fields)
9315         ),
9316         proxy : new Roo.data.MemoryProxy(config.data)
9317     });
9318     this.load();
9319 };
9320 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9321  * Based on:
9322  * Ext JS Library 1.1.1
9323  * Copyright(c) 2006-2007, Ext JS, LLC.
9324  *
9325  * Originally Released Under LGPL - original licence link has changed is not relivant.
9326  *
9327  * Fork - LGPL
9328  * <script type="text/javascript">
9329  */
9330
9331 /**
9332 /**
9333  * @extends Roo.data.Store
9334  * @class Roo.data.JsonStore
9335  * Small helper class to make creating Stores for JSON data easier. <br/>
9336 <pre><code>
9337 var store = new Roo.data.JsonStore({
9338     url: 'get-images.php',
9339     root: 'images',
9340     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9341 });
9342 </code></pre>
9343  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9344  * JsonReader and HttpProxy (unless inline data is provided).</b>
9345  * @cfg {Array} fields An array of field definition objects, or field name strings.
9346  * @constructor
9347  * @param {Object} config
9348  */
9349 Roo.data.JsonStore = function(c){
9350     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9351         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9352         reader: new Roo.data.JsonReader(c, c.fields)
9353     }));
9354 };
9355 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9356  * Based on:
9357  * Ext JS Library 1.1.1
9358  * Copyright(c) 2006-2007, Ext JS, LLC.
9359  *
9360  * Originally Released Under LGPL - original licence link has changed is not relivant.
9361  *
9362  * Fork - LGPL
9363  * <script type="text/javascript">
9364  */
9365
9366  
9367 Roo.data.Field = function(config){
9368     if(typeof config == "string"){
9369         config = {name: config};
9370     }
9371     Roo.apply(this, config);
9372     
9373     if(!this.type){
9374         this.type = "auto";
9375     }
9376     
9377     var st = Roo.data.SortTypes;
9378     // named sortTypes are supported, here we look them up
9379     if(typeof this.sortType == "string"){
9380         this.sortType = st[this.sortType];
9381     }
9382     
9383     // set default sortType for strings and dates
9384     if(!this.sortType){
9385         switch(this.type){
9386             case "string":
9387                 this.sortType = st.asUCString;
9388                 break;
9389             case "date":
9390                 this.sortType = st.asDate;
9391                 break;
9392             default:
9393                 this.sortType = st.none;
9394         }
9395     }
9396
9397     // define once
9398     var stripRe = /[\$,%]/g;
9399
9400     // prebuilt conversion function for this field, instead of
9401     // switching every time we're reading a value
9402     if(!this.convert){
9403         var cv, dateFormat = this.dateFormat;
9404         switch(this.type){
9405             case "":
9406             case "auto":
9407             case undefined:
9408                 cv = function(v){ return v; };
9409                 break;
9410             case "string":
9411                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9412                 break;
9413             case "int":
9414                 cv = function(v){
9415                     return v !== undefined && v !== null && v !== '' ?
9416                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9417                     };
9418                 break;
9419             case "float":
9420                 cv = function(v){
9421                     return v !== undefined && v !== null && v !== '' ?
9422                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9423                     };
9424                 break;
9425             case "bool":
9426             case "boolean":
9427                 cv = function(v){ return v === true || v === "true" || v == 1; };
9428                 break;
9429             case "date":
9430                 cv = function(v){
9431                     if(!v){
9432                         return '';
9433                     }
9434                     if(v instanceof Date){
9435                         return v;
9436                     }
9437                     if(dateFormat){
9438                         if(dateFormat == "timestamp"){
9439                             return new Date(v*1000);
9440                         }
9441                         return Date.parseDate(v, dateFormat);
9442                     }
9443                     var parsed = Date.parse(v);
9444                     return parsed ? new Date(parsed) : null;
9445                 };
9446              break;
9447             
9448         }
9449         this.convert = cv;
9450     }
9451 };
9452
9453 Roo.data.Field.prototype = {
9454     dateFormat: null,
9455     defaultValue: "",
9456     mapping: null,
9457     sortType : null,
9458     sortDir : "ASC"
9459 };/*
9460  * Based on:
9461  * Ext JS Library 1.1.1
9462  * Copyright(c) 2006-2007, Ext JS, LLC.
9463  *
9464  * Originally Released Under LGPL - original licence link has changed is not relivant.
9465  *
9466  * Fork - LGPL
9467  * <script type="text/javascript">
9468  */
9469  
9470 // Base class for reading structured data from a data source.  This class is intended to be
9471 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9472
9473 /**
9474  * @class Roo.data.DataReader
9475  * Base class for reading structured data from a data source.  This class is intended to be
9476  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9477  */
9478
9479 Roo.data.DataReader = function(meta, recordType){
9480     
9481     this.meta = meta;
9482     
9483     this.recordType = recordType instanceof Array ? 
9484         Roo.data.Record.create(recordType) : recordType;
9485 };
9486
9487 Roo.data.DataReader.prototype = {
9488      /**
9489      * Create an empty record
9490      * @param {Object} data (optional) - overlay some values
9491      * @return {Roo.data.Record} record created.
9492      */
9493     newRow :  function(d) {
9494         var da =  {};
9495         this.recordType.prototype.fields.each(function(c) {
9496             switch( c.type) {
9497                 case 'int' : da[c.name] = 0; break;
9498                 case 'date' : da[c.name] = new Date(); break;
9499                 case 'float' : da[c.name] = 0.0; break;
9500                 case 'boolean' : da[c.name] = false; break;
9501                 default : da[c.name] = ""; break;
9502             }
9503             
9504         });
9505         return new this.recordType(Roo.apply(da, d));
9506     }
9507     
9508 };/*
9509  * Based on:
9510  * Ext JS Library 1.1.1
9511  * Copyright(c) 2006-2007, Ext JS, LLC.
9512  *
9513  * Originally Released Under LGPL - original licence link has changed is not relivant.
9514  *
9515  * Fork - LGPL
9516  * <script type="text/javascript">
9517  */
9518
9519 /**
9520  * @class Roo.data.DataProxy
9521  * @extends Roo.data.Observable
9522  * This class is an abstract base class for implementations which provide retrieval of
9523  * unformatted data objects.<br>
9524  * <p>
9525  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9526  * (of the appropriate type which knows how to parse the data object) to provide a block of
9527  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9528  * <p>
9529  * Custom implementations must implement the load method as described in
9530  * {@link Roo.data.HttpProxy#load}.
9531  */
9532 Roo.data.DataProxy = function(){
9533     this.addEvents({
9534         /**
9535          * @event beforeload
9536          * Fires before a network request is made to retrieve a data object.
9537          * @param {Object} This DataProxy object.
9538          * @param {Object} params The params parameter to the load function.
9539          */
9540         beforeload : true,
9541         /**
9542          * @event load
9543          * Fires before the load method's callback is called.
9544          * @param {Object} This DataProxy object.
9545          * @param {Object} o The data object.
9546          * @param {Object} arg The callback argument object passed to the load function.
9547          */
9548         load : true,
9549         /**
9550          * @event loadexception
9551          * Fires if an Exception occurs during data retrieval.
9552          * @param {Object} This DataProxy object.
9553          * @param {Object} o The data object.
9554          * @param {Object} arg The callback argument object passed to the load function.
9555          * @param {Object} e The Exception.
9556          */
9557         loadexception : true
9558     });
9559     Roo.data.DataProxy.superclass.constructor.call(this);
9560 };
9561
9562 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9563
9564     /**
9565      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9566      */
9567 /*
9568  * Based on:
9569  * Ext JS Library 1.1.1
9570  * Copyright(c) 2006-2007, Ext JS, LLC.
9571  *
9572  * Originally Released Under LGPL - original licence link has changed is not relivant.
9573  *
9574  * Fork - LGPL
9575  * <script type="text/javascript">
9576  */
9577 /**
9578  * @class Roo.data.MemoryProxy
9579  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9580  * to the Reader when its load method is called.
9581  * @constructor
9582  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9583  */
9584 Roo.data.MemoryProxy = function(data){
9585     if (data.data) {
9586         data = data.data;
9587     }
9588     Roo.data.MemoryProxy.superclass.constructor.call(this);
9589     this.data = data;
9590 };
9591
9592 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9593     /**
9594      * Load data from the requested source (in this case an in-memory
9595      * data object passed to the constructor), read the data object into
9596      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9597      * process that block using the passed callback.
9598      * @param {Object} params This parameter is not used by the MemoryProxy class.
9599      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9600      * object into a block of Roo.data.Records.
9601      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9602      * The function must be passed <ul>
9603      * <li>The Record block object</li>
9604      * <li>The "arg" argument from the load function</li>
9605      * <li>A boolean success indicator</li>
9606      * </ul>
9607      * @param {Object} scope The scope in which to call the callback
9608      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9609      */
9610     load : function(params, reader, callback, scope, arg){
9611         params = params || {};
9612         var result;
9613         try {
9614             result = reader.readRecords(this.data);
9615         }catch(e){
9616             this.fireEvent("loadexception", this, arg, null, e);
9617             callback.call(scope, null, arg, false);
9618             return;
9619         }
9620         callback.call(scope, result, arg, true);
9621     },
9622     
9623     // private
9624     update : function(params, records){
9625         
9626     }
9627 });/*
9628  * Based on:
9629  * Ext JS Library 1.1.1
9630  * Copyright(c) 2006-2007, Ext JS, LLC.
9631  *
9632  * Originally Released Under LGPL - original licence link has changed is not relivant.
9633  *
9634  * Fork - LGPL
9635  * <script type="text/javascript">
9636  */
9637 /**
9638  * @class Roo.data.HttpProxy
9639  * @extends Roo.data.DataProxy
9640  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9641  * configured to reference a certain URL.<br><br>
9642  * <p>
9643  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9644  * from which the running page was served.<br><br>
9645  * <p>
9646  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9647  * <p>
9648  * Be aware that to enable the browser to parse an XML document, the server must set
9649  * the Content-Type header in the HTTP response to "text/xml".
9650  * @constructor
9651  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9652  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9653  * will be used to make the request.
9654  */
9655 Roo.data.HttpProxy = function(conn){
9656     Roo.data.HttpProxy.superclass.constructor.call(this);
9657     // is conn a conn config or a real conn?
9658     this.conn = conn;
9659     this.useAjax = !conn || !conn.events;
9660   
9661 };
9662
9663 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9664     // thse are take from connection...
9665     
9666     /**
9667      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9668      */
9669     /**
9670      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9671      * extra parameters to each request made by this object. (defaults to undefined)
9672      */
9673     /**
9674      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9675      *  to each request made by this object. (defaults to undefined)
9676      */
9677     /**
9678      * @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)
9679      */
9680     /**
9681      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9682      */
9683      /**
9684      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9685      * @type Boolean
9686      */
9687   
9688
9689     /**
9690      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9691      * @type Boolean
9692      */
9693     /**
9694      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9695      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9696      * a finer-grained basis than the DataProxy events.
9697      */
9698     getConnection : function(){
9699         return this.useAjax ? Roo.Ajax : this.conn;
9700     },
9701
9702     /**
9703      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9704      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9705      * process that block using the passed callback.
9706      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9707      * for the request to the remote server.
9708      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9709      * object into a block of Roo.data.Records.
9710      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9711      * The function must be passed <ul>
9712      * <li>The Record block object</li>
9713      * <li>The "arg" argument from the load function</li>
9714      * <li>A boolean success indicator</li>
9715      * </ul>
9716      * @param {Object} scope The scope in which to call the callback
9717      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9718      */
9719     load : function(params, reader, callback, scope, arg){
9720         if(this.fireEvent("beforeload", this, params) !== false){
9721             var  o = {
9722                 params : params || {},
9723                 request: {
9724                     callback : callback,
9725                     scope : scope,
9726                     arg : arg
9727                 },
9728                 reader: reader,
9729                 callback : this.loadResponse,
9730                 scope: this
9731             };
9732             if(this.useAjax){
9733                 Roo.applyIf(o, this.conn);
9734                 if(this.activeRequest){
9735                     Roo.Ajax.abort(this.activeRequest);
9736                 }
9737                 this.activeRequest = Roo.Ajax.request(o);
9738             }else{
9739                 this.conn.request(o);
9740             }
9741         }else{
9742             callback.call(scope||this, null, arg, false);
9743         }
9744     },
9745
9746     // private
9747     loadResponse : function(o, success, response){
9748         delete this.activeRequest;
9749         if(!success){
9750             this.fireEvent("loadexception", this, o, response);
9751             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9752             return;
9753         }
9754         var result;
9755         try {
9756             result = o.reader.read(response);
9757         }catch(e){
9758             this.fireEvent("loadexception", this, o, response, e);
9759             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9760             return;
9761         }
9762         
9763         this.fireEvent("load", this, o, o.request.arg);
9764         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9765     },
9766
9767     // private
9768     update : function(dataSet){
9769
9770     },
9771
9772     // private
9773     updateResponse : function(dataSet){
9774
9775     }
9776 });/*
9777  * Based on:
9778  * Ext JS Library 1.1.1
9779  * Copyright(c) 2006-2007, Ext JS, LLC.
9780  *
9781  * Originally Released Under LGPL - original licence link has changed is not relivant.
9782  *
9783  * Fork - LGPL
9784  * <script type="text/javascript">
9785  */
9786
9787 /**
9788  * @class Roo.data.ScriptTagProxy
9789  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9790  * other than the originating domain of the running page.<br><br>
9791  * <p>
9792  * <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
9793  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9794  * <p>
9795  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9796  * source code that is used as the source inside a &lt;script> tag.<br><br>
9797  * <p>
9798  * In order for the browser to process the returned data, the server must wrap the data object
9799  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9800  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9801  * depending on whether the callback name was passed:
9802  * <p>
9803  * <pre><code>
9804 boolean scriptTag = false;
9805 String cb = request.getParameter("callback");
9806 if (cb != null) {
9807     scriptTag = true;
9808     response.setContentType("text/javascript");
9809 } else {
9810     response.setContentType("application/x-json");
9811 }
9812 Writer out = response.getWriter();
9813 if (scriptTag) {
9814     out.write(cb + "(");
9815 }
9816 out.print(dataBlock.toJsonString());
9817 if (scriptTag) {
9818     out.write(");");
9819 }
9820 </pre></code>
9821  *
9822  * @constructor
9823  * @param {Object} config A configuration object.
9824  */
9825 Roo.data.ScriptTagProxy = function(config){
9826     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9827     Roo.apply(this, config);
9828     this.head = document.getElementsByTagName("head")[0];
9829 };
9830
9831 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9832
9833 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9834     /**
9835      * @cfg {String} url The URL from which to request the data object.
9836      */
9837     /**
9838      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9839      */
9840     timeout : 30000,
9841     /**
9842      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9843      * the server the name of the callback function set up by the load call to process the returned data object.
9844      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9845      * javascript output which calls this named function passing the data object as its only parameter.
9846      */
9847     callbackParam : "callback",
9848     /**
9849      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9850      * name to the request.
9851      */
9852     nocache : true,
9853
9854     /**
9855      * Load data from the configured URL, read the data object into
9856      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9857      * process that block using the passed callback.
9858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9859      * for the request to the remote server.
9860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9861      * object into a block of Roo.data.Records.
9862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9863      * The function must be passed <ul>
9864      * <li>The Record block object</li>
9865      * <li>The "arg" argument from the load function</li>
9866      * <li>A boolean success indicator</li>
9867      * </ul>
9868      * @param {Object} scope The scope in which to call the callback
9869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9870      */
9871     load : function(params, reader, callback, scope, arg){
9872         if(this.fireEvent("beforeload", this, params) !== false){
9873
9874             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9875
9876             var url = this.url;
9877             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9878             if(this.nocache){
9879                 url += "&_dc=" + (new Date().getTime());
9880             }
9881             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9882             var trans = {
9883                 id : transId,
9884                 cb : "stcCallback"+transId,
9885                 scriptId : "stcScript"+transId,
9886                 params : params,
9887                 arg : arg,
9888                 url : url,
9889                 callback : callback,
9890                 scope : scope,
9891                 reader : reader
9892             };
9893             var conn = this;
9894
9895             window[trans.cb] = function(o){
9896                 conn.handleResponse(o, trans);
9897             };
9898
9899             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9900
9901             if(this.autoAbort !== false){
9902                 this.abort();
9903             }
9904
9905             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9906
9907             var script = document.createElement("script");
9908             script.setAttribute("src", url);
9909             script.setAttribute("type", "text/javascript");
9910             script.setAttribute("id", trans.scriptId);
9911             this.head.appendChild(script);
9912
9913             this.trans = trans;
9914         }else{
9915             callback.call(scope||this, null, arg, false);
9916         }
9917     },
9918
9919     // private
9920     isLoading : function(){
9921         return this.trans ? true : false;
9922     },
9923
9924     /**
9925      * Abort the current server request.
9926      */
9927     abort : function(){
9928         if(this.isLoading()){
9929             this.destroyTrans(this.trans);
9930         }
9931     },
9932
9933     // private
9934     destroyTrans : function(trans, isLoaded){
9935         this.head.removeChild(document.getElementById(trans.scriptId));
9936         clearTimeout(trans.timeoutId);
9937         if(isLoaded){
9938             window[trans.cb] = undefined;
9939             try{
9940                 delete window[trans.cb];
9941             }catch(e){}
9942         }else{
9943             // if hasn't been loaded, wait for load to remove it to prevent script error
9944             window[trans.cb] = function(){
9945                 window[trans.cb] = undefined;
9946                 try{
9947                     delete window[trans.cb];
9948                 }catch(e){}
9949             };
9950         }
9951     },
9952
9953     // private
9954     handleResponse : function(o, trans){
9955         this.trans = false;
9956         this.destroyTrans(trans, true);
9957         var result;
9958         try {
9959             result = trans.reader.readRecords(o);
9960         }catch(e){
9961             this.fireEvent("loadexception", this, o, trans.arg, e);
9962             trans.callback.call(trans.scope||window, null, trans.arg, false);
9963             return;
9964         }
9965         this.fireEvent("load", this, o, trans.arg);
9966         trans.callback.call(trans.scope||window, result, trans.arg, true);
9967     },
9968
9969     // private
9970     handleFailure : function(trans){
9971         this.trans = false;
9972         this.destroyTrans(trans, false);
9973         this.fireEvent("loadexception", this, null, trans.arg);
9974         trans.callback.call(trans.scope||window, null, trans.arg, false);
9975     }
9976 });/*
9977  * Based on:
9978  * Ext JS Library 1.1.1
9979  * Copyright(c) 2006-2007, Ext JS, LLC.
9980  *
9981  * Originally Released Under LGPL - original licence link has changed is not relivant.
9982  *
9983  * Fork - LGPL
9984  * <script type="text/javascript">
9985  */
9986
9987 /**
9988  * @class Roo.data.JsonReader
9989  * @extends Roo.data.DataReader
9990  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9991  * based on mappings in a provided Roo.data.Record constructor.
9992  * 
9993  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9994  * in the reply previously. 
9995  * 
9996  * <p>
9997  * Example code:
9998  * <pre><code>
9999 var RecordDef = Roo.data.Record.create([
10000     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10001     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10002 ]);
10003 var myReader = new Roo.data.JsonReader({
10004     totalProperty: "results",    // The property which contains the total dataset size (optional)
10005     root: "rows",                // The property which contains an Array of row objects
10006     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10007 }, RecordDef);
10008 </code></pre>
10009  * <p>
10010  * This would consume a JSON file like this:
10011  * <pre><code>
10012 { 'results': 2, 'rows': [
10013     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10014     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10015 }
10016 </code></pre>
10017  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10018  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10019  * paged from the remote server.
10020  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10021  * @cfg {String} root name of the property which contains the Array of row objects.
10022  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10023  * @constructor
10024  * Create a new JsonReader
10025  * @param {Object} meta Metadata configuration options
10026  * @param {Object} recordType Either an Array of field definition objects,
10027  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10028  */
10029 Roo.data.JsonReader = function(meta, recordType){
10030     
10031     meta = meta || {};
10032     // set some defaults:
10033     Roo.applyIf(meta, {
10034         totalProperty: 'total',
10035         successProperty : 'success',
10036         root : 'data',
10037         id : 'id'
10038     });
10039     
10040     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10041 };
10042 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10043     
10044     /**
10045      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10046      * Used by Store query builder to append _requestMeta to params.
10047      * 
10048      */
10049     metaFromRemote : false,
10050     /**
10051      * This method is only used by a DataProxy which has retrieved data from a remote server.
10052      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10053      * @return {Object} data A data block which is used by an Roo.data.Store object as
10054      * a cache of Roo.data.Records.
10055      */
10056     read : function(response){
10057         var json = response.responseText;
10058        
10059         var o = /* eval:var:o */ eval("("+json+")");
10060         if(!o) {
10061             throw {message: "JsonReader.read: Json object not found"};
10062         }
10063         
10064         if(o.metaData){
10065             
10066             delete this.ef;
10067             this.metaFromRemote = true;
10068             this.meta = o.metaData;
10069             this.recordType = Roo.data.Record.create(o.metaData.fields);
10070             this.onMetaChange(this.meta, this.recordType, o);
10071         }
10072         return this.readRecords(o);
10073     },
10074
10075     // private function a store will implement
10076     onMetaChange : function(meta, recordType, o){
10077
10078     },
10079
10080     /**
10081          * @ignore
10082          */
10083     simpleAccess: function(obj, subsc) {
10084         return obj[subsc];
10085     },
10086
10087         /**
10088          * @ignore
10089          */
10090     getJsonAccessor: function(){
10091         var re = /[\[\.]/;
10092         return function(expr) {
10093             try {
10094                 return(re.test(expr))
10095                     ? new Function("obj", "return obj." + expr)
10096                     : function(obj){
10097                         return obj[expr];
10098                     };
10099             } catch(e){}
10100             return Roo.emptyFn;
10101         };
10102     }(),
10103
10104     /**
10105      * Create a data block containing Roo.data.Records from an XML document.
10106      * @param {Object} o An object which contains an Array of row objects in the property specified
10107      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10108      * which contains the total size of the dataset.
10109      * @return {Object} data A data block which is used by an Roo.data.Store object as
10110      * a cache of Roo.data.Records.
10111      */
10112     readRecords : function(o){
10113         /**
10114          * After any data loads, the raw JSON data is available for further custom processing.
10115          * @type Object
10116          */
10117         this.o = o;
10118         var s = this.meta, Record = this.recordType,
10119             f = Record.prototype.fields, fi = f.items, fl = f.length;
10120
10121 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10122         if (!this.ef) {
10123             if(s.totalProperty) {
10124                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10125                 }
10126                 if(s.successProperty) {
10127                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10128                 }
10129                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10130                 if (s.id) {
10131                         var g = this.getJsonAccessor(s.id);
10132                         this.getId = function(rec) {
10133                                 var r = g(rec);
10134                                 return (r === undefined || r === "") ? null : r;
10135                         };
10136                 } else {
10137                         this.getId = function(){return null;};
10138                 }
10139             this.ef = [];
10140             for(var jj = 0; jj < fl; jj++){
10141                 f = fi[jj];
10142                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10143                 this.ef[jj] = this.getJsonAccessor(map);
10144             }
10145         }
10146
10147         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10148         if(s.totalProperty){
10149             var vt = parseInt(this.getTotal(o), 10);
10150             if(!isNaN(vt)){
10151                 totalRecords = vt;
10152             }
10153         }
10154         if(s.successProperty){
10155             var vs = this.getSuccess(o);
10156             if(vs === false || vs === 'false'){
10157                 success = false;
10158             }
10159         }
10160         var records = [];
10161             for(var i = 0; i < c; i++){
10162                     var n = root[i];
10163                 var values = {};
10164                 var id = this.getId(n);
10165                 for(var j = 0; j < fl; j++){
10166                     f = fi[j];
10167                 var v = this.ef[j](n);
10168                 if (!f.convert) {
10169                     Roo.log('missing convert for ' + f.name);
10170                     Roo.log(f);
10171                     continue;
10172                 }
10173                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10174                 }
10175                 var record = new Record(values, id);
10176                 record.json = n;
10177                 records[i] = record;
10178             }
10179             return {
10180             raw : o,
10181                 success : success,
10182                 records : records,
10183                 totalRecords : totalRecords
10184             };
10185     }
10186 });/*
10187  * Based on:
10188  * Ext JS Library 1.1.1
10189  * Copyright(c) 2006-2007, Ext JS, LLC.
10190  *
10191  * Originally Released Under LGPL - original licence link has changed is not relivant.
10192  *
10193  * Fork - LGPL
10194  * <script type="text/javascript">
10195  */
10196
10197 /**
10198  * @class Roo.data.ArrayReader
10199  * @extends Roo.data.DataReader
10200  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10201  * Each element of that Array represents a row of data fields. The
10202  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10203  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10204  * <p>
10205  * Example code:.
10206  * <pre><code>
10207 var RecordDef = Roo.data.Record.create([
10208     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10209     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10210 ]);
10211 var myReader = new Roo.data.ArrayReader({
10212     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10213 }, RecordDef);
10214 </code></pre>
10215  * <p>
10216  * This would consume an Array like this:
10217  * <pre><code>
10218 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10219   </code></pre>
10220  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10221  * @constructor
10222  * Create a new JsonReader
10223  * @param {Object} meta Metadata configuration options.
10224  * @param {Object} recordType Either an Array of field definition objects
10225  * as specified to {@link Roo.data.Record#create},
10226  * or an {@link Roo.data.Record} object
10227  * created using {@link Roo.data.Record#create}.
10228  */
10229 Roo.data.ArrayReader = function(meta, recordType){
10230     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10231 };
10232
10233 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10234     /**
10235      * Create a data block containing Roo.data.Records from an XML document.
10236      * @param {Object} o An Array of row objects which represents the dataset.
10237      * @return {Object} data A data block which is used by an Roo.data.Store object as
10238      * a cache of Roo.data.Records.
10239      */
10240     readRecords : function(o){
10241         var sid = this.meta ? this.meta.id : null;
10242         var recordType = this.recordType, fields = recordType.prototype.fields;
10243         var records = [];
10244         var root = o;
10245             for(var i = 0; i < root.length; i++){
10246                     var n = root[i];
10247                 var values = {};
10248                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10249                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10250                 var f = fields.items[j];
10251                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10252                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10253                 v = f.convert(v);
10254                 values[f.name] = v;
10255             }
10256                 var record = new recordType(values, id);
10257                 record.json = n;
10258                 records[records.length] = record;
10259             }
10260             return {
10261                 records : records,
10262                 totalRecords : records.length
10263             };
10264     }
10265 });/*
10266  * - LGPL
10267  * * 
10268  */
10269
10270 /**
10271  * @class Roo.bootstrap.ComboBox
10272  * @extends Roo.bootstrap.TriggerField
10273  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10274  * @cfg {Boolean} append (true|false) default false
10275  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10276  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10277  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10278  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10279  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10280  * @constructor
10281  * Create a new ComboBox.
10282  * @param {Object} config Configuration options
10283  */
10284 Roo.bootstrap.ComboBox = function(config){
10285     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10286     this.addEvents({
10287         /**
10288          * @event expand
10289          * Fires when the dropdown list is expanded
10290              * @param {Roo.bootstrap.ComboBox} combo This combo box
10291              */
10292         'expand' : true,
10293         /**
10294          * @event collapse
10295          * Fires when the dropdown list is collapsed
10296              * @param {Roo.bootstrap.ComboBox} combo This combo box
10297              */
10298         'collapse' : true,
10299         /**
10300          * @event beforeselect
10301          * Fires before a list item is selected. Return false to cancel the selection.
10302              * @param {Roo.bootstrap.ComboBox} combo This combo box
10303              * @param {Roo.data.Record} record The data record returned from the underlying store
10304              * @param {Number} index The index of the selected item in the dropdown list
10305              */
10306         'beforeselect' : true,
10307         /**
10308          * @event select
10309          * Fires when a list item is selected
10310              * @param {Roo.bootstrap.ComboBox} combo This combo box
10311              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10312              * @param {Number} index The index of the selected item in the dropdown list
10313              */
10314         'select' : true,
10315         /**
10316          * @event beforequery
10317          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10318          * The event object passed has these properties:
10319              * @param {Roo.bootstrap.ComboBox} combo This combo box
10320              * @param {String} query The query
10321              * @param {Boolean} forceAll true to force "all" query
10322              * @param {Boolean} cancel true to cancel the query
10323              * @param {Object} e The query event object
10324              */
10325         'beforequery': true,
10326          /**
10327          * @event add
10328          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10329              * @param {Roo.bootstrap.ComboBox} combo This combo box
10330              */
10331         'add' : true,
10332         /**
10333          * @event edit
10334          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10335              * @param {Roo.bootstrap.ComboBox} combo This combo box
10336              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10337              */
10338         'edit' : true,
10339         /**
10340          * @event remove
10341          * Fires when the remove value from the combobox array
10342              * @param {Roo.bootstrap.ComboBox} combo This combo box
10343              */
10344         'remove' : true
10345         
10346     });
10347     
10348     this.item = [];
10349     this.tickItems = [];
10350     
10351     this.selectedIndex = -1;
10352     if(this.mode == 'local'){
10353         if(config.queryDelay === undefined){
10354             this.queryDelay = 10;
10355         }
10356         if(config.minChars === undefined){
10357             this.minChars = 0;
10358         }
10359     }
10360 };
10361
10362 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10363      
10364     /**
10365      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10366      * rendering into an Roo.Editor, defaults to false)
10367      */
10368     /**
10369      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10370      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10371      */
10372     /**
10373      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10374      */
10375     /**
10376      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10377      * the dropdown list (defaults to undefined, with no header element)
10378      */
10379
10380      /**
10381      * @cfg {String/Roo.Template} tpl The template to use to render the output
10382      */
10383      
10384      /**
10385      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10386      */
10387     listWidth: undefined,
10388     /**
10389      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10390      * mode = 'remote' or 'text' if mode = 'local')
10391      */
10392     displayField: undefined,
10393     /**
10394      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10395      * mode = 'remote' or 'value' if mode = 'local'). 
10396      * Note: use of a valueField requires the user make a selection
10397      * in order for a value to be mapped.
10398      */
10399     valueField: undefined,
10400     
10401     
10402     /**
10403      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10404      * field's data value (defaults to the underlying DOM element's name)
10405      */
10406     hiddenName: undefined,
10407     /**
10408      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10409      */
10410     listClass: '',
10411     /**
10412      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10413      */
10414     selectedClass: 'active',
10415     
10416     /**
10417      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10418      */
10419     shadow:'sides',
10420     /**
10421      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10422      * anchor positions (defaults to 'tl-bl')
10423      */
10424     listAlign: 'tl-bl?',
10425     /**
10426      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10427      */
10428     maxHeight: 300,
10429     /**
10430      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10431      * query specified by the allQuery config option (defaults to 'query')
10432      */
10433     triggerAction: 'query',
10434     /**
10435      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10436      * (defaults to 4, does not apply if editable = false)
10437      */
10438     minChars : 4,
10439     /**
10440      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10441      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10442      */
10443     typeAhead: false,
10444     /**
10445      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10446      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10447      */
10448     queryDelay: 500,
10449     /**
10450      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10451      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10452      */
10453     pageSize: 0,
10454     /**
10455      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10456      * when editable = true (defaults to false)
10457      */
10458     selectOnFocus:false,
10459     /**
10460      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10461      */
10462     queryParam: 'query',
10463     /**
10464      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10465      * when mode = 'remote' (defaults to 'Loading...')
10466      */
10467     loadingText: 'Loading...',
10468     /**
10469      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10470      */
10471     resizable: false,
10472     /**
10473      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10474      */
10475     handleHeight : 8,
10476     /**
10477      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10478      * traditional select (defaults to true)
10479      */
10480     editable: true,
10481     /**
10482      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10483      */
10484     allQuery: '',
10485     /**
10486      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10487      */
10488     mode: 'remote',
10489     /**
10490      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10491      * listWidth has a higher value)
10492      */
10493     minListWidth : 70,
10494     /**
10495      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10496      * allow the user to set arbitrary text into the field (defaults to false)
10497      */
10498     forceSelection:false,
10499     /**
10500      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10501      * if typeAhead = true (defaults to 250)
10502      */
10503     typeAheadDelay : 250,
10504     /**
10505      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10506      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10507      */
10508     valueNotFoundText : undefined,
10509     /**
10510      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10511      */
10512     blockFocus : false,
10513     
10514     /**
10515      * @cfg {Boolean} disableClear Disable showing of clear button.
10516      */
10517     disableClear : false,
10518     /**
10519      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10520      */
10521     alwaysQuery : false,
10522     
10523     /**
10524      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10525      */
10526     multiple : false,
10527     
10528     //private
10529     addicon : false,
10530     editicon: false,
10531     
10532     page: 0,
10533     hasQuery: false,
10534     append: false,
10535     loadNext: false,
10536     autoFocus : true,
10537     tickable : false,
10538     btnPosition : 'right',
10539     triggerList : true,
10540     showToggleBtn : true,
10541     // element that contains real text value.. (when hidden is used..)
10542     
10543     getAutoCreate : function()
10544     {
10545         var cfg = false;
10546         
10547         /*
10548          *  Normal ComboBox
10549          */
10550         if(!this.tickable){
10551             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10552             return cfg;
10553         }
10554         
10555         /*
10556          *  ComboBox with tickable selections
10557          */
10558              
10559         var align = this.labelAlign || this.parentLabelAlign();
10560         
10561         cfg = {
10562             cls : 'form-group roo-combobox-tickable' //input-group
10563         };
10564         
10565         
10566         var buttons = {
10567             tag : 'div',
10568             cls : 'tickable-buttons',
10569             cn : [
10570                 {
10571                     tag : 'button',
10572                     type : 'button',
10573                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10574                     html : 'Edit'
10575                 },
10576                 {
10577                     tag : 'button',
10578                     type : 'button',
10579                     name : 'ok',
10580                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10581                     html : 'Done'
10582                 },
10583                 {
10584                     tag : 'button',
10585                     type : 'button',
10586                     name : 'cancel',
10587                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10588                     html : 'Cancel'
10589                 }
10590             ]
10591         };
10592         
10593         var _this = this;
10594         Roo.each(buttons.cn, function(c){
10595             if (_this.size) {
10596                 c.cls += ' btn-' + _this.size;
10597             }
10598
10599             if (_this.disabled) {
10600                 c.disabled = true;
10601             }
10602         });
10603         
10604         var box = {
10605             tag: 'div',
10606             cn: [
10607                 {
10608                     tag: 'input',
10609                     type : 'hidden',
10610                     cls: 'form-hidden-field'
10611                 },
10612                 {
10613                     tag: 'ul',
10614                     cls: 'select2-choices',
10615                     cn:[
10616                         {
10617                             tag: 'li',
10618                             cls: 'select2-search-field',
10619                             cn: [
10620
10621                                 buttons
10622                             ]
10623                         }
10624                     ]
10625                 }
10626             ]
10627         }
10628         
10629         var combobox = {
10630             cls: 'select2-container input-group select2-container-multi',
10631             cn: [
10632                 box
10633 //                {
10634 //                    tag: 'ul',
10635 //                    cls: 'typeahead typeahead-long dropdown-menu',
10636 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10637 //                }
10638             ]
10639         };
10640         
10641         if (align ==='left' && this.fieldLabel.length) {
10642             
10643                 Roo.log("left and has label");
10644                 cfg.cn = [
10645                     
10646                     {
10647                         tag: 'label',
10648                         'for' :  id,
10649                         cls : 'control-label col-sm-' + this.labelWidth,
10650                         html : this.fieldLabel
10651                         
10652                     },
10653                     {
10654                         cls : "col-sm-" + (12 - this.labelWidth), 
10655                         cn: [
10656                             combobox
10657                         ]
10658                     }
10659                     
10660                 ];
10661         } else if ( this.fieldLabel.length) {
10662                 Roo.log(" label");
10663                  cfg.cn = [
10664                    
10665                     {
10666                         tag: 'label',
10667                         //cls : 'input-group-addon',
10668                         html : this.fieldLabel
10669                         
10670                     },
10671                     
10672                     combobox
10673                     
10674                 ];
10675
10676         } else {
10677             
10678                 Roo.log(" no label && no align");
10679                 cfg = combobox
10680                      
10681                 
10682         }
10683          
10684         var settings=this;
10685         ['xs','sm','md','lg'].map(function(size){
10686             if (settings[size]) {
10687                 cfg.cls += ' col-' + size + '-' + settings[size];
10688             }
10689         });
10690         
10691         return cfg;
10692         
10693     },
10694     
10695     // private
10696     initEvents: function()
10697     {
10698         
10699         if (!this.store) {
10700             throw "can not find store for combo";
10701         }
10702         this.store = Roo.factory(this.store, Roo.data);
10703         
10704         if(this.tickable){
10705             this.initTickableEvents();
10706             return;
10707         }
10708         
10709         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10710         
10711         if(this.hiddenName){
10712             
10713             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10714             
10715             this.hiddenField.dom.value =
10716                 this.hiddenValue !== undefined ? this.hiddenValue :
10717                 this.value !== undefined ? this.value : '';
10718
10719             // prevent input submission
10720             this.el.dom.removeAttribute('name');
10721             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10722              
10723              
10724         }
10725         //if(Roo.isGecko){
10726         //    this.el.dom.setAttribute('autocomplete', 'off');
10727         //}
10728         
10729         var cls = 'x-combo-list';
10730         
10731         //this.list = new Roo.Layer({
10732         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10733         //});
10734         
10735         var _this = this;
10736         
10737         (function(){
10738             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10739             _this.list.setWidth(lw);
10740         }).defer(100);
10741         
10742         this.list.on('mouseover', this.onViewOver, this);
10743         this.list.on('mousemove', this.onViewMove, this);
10744         
10745         this.list.on('scroll', this.onViewScroll, this);
10746         
10747         /*
10748         this.list.swallowEvent('mousewheel');
10749         this.assetHeight = 0;
10750
10751         if(this.title){
10752             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10753             this.assetHeight += this.header.getHeight();
10754         }
10755
10756         this.innerList = this.list.createChild({cls:cls+'-inner'});
10757         this.innerList.on('mouseover', this.onViewOver, this);
10758         this.innerList.on('mousemove', this.onViewMove, this);
10759         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10760         
10761         if(this.allowBlank && !this.pageSize && !this.disableClear){
10762             this.footer = this.list.createChild({cls:cls+'-ft'});
10763             this.pageTb = new Roo.Toolbar(this.footer);
10764            
10765         }
10766         if(this.pageSize){
10767             this.footer = this.list.createChild({cls:cls+'-ft'});
10768             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10769                     {pageSize: this.pageSize});
10770             
10771         }
10772         
10773         if (this.pageTb && this.allowBlank && !this.disableClear) {
10774             var _this = this;
10775             this.pageTb.add(new Roo.Toolbar.Fill(), {
10776                 cls: 'x-btn-icon x-btn-clear',
10777                 text: '&#160;',
10778                 handler: function()
10779                 {
10780                     _this.collapse();
10781                     _this.clearValue();
10782                     _this.onSelect(false, -1);
10783                 }
10784             });
10785         }
10786         if (this.footer) {
10787             this.assetHeight += this.footer.getHeight();
10788         }
10789         */
10790             
10791         if(!this.tpl){
10792             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10793         }
10794
10795         this.view = new Roo.View(this.list, this.tpl, {
10796             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10797         });
10798         //this.view.wrapEl.setDisplayed(false);
10799         this.view.on('click', this.onViewClick, this);
10800         
10801         
10802         
10803         this.store.on('beforeload', this.onBeforeLoad, this);
10804         this.store.on('load', this.onLoad, this);
10805         this.store.on('loadexception', this.onLoadException, this);
10806         /*
10807         if(this.resizable){
10808             this.resizer = new Roo.Resizable(this.list,  {
10809                pinned:true, handles:'se'
10810             });
10811             this.resizer.on('resize', function(r, w, h){
10812                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10813                 this.listWidth = w;
10814                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10815                 this.restrictHeight();
10816             }, this);
10817             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10818         }
10819         */
10820         if(!this.editable){
10821             this.editable = true;
10822             this.setEditable(false);
10823         }
10824         
10825         /*
10826         
10827         if (typeof(this.events.add.listeners) != 'undefined') {
10828             
10829             this.addicon = this.wrap.createChild(
10830                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10831        
10832             this.addicon.on('click', function(e) {
10833                 this.fireEvent('add', this);
10834             }, this);
10835         }
10836         if (typeof(this.events.edit.listeners) != 'undefined') {
10837             
10838             this.editicon = this.wrap.createChild(
10839                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10840             if (this.addicon) {
10841                 this.editicon.setStyle('margin-left', '40px');
10842             }
10843             this.editicon.on('click', function(e) {
10844                 
10845                 // we fire even  if inothing is selected..
10846                 this.fireEvent('edit', this, this.lastData );
10847                 
10848             }, this);
10849         }
10850         */
10851         
10852         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10853             "up" : function(e){
10854                 this.inKeyMode = true;
10855                 this.selectPrev();
10856             },
10857
10858             "down" : function(e){
10859                 if(!this.isExpanded()){
10860                     this.onTriggerClick();
10861                 }else{
10862                     this.inKeyMode = true;
10863                     this.selectNext();
10864                 }
10865             },
10866
10867             "enter" : function(e){
10868 //                this.onViewClick();
10869                 //return true;
10870                 this.collapse();
10871                 
10872                 if(this.fireEvent("specialkey", this, e)){
10873                     this.onViewClick(false);
10874                 }
10875                 
10876                 return true;
10877             },
10878
10879             "esc" : function(e){
10880                 this.collapse();
10881             },
10882
10883             "tab" : function(e){
10884                 this.collapse();
10885                 
10886                 if(this.fireEvent("specialkey", this, e)){
10887                     this.onViewClick(false);
10888                 }
10889                 
10890                 return true;
10891             },
10892
10893             scope : this,
10894
10895             doRelay : function(foo, bar, hname){
10896                 if(hname == 'down' || this.scope.isExpanded()){
10897                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10898                 }
10899                 return true;
10900             },
10901
10902             forceKeyDown: true
10903         });
10904         
10905         
10906         this.queryDelay = Math.max(this.queryDelay || 10,
10907                 this.mode == 'local' ? 10 : 250);
10908         
10909         
10910         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10911         
10912         if(this.typeAhead){
10913             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10914         }
10915         if(this.editable !== false){
10916             this.inputEl().on("keyup", this.onKeyUp, this);
10917         }
10918         if(this.forceSelection){
10919             this.inputEl().on('blur', this.doForce, this);
10920         }
10921         
10922         if(this.multiple){
10923             this.choices = this.el.select('ul.select2-choices', true).first();
10924             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10925         }
10926     },
10927     
10928     initTickableEvents: function()
10929     {   
10930         this.createList();
10931         
10932         if(this.hiddenName){
10933             
10934             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10935             
10936             this.hiddenField.dom.value =
10937                 this.hiddenValue !== undefined ? this.hiddenValue :
10938                 this.value !== undefined ? this.value : '';
10939
10940             // prevent input submission
10941             this.el.dom.removeAttribute('name');
10942             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10943              
10944              
10945         }
10946         
10947 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10948         
10949         this.choices = this.el.select('ul.select2-choices', true).first();
10950         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10951         if(this.triggerList){
10952             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10953         }
10954          
10955         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10956         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10957         
10958         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10959         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10960         
10961         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10962         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10963         
10964         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10965         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10966         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10967         
10968         this.okBtn.hide();
10969         this.cancelBtn.hide();
10970         
10971         var _this = this;
10972         
10973         (function(){
10974             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10975             _this.list.setWidth(lw);
10976         }).defer(100);
10977         
10978         this.list.on('mouseover', this.onViewOver, this);
10979         this.list.on('mousemove', this.onViewMove, this);
10980         
10981         this.list.on('scroll', this.onViewScroll, this);
10982         
10983         if(!this.tpl){
10984             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>';
10985         }
10986
10987         this.view = new Roo.View(this.list, this.tpl, {
10988             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10989         });
10990         
10991         //this.view.wrapEl.setDisplayed(false);
10992         this.view.on('click', this.onViewClick, this);
10993         
10994         
10995         
10996         this.store.on('beforeload', this.onBeforeLoad, this);
10997         this.store.on('load', this.onLoad, this);
10998         this.store.on('loadexception', this.onLoadException, this);
10999         
11000 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11001 //            "up" : function(e){
11002 //                this.inKeyMode = true;
11003 //                this.selectPrev();
11004 //            },
11005 //
11006 //            "down" : function(e){
11007 //                if(!this.isExpanded()){
11008 //                    this.onTriggerClick();
11009 //                }else{
11010 //                    this.inKeyMode = true;
11011 //                    this.selectNext();
11012 //                }
11013 //            },
11014 //
11015 //            "enter" : function(e){
11016 ////                this.onViewClick();
11017 //                //return true;
11018 //                this.collapse();
11019 //                
11020 //                if(this.fireEvent("specialkey", this, e)){
11021 //                    this.onViewClick(false);
11022 //                }
11023 //                
11024 //                return true;
11025 //            },
11026 //
11027 //            "esc" : function(e){
11028 //                this.collapse();
11029 //            },
11030 //
11031 //            "tab" : function(e){
11032 //                this.collapse();
11033 //                
11034 //                if(this.fireEvent("specialkey", this, e)){
11035 //                    this.onViewClick(false);
11036 //                }
11037 //                
11038 //                return true;
11039 //            },
11040 //
11041 //            scope : this,
11042 //
11043 //            doRelay : function(foo, bar, hname){
11044 //                if(hname == 'down' || this.scope.isExpanded()){
11045 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11046 //                }
11047 //                return true;
11048 //            },
11049 //
11050 //            forceKeyDown: true
11051 //        });
11052         
11053         
11054         this.queryDelay = Math.max(this.queryDelay || 10,
11055                 this.mode == 'local' ? 10 : 250);
11056         
11057         
11058         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11059         
11060         if(this.typeAhead){
11061             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11062         }
11063     },
11064
11065     onDestroy : function(){
11066         if(this.view){
11067             this.view.setStore(null);
11068             this.view.el.removeAllListeners();
11069             this.view.el.remove();
11070             this.view.purgeListeners();
11071         }
11072         if(this.list){
11073             this.list.dom.innerHTML  = '';
11074         }
11075         
11076         if(this.store){
11077             this.store.un('beforeload', this.onBeforeLoad, this);
11078             this.store.un('load', this.onLoad, this);
11079             this.store.un('loadexception', this.onLoadException, this);
11080         }
11081         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11082     },
11083
11084     // private
11085     fireKey : function(e){
11086         if(e.isNavKeyPress() && !this.list.isVisible()){
11087             this.fireEvent("specialkey", this, e);
11088         }
11089     },
11090
11091     // private
11092     onResize: function(w, h){
11093 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11094 //        
11095 //        if(typeof w != 'number'){
11096 //            // we do not handle it!?!?
11097 //            return;
11098 //        }
11099 //        var tw = this.trigger.getWidth();
11100 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11101 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11102 //        var x = w - tw;
11103 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11104 //            
11105 //        //this.trigger.setStyle('left', x+'px');
11106 //        
11107 //        if(this.list && this.listWidth === undefined){
11108 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11109 //            this.list.setWidth(lw);
11110 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11111 //        }
11112         
11113     
11114         
11115     },
11116
11117     /**
11118      * Allow or prevent the user from directly editing the field text.  If false is passed,
11119      * the user will only be able to select from the items defined in the dropdown list.  This method
11120      * is the runtime equivalent of setting the 'editable' config option at config time.
11121      * @param {Boolean} value True to allow the user to directly edit the field text
11122      */
11123     setEditable : function(value){
11124         if(value == this.editable){
11125             return;
11126         }
11127         this.editable = value;
11128         if(!value){
11129             this.inputEl().dom.setAttribute('readOnly', true);
11130             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11131             this.inputEl().addClass('x-combo-noedit');
11132         }else{
11133             this.inputEl().dom.setAttribute('readOnly', false);
11134             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11135             this.inputEl().removeClass('x-combo-noedit');
11136         }
11137     },
11138
11139     // private
11140     
11141     onBeforeLoad : function(combo,opts){
11142         if(!this.hasFocus){
11143             return;
11144         }
11145          if (!opts.add) {
11146             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11147          }
11148         this.restrictHeight();
11149         this.selectedIndex = -1;
11150     },
11151
11152     // private
11153     onLoad : function(){
11154         
11155         this.hasQuery = false;
11156         
11157         if(!this.hasFocus){
11158             return;
11159         }
11160         
11161         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11162             this.loading.hide();
11163         }
11164         
11165         if(this.store.getCount() > 0){
11166             this.expand();
11167 //            this.restrictHeight();
11168             if(this.lastQuery == this.allQuery){
11169                 if(this.editable && !this.tickable){
11170                     this.inputEl().dom.select();
11171                 }
11172                 
11173                 if(
11174                     !this.selectByValue(this.value, true) &&
11175                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11176                     this.store.lastOptions.add != true)
11177                 ){
11178                     this.select(0, true);
11179                 }
11180             }else{
11181                 if(this.autoFocus){
11182                     this.selectNext();
11183                 }
11184                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11185                     this.taTask.delay(this.typeAheadDelay);
11186                 }
11187             }
11188         }else{
11189             this.onEmptyResults();
11190         }
11191         
11192         //this.el.focus();
11193     },
11194     // private
11195     onLoadException : function()
11196     {
11197         this.hasQuery = false;
11198         
11199         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11200             this.loading.hide();
11201         }
11202         
11203         this.collapse();
11204         Roo.log(this.store.reader.jsonData);
11205         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11206             // fixme
11207             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11208         }
11209         
11210         
11211     },
11212     // private
11213     onTypeAhead : function(){
11214         if(this.store.getCount() > 0){
11215             var r = this.store.getAt(0);
11216             var newValue = r.data[this.displayField];
11217             var len = newValue.length;
11218             var selStart = this.getRawValue().length;
11219             
11220             if(selStart != len){
11221                 this.setRawValue(newValue);
11222                 this.selectText(selStart, newValue.length);
11223             }
11224         }
11225     },
11226
11227     // private
11228     onSelect : function(record, index){
11229         
11230         if(this.fireEvent('beforeselect', this, record, index) !== false){
11231         
11232             this.setFromData(index > -1 ? record.data : false);
11233             
11234             this.collapse();
11235             this.fireEvent('select', this, record, index);
11236         }
11237     },
11238
11239     /**
11240      * Returns the currently selected field value or empty string if no value is set.
11241      * @return {String} value The selected value
11242      */
11243     getValue : function(){
11244         
11245         if(this.multiple){
11246             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11247         }
11248         
11249         if(this.valueField){
11250             return typeof this.value != 'undefined' ? this.value : '';
11251         }else{
11252             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11253         }
11254     },
11255
11256     /**
11257      * Clears any text/value currently set in the field
11258      */
11259     clearValue : function(){
11260         if(this.hiddenField){
11261             this.hiddenField.dom.value = '';
11262         }
11263         this.value = '';
11264         this.setRawValue('');
11265         this.lastSelectionText = '';
11266         
11267     },
11268
11269     /**
11270      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11271      * will be displayed in the field.  If the value does not match the data value of an existing item,
11272      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11273      * Otherwise the field will be blank (although the value will still be set).
11274      * @param {String} value The value to match
11275      */
11276     setValue : function(v){
11277         if(this.multiple){
11278             this.syncValue();
11279             return;
11280         }
11281         
11282         var text = v;
11283         if(this.valueField){
11284             var r = this.findRecord(this.valueField, v);
11285             if(r){
11286                 text = r.data[this.displayField];
11287             }else if(this.valueNotFoundText !== undefined){
11288                 text = this.valueNotFoundText;
11289             }
11290         }
11291         this.lastSelectionText = text;
11292         if(this.hiddenField){
11293             this.hiddenField.dom.value = v;
11294         }
11295         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11296         this.value = v;
11297     },
11298     /**
11299      * @property {Object} the last set data for the element
11300      */
11301     
11302     lastData : false,
11303     /**
11304      * Sets the value of the field based on a object which is related to the record format for the store.
11305      * @param {Object} value the value to set as. or false on reset?
11306      */
11307     setFromData : function(o){
11308         
11309         if(this.multiple){
11310             if(typeof o.display_name !== 'string'){
11311                 for(var i=0;i<o.display_name.length;i++){
11312                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11313                 }
11314                 return;
11315             }
11316             this.addItem(o);
11317             return;
11318         }
11319             
11320         var dv = ''; // display value
11321         var vv = ''; // value value..
11322         this.lastData = o;
11323         if (this.displayField) {
11324             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11325         } else {
11326             // this is an error condition!!!
11327             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11328         }
11329         
11330         if(this.valueField){
11331             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11332         }
11333         
11334         if(this.hiddenField){
11335             this.hiddenField.dom.value = vv;
11336             
11337             this.lastSelectionText = dv;
11338             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11339             this.value = vv;
11340             return;
11341         }
11342         // no hidden field.. - we store the value in 'value', but still display
11343         // display field!!!!
11344         this.lastSelectionText = dv;
11345         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11346         this.value = vv;
11347         
11348         
11349     },
11350     // private
11351     reset : function(){
11352         // overridden so that last data is reset..
11353         this.setValue(this.originalValue);
11354         this.clearInvalid();
11355         this.lastData = false;
11356         if (this.view) {
11357             this.view.clearSelections();
11358         }
11359     },
11360     // private
11361     findRecord : function(prop, value){
11362         var record;
11363         if(this.store.getCount() > 0){
11364             this.store.each(function(r){
11365                 if(r.data[prop] == value){
11366                     record = r;
11367                     return false;
11368                 }
11369                 return true;
11370             });
11371         }
11372         return record;
11373     },
11374     
11375     getName: function()
11376     {
11377         // returns hidden if it's set..
11378         if (!this.rendered) {return ''};
11379         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11380         
11381     },
11382     // private
11383     onViewMove : function(e, t){
11384         this.inKeyMode = false;
11385     },
11386
11387     // private
11388     onViewOver : function(e, t){
11389         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11390             return;
11391         }
11392         var item = this.view.findItemFromChild(t);
11393         
11394         if(item){
11395             var index = this.view.indexOf(item);
11396             this.select(index, false);
11397         }
11398     },
11399
11400     // private
11401     onViewClick : function(view, doFocus, el, e)
11402     {
11403         var index = this.view.getSelectedIndexes()[0];
11404         
11405         var r = this.store.getAt(index);
11406         
11407         if(this.tickable){
11408             
11409             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11410                 return;
11411             }
11412             
11413             var rm = false;
11414             var _this = this;
11415             
11416             Roo.each(this.tickItems, function(v,k){
11417                 
11418                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11419                     _this.tickItems.splice(k, 1);
11420                     rm = true;
11421                     return;
11422                 }
11423             })
11424             
11425             if(rm){
11426                 return;
11427             }
11428             
11429             this.tickItems.push(r.data);
11430             return;
11431         }
11432         
11433         if(r){
11434             this.onSelect(r, index);
11435         }
11436         if(doFocus !== false && !this.blockFocus){
11437             this.inputEl().focus();
11438         }
11439     },
11440
11441     // private
11442     restrictHeight : function(){
11443         //this.innerList.dom.style.height = '';
11444         //var inner = this.innerList.dom;
11445         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11446         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11447         //this.list.beginUpdate();
11448         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11449         this.list.alignTo(this.inputEl(), this.listAlign);
11450         this.list.alignTo(this.inputEl(), this.listAlign);
11451         //this.list.endUpdate();
11452     },
11453
11454     // private
11455     onEmptyResults : function(){
11456         this.collapse();
11457     },
11458
11459     /**
11460      * Returns true if the dropdown list is expanded, else false.
11461      */
11462     isExpanded : function(){
11463         return this.list.isVisible();
11464     },
11465
11466     /**
11467      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11468      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11469      * @param {String} value The data value of the item to select
11470      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11471      * selected item if it is not currently in view (defaults to true)
11472      * @return {Boolean} True if the value matched an item in the list, else false
11473      */
11474     selectByValue : function(v, scrollIntoView){
11475         if(v !== undefined && v !== null){
11476             var r = this.findRecord(this.valueField || this.displayField, v);
11477             if(r){
11478                 this.select(this.store.indexOf(r), scrollIntoView);
11479                 return true;
11480             }
11481         }
11482         return false;
11483     },
11484
11485     /**
11486      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11487      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11488      * @param {Number} index The zero-based index of the list item to select
11489      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11490      * selected item if it is not currently in view (defaults to true)
11491      */
11492     select : function(index, scrollIntoView){
11493         this.selectedIndex = index;
11494         this.view.select(index);
11495         if(scrollIntoView !== false){
11496             var el = this.view.getNode(index);
11497             if(el && !this.multiple && !this.tickable){
11498                 this.list.scrollChildIntoView(el, false);
11499             }
11500         }
11501     },
11502
11503     // private
11504     selectNext : function(){
11505         var ct = this.store.getCount();
11506         if(ct > 0){
11507             if(this.selectedIndex == -1){
11508                 this.select(0);
11509             }else if(this.selectedIndex < ct-1){
11510                 this.select(this.selectedIndex+1);
11511             }
11512         }
11513     },
11514
11515     // private
11516     selectPrev : function(){
11517         var ct = this.store.getCount();
11518         if(ct > 0){
11519             if(this.selectedIndex == -1){
11520                 this.select(0);
11521             }else if(this.selectedIndex != 0){
11522                 this.select(this.selectedIndex-1);
11523             }
11524         }
11525     },
11526
11527     // private
11528     onKeyUp : function(e){
11529         if(this.editable !== false && !e.isSpecialKey()){
11530             this.lastKey = e.getKey();
11531             this.dqTask.delay(this.queryDelay);
11532         }
11533     },
11534
11535     // private
11536     validateBlur : function(){
11537         return !this.list || !this.list.isVisible();   
11538     },
11539
11540     // private
11541     initQuery : function(){
11542         this.doQuery(this.getRawValue());
11543     },
11544
11545     // private
11546     doForce : function(){
11547         if(this.inputEl().dom.value.length > 0){
11548             this.inputEl().dom.value =
11549                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11550              
11551         }
11552     },
11553
11554     /**
11555      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11556      * query allowing the query action to be canceled if needed.
11557      * @param {String} query The SQL query to execute
11558      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11559      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11560      * saved in the current store (defaults to false)
11561      */
11562     doQuery : function(q, forceAll){
11563         
11564         if(q === undefined || q === null){
11565             q = '';
11566         }
11567         var qe = {
11568             query: q,
11569             forceAll: forceAll,
11570             combo: this,
11571             cancel:false
11572         };
11573         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11574             return false;
11575         }
11576         q = qe.query;
11577         
11578         forceAll = qe.forceAll;
11579         if(forceAll === true || (q.length >= this.minChars)){
11580             
11581             this.hasQuery = true;
11582             
11583             if(this.lastQuery != q || this.alwaysQuery){
11584                 this.lastQuery = q;
11585                 if(this.mode == 'local'){
11586                     this.selectedIndex = -1;
11587                     if(forceAll){
11588                         this.store.clearFilter();
11589                     }else{
11590                         this.store.filter(this.displayField, q);
11591                     }
11592                     this.onLoad();
11593                 }else{
11594                     this.store.baseParams[this.queryParam] = q;
11595                     
11596                     var options = {params : this.getParams(q)};
11597                     
11598                     if(this.loadNext){
11599                         options.add = true;
11600                         options.params.start = this.page * this.pageSize;
11601                     }
11602                     
11603                     this.store.load(options);
11604                     /*
11605                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11606                      *  we should expand the list on onLoad
11607                      *  so command out it
11608                      */
11609 //                    this.expand();
11610                 }
11611             }else{
11612                 this.selectedIndex = -1;
11613                 this.onLoad();   
11614             }
11615         }
11616         
11617         this.loadNext = false;
11618     },
11619
11620     // private
11621     getParams : function(q){
11622         var p = {};
11623         //p[this.queryParam] = q;
11624         
11625         if(this.pageSize){
11626             p.start = 0;
11627             p.limit = this.pageSize;
11628         }
11629         return p;
11630     },
11631
11632     /**
11633      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11634      */
11635     collapse : function(){
11636         if(!this.isExpanded()){
11637             return;
11638         }
11639         
11640         this.list.hide();
11641         
11642         if(this.tickable){
11643             this.okBtn.hide();
11644             this.cancelBtn.hide();
11645             this.trigger.show();
11646         }
11647         
11648         Roo.get(document).un('mousedown', this.collapseIf, this);
11649         Roo.get(document).un('mousewheel', this.collapseIf, this);
11650         if (!this.editable) {
11651             Roo.get(document).un('keydown', this.listKeyPress, this);
11652         }
11653         this.fireEvent('collapse', this);
11654     },
11655
11656     // private
11657     collapseIf : function(e){
11658         var in_combo  = e.within(this.el);
11659         var in_list =  e.within(this.list);
11660         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11661         
11662         if (in_combo || in_list || is_list) {
11663             //e.stopPropagation();
11664             return;
11665         }
11666         
11667         if(this.tickable){
11668             this.onTickableFooterButtonClick(e, false, false);
11669         }
11670
11671         this.collapse();
11672         
11673     },
11674
11675     /**
11676      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11677      */
11678     expand : function(){
11679        
11680         if(this.isExpanded() || !this.hasFocus){
11681             return;
11682         }
11683         
11684         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11685         this.list.setWidth(lw);
11686         
11687         
11688          Roo.log('expand');
11689         
11690         this.list.show();
11691         
11692         this.restrictHeight();
11693         
11694         if(this.tickable){
11695             
11696             this.tickItems = Roo.apply([], this.item);
11697             
11698             this.okBtn.show();
11699             this.cancelBtn.show();
11700             this.trigger.hide();
11701             
11702         }
11703         
11704         Roo.get(document).on('mousedown', this.collapseIf, this);
11705         Roo.get(document).on('mousewheel', this.collapseIf, this);
11706         if (!this.editable) {
11707             Roo.get(document).on('keydown', this.listKeyPress, this);
11708         }
11709         
11710         this.fireEvent('expand', this);
11711     },
11712
11713     // private
11714     // Implements the default empty TriggerField.onTriggerClick function
11715     onTriggerClick : function(e)
11716     {
11717         Roo.log('trigger click');
11718         
11719         if(this.disabled || !this.triggerList){
11720             return;
11721         }
11722         
11723         this.page = 0;
11724         this.loadNext = false;
11725         
11726         if(this.isExpanded()){
11727             this.collapse();
11728             if (!this.blockFocus) {
11729                 this.inputEl().focus();
11730             }
11731             
11732         }else {
11733             this.hasFocus = true;
11734             if(this.triggerAction == 'all') {
11735                 this.doQuery(this.allQuery, true);
11736             } else {
11737                 this.doQuery(this.getRawValue());
11738             }
11739             if (!this.blockFocus) {
11740                 this.inputEl().focus();
11741             }
11742         }
11743     },
11744     
11745     onTickableTriggerClick : function(e)
11746     {
11747         if(this.disabled){
11748             return;
11749         }
11750         
11751         this.page = 0;
11752         this.loadNext = false;
11753         this.hasFocus = true;
11754         
11755         if(this.triggerAction == 'all') {
11756             this.doQuery(this.allQuery, true);
11757         } else {
11758             this.doQuery(this.getRawValue());
11759         }
11760     },
11761     
11762     onSearchFieldClick : function(e)
11763     {
11764         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11765             return;
11766         }
11767         
11768         this.page = 0;
11769         this.loadNext = false;
11770         this.hasFocus = true;
11771         
11772         if(this.triggerAction == 'all') {
11773             this.doQuery(this.allQuery, true);
11774         } else {
11775             this.doQuery(this.getRawValue());
11776         }
11777     },
11778     
11779     listKeyPress : function(e)
11780     {
11781         //Roo.log('listkeypress');
11782         // scroll to first matching element based on key pres..
11783         if (e.isSpecialKey()) {
11784             return false;
11785         }
11786         var k = String.fromCharCode(e.getKey()).toUpperCase();
11787         //Roo.log(k);
11788         var match  = false;
11789         var csel = this.view.getSelectedNodes();
11790         var cselitem = false;
11791         if (csel.length) {
11792             var ix = this.view.indexOf(csel[0]);
11793             cselitem  = this.store.getAt(ix);
11794             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11795                 cselitem = false;
11796             }
11797             
11798         }
11799         
11800         this.store.each(function(v) { 
11801             if (cselitem) {
11802                 // start at existing selection.
11803                 if (cselitem.id == v.id) {
11804                     cselitem = false;
11805                 }
11806                 return true;
11807             }
11808                 
11809             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11810                 match = this.store.indexOf(v);
11811                 return false;
11812             }
11813             return true;
11814         }, this);
11815         
11816         if (match === false) {
11817             return true; // no more action?
11818         }
11819         // scroll to?
11820         this.view.select(match);
11821         var sn = Roo.get(this.view.getSelectedNodes()[0])
11822         //sn.scrollIntoView(sn.dom.parentNode, false);
11823     },
11824     
11825     onViewScroll : function(e, t){
11826         
11827         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){
11828             return;
11829         }
11830         
11831         this.hasQuery = true;
11832         
11833         this.loading = this.list.select('.loading', true).first();
11834         
11835         if(this.loading === null){
11836             this.list.createChild({
11837                 tag: 'div',
11838                 cls: 'loading select2-more-results select2-active',
11839                 html: 'Loading more results...'
11840             })
11841             
11842             this.loading = this.list.select('.loading', true).first();
11843             
11844             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11845             
11846             this.loading.hide();
11847         }
11848         
11849         this.loading.show();
11850         
11851         var _combo = this;
11852         
11853         this.page++;
11854         this.loadNext = true;
11855         
11856         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11857         
11858         return;
11859     },
11860     
11861     addItem : function(o)
11862     {   
11863         var dv = ''; // display value
11864         
11865         if (this.displayField) {
11866             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11867         } else {
11868             // this is an error condition!!!
11869             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11870         }
11871         
11872         if(!dv.length){
11873             return;
11874         }
11875         
11876         var choice = this.choices.createChild({
11877             tag: 'li',
11878             cls: 'select2-search-choice',
11879             cn: [
11880                 {
11881                     tag: 'div',
11882                     html: dv
11883                 },
11884                 {
11885                     tag: 'a',
11886                     href: '#',
11887                     cls: 'select2-search-choice-close',
11888                     tabindex: '-1'
11889                 }
11890             ]
11891             
11892         }, this.searchField);
11893         
11894         var close = choice.select('a.select2-search-choice-close', true).first()
11895         
11896         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11897         
11898         this.item.push(o);
11899         
11900         this.lastData = o;
11901         
11902         this.syncValue();
11903         
11904         this.inputEl().dom.value = '';
11905         
11906     },
11907     
11908     onRemoveItem : function(e, _self, o)
11909     {
11910         e.preventDefault();
11911         var index = this.item.indexOf(o.data) * 1;
11912         
11913         if( index < 0){
11914             Roo.log('not this item?!');
11915             return;
11916         }
11917         
11918         this.item.splice(index, 1);
11919         o.item.remove();
11920         
11921         this.syncValue();
11922         
11923         this.fireEvent('remove', this, e);
11924         
11925     },
11926     
11927     syncValue : function()
11928     {
11929         if(!this.item.length){
11930             this.clearValue();
11931             return;
11932         }
11933             
11934         var value = [];
11935         var _this = this;
11936         Roo.each(this.item, function(i){
11937             if(_this.valueField){
11938                 value.push(i[_this.valueField]);
11939                 return;
11940             }
11941
11942             value.push(i);
11943         });
11944
11945         this.value = value.join(',');
11946
11947         if(this.hiddenField){
11948             this.hiddenField.dom.value = this.value;
11949         }
11950     },
11951     
11952     clearItem : function()
11953     {
11954         if(!this.multiple){
11955             return;
11956         }
11957         
11958         this.item = [];
11959         
11960         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11961            c.remove();
11962         });
11963         
11964         this.syncValue();
11965     },
11966     
11967     inputEl: function ()
11968     {
11969         if(this.tickable){
11970             return this.searchField;
11971         }
11972         return this.el.select('input.form-control',true).first();
11973     },
11974     
11975     
11976     onTickableFooterButtonClick : function(e, btn, el)
11977     {
11978         e.preventDefault();
11979         
11980         if(btn && btn.name == 'cancel'){
11981             this.tickItems = Roo.apply([], this.item);
11982             this.collapse();
11983             return;
11984         }
11985         
11986         this.clearItem();
11987         
11988         var _this = this;
11989         
11990         Roo.each(this.tickItems, function(o){
11991             _this.addItem(o);
11992         });
11993         
11994         this.collapse();
11995         
11996     }
11997     
11998     
11999
12000     /** 
12001     * @cfg {Boolean} grow 
12002     * @hide 
12003     */
12004     /** 
12005     * @cfg {Number} growMin 
12006     * @hide 
12007     */
12008     /** 
12009     * @cfg {Number} growMax 
12010     * @hide 
12011     */
12012     /**
12013      * @hide
12014      * @method autoSize
12015      */
12016 });
12017 /*
12018  * Based on:
12019  * Ext JS Library 1.1.1
12020  * Copyright(c) 2006-2007, Ext JS, LLC.
12021  *
12022  * Originally Released Under LGPL - original licence link has changed is not relivant.
12023  *
12024  * Fork - LGPL
12025  * <script type="text/javascript">
12026  */
12027
12028 /**
12029  * @class Roo.View
12030  * @extends Roo.util.Observable
12031  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12032  * This class also supports single and multi selection modes. <br>
12033  * Create a data model bound view:
12034  <pre><code>
12035  var store = new Roo.data.Store(...);
12036
12037  var view = new Roo.View({
12038     el : "my-element",
12039     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12040  
12041     singleSelect: true,
12042     selectedClass: "ydataview-selected",
12043     store: store
12044  });
12045
12046  // listen for node click?
12047  view.on("click", function(vw, index, node, e){
12048  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12049  });
12050
12051  // load XML data
12052  dataModel.load("foobar.xml");
12053  </code></pre>
12054  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12055  * <br><br>
12056  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12057  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12058  * 
12059  * Note: old style constructor is still suported (container, template, config)
12060  * 
12061  * @constructor
12062  * Create a new View
12063  * @param {Object} config The config object
12064  * 
12065  */
12066 Roo.View = function(config, depreciated_tpl, depreciated_config){
12067     
12068     this.parent = false;
12069     
12070     if (typeof(depreciated_tpl) == 'undefined') {
12071         // new way.. - universal constructor.
12072         Roo.apply(this, config);
12073         this.el  = Roo.get(this.el);
12074     } else {
12075         // old format..
12076         this.el  = Roo.get(config);
12077         this.tpl = depreciated_tpl;
12078         Roo.apply(this, depreciated_config);
12079     }
12080     this.wrapEl  = this.el.wrap().wrap();
12081     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12082     
12083     
12084     if(typeof(this.tpl) == "string"){
12085         this.tpl = new Roo.Template(this.tpl);
12086     } else {
12087         // support xtype ctors..
12088         this.tpl = new Roo.factory(this.tpl, Roo);
12089     }
12090     
12091     
12092     this.tpl.compile();
12093     
12094     /** @private */
12095     this.addEvents({
12096         /**
12097          * @event beforeclick
12098          * Fires before a click is processed. Returns false to cancel the default action.
12099          * @param {Roo.View} this
12100          * @param {Number} index The index of the target node
12101          * @param {HTMLElement} node The target node
12102          * @param {Roo.EventObject} e The raw event object
12103          */
12104             "beforeclick" : true,
12105         /**
12106          * @event click
12107          * Fires when a template node is clicked.
12108          * @param {Roo.View} this
12109          * @param {Number} index The index of the target node
12110          * @param {HTMLElement} node The target node
12111          * @param {Roo.EventObject} e The raw event object
12112          */
12113             "click" : true,
12114         /**
12115          * @event dblclick
12116          * Fires when a template node is double clicked.
12117          * @param {Roo.View} this
12118          * @param {Number} index The index of the target node
12119          * @param {HTMLElement} node The target node
12120          * @param {Roo.EventObject} e The raw event object
12121          */
12122             "dblclick" : true,
12123         /**
12124          * @event contextmenu
12125          * Fires when a template node is right clicked.
12126          * @param {Roo.View} this
12127          * @param {Number} index The index of the target node
12128          * @param {HTMLElement} node The target node
12129          * @param {Roo.EventObject} e The raw event object
12130          */
12131             "contextmenu" : true,
12132         /**
12133          * @event selectionchange
12134          * Fires when the selected nodes change.
12135          * @param {Roo.View} this
12136          * @param {Array} selections Array of the selected nodes
12137          */
12138             "selectionchange" : true,
12139     
12140         /**
12141          * @event beforeselect
12142          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12143          * @param {Roo.View} this
12144          * @param {HTMLElement} node The node to be selected
12145          * @param {Array} selections Array of currently selected nodes
12146          */
12147             "beforeselect" : true,
12148         /**
12149          * @event preparedata
12150          * Fires on every row to render, to allow you to change the data.
12151          * @param {Roo.View} this
12152          * @param {Object} data to be rendered (change this)
12153          */
12154           "preparedata" : true
12155           
12156           
12157         });
12158
12159
12160
12161     this.el.on({
12162         "click": this.onClick,
12163         "dblclick": this.onDblClick,
12164         "contextmenu": this.onContextMenu,
12165         scope:this
12166     });
12167
12168     this.selections = [];
12169     this.nodes = [];
12170     this.cmp = new Roo.CompositeElementLite([]);
12171     if(this.store){
12172         this.store = Roo.factory(this.store, Roo.data);
12173         this.setStore(this.store, true);
12174     }
12175     
12176     if ( this.footer && this.footer.xtype) {
12177            
12178          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12179         
12180         this.footer.dataSource = this.store
12181         this.footer.container = fctr;
12182         this.footer = Roo.factory(this.footer, Roo);
12183         fctr.insertFirst(this.el);
12184         
12185         // this is a bit insane - as the paging toolbar seems to detach the el..
12186 //        dom.parentNode.parentNode.parentNode
12187          // they get detached?
12188     }
12189     
12190     
12191     Roo.View.superclass.constructor.call(this);
12192     
12193     
12194 };
12195
12196 Roo.extend(Roo.View, Roo.util.Observable, {
12197     
12198      /**
12199      * @cfg {Roo.data.Store} store Data store to load data from.
12200      */
12201     store : false,
12202     
12203     /**
12204      * @cfg {String|Roo.Element} el The container element.
12205      */
12206     el : '',
12207     
12208     /**
12209      * @cfg {String|Roo.Template} tpl The template used by this View 
12210      */
12211     tpl : false,
12212     /**
12213      * @cfg {String} dataName the named area of the template to use as the data area
12214      *                          Works with domtemplates roo-name="name"
12215      */
12216     dataName: false,
12217     /**
12218      * @cfg {String} selectedClass The css class to add to selected nodes
12219      */
12220     selectedClass : "x-view-selected",
12221      /**
12222      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12223      */
12224     emptyText : "",
12225     
12226     /**
12227      * @cfg {String} text to display on mask (default Loading)
12228      */
12229     mask : false,
12230     /**
12231      * @cfg {Boolean} multiSelect Allow multiple selection
12232      */
12233     multiSelect : false,
12234     /**
12235      * @cfg {Boolean} singleSelect Allow single selection
12236      */
12237     singleSelect:  false,
12238     
12239     /**
12240      * @cfg {Boolean} toggleSelect - selecting 
12241      */
12242     toggleSelect : false,
12243     
12244     /**
12245      * @cfg {Boolean} tickable - selecting 
12246      */
12247     tickable : false,
12248     
12249     /**
12250      * Returns the element this view is bound to.
12251      * @return {Roo.Element}
12252      */
12253     getEl : function(){
12254         return this.wrapEl;
12255     },
12256     
12257     
12258
12259     /**
12260      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12261      */
12262     refresh : function(){
12263         Roo.log('refresh');
12264         var t = this.tpl;
12265         
12266         // if we are using something like 'domtemplate', then
12267         // the what gets used is:
12268         // t.applySubtemplate(NAME, data, wrapping data..)
12269         // the outer template then get' applied with
12270         //     the store 'extra data'
12271         // and the body get's added to the
12272         //      roo-name="data" node?
12273         //      <span class='roo-tpl-{name}'></span> ?????
12274         
12275         
12276         
12277         this.clearSelections();
12278         this.el.update("");
12279         var html = [];
12280         var records = this.store.getRange();
12281         if(records.length < 1) {
12282             
12283             // is this valid??  = should it render a template??
12284             
12285             this.el.update(this.emptyText);
12286             return;
12287         }
12288         var el = this.el;
12289         if (this.dataName) {
12290             this.el.update(t.apply(this.store.meta)); //????
12291             el = this.el.child('.roo-tpl-' + this.dataName);
12292         }
12293         
12294         for(var i = 0, len = records.length; i < len; i++){
12295             var data = this.prepareData(records[i].data, i, records[i]);
12296             this.fireEvent("preparedata", this, data, i, records[i]);
12297             
12298             var d = Roo.apply({}, data);
12299             
12300             if(this.tickable){
12301                 Roo.apply(d, {'roo-id' : Roo.id()});
12302                 
12303                 var _this = this;
12304             
12305                 Roo.each(this.parent.item, function(item){
12306                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12307                         return;
12308                     }
12309                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12310                 });
12311             }
12312             
12313             html[html.length] = Roo.util.Format.trim(
12314                 this.dataName ?
12315                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12316                     t.apply(d)
12317             );
12318         }
12319         
12320         
12321         
12322         el.update(html.join(""));
12323         this.nodes = el.dom.childNodes;
12324         this.updateIndexes(0);
12325     },
12326     
12327
12328     /**
12329      * Function to override to reformat the data that is sent to
12330      * the template for each node.
12331      * DEPRICATED - use the preparedata event handler.
12332      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12333      * a JSON object for an UpdateManager bound view).
12334      */
12335     prepareData : function(data, index, record)
12336     {
12337         this.fireEvent("preparedata", this, data, index, record);
12338         return data;
12339     },
12340
12341     onUpdate : function(ds, record){
12342          Roo.log('on update');   
12343         this.clearSelections();
12344         var index = this.store.indexOf(record);
12345         var n = this.nodes[index];
12346         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12347         n.parentNode.removeChild(n);
12348         this.updateIndexes(index, index);
12349     },
12350
12351     
12352     
12353 // --------- FIXME     
12354     onAdd : function(ds, records, index)
12355     {
12356         Roo.log(['on Add', ds, records, index] );        
12357         this.clearSelections();
12358         if(this.nodes.length == 0){
12359             this.refresh();
12360             return;
12361         }
12362         var n = this.nodes[index];
12363         for(var i = 0, len = records.length; i < len; i++){
12364             var d = this.prepareData(records[i].data, i, records[i]);
12365             if(n){
12366                 this.tpl.insertBefore(n, d);
12367             }else{
12368                 
12369                 this.tpl.append(this.el, d);
12370             }
12371         }
12372         this.updateIndexes(index);
12373     },
12374
12375     onRemove : function(ds, record, index){
12376         Roo.log('onRemove');
12377         this.clearSelections();
12378         var el = this.dataName  ?
12379             this.el.child('.roo-tpl-' + this.dataName) :
12380             this.el; 
12381         
12382         el.dom.removeChild(this.nodes[index]);
12383         this.updateIndexes(index);
12384     },
12385
12386     /**
12387      * Refresh an individual node.
12388      * @param {Number} index
12389      */
12390     refreshNode : function(index){
12391         this.onUpdate(this.store, this.store.getAt(index));
12392     },
12393
12394     updateIndexes : function(startIndex, endIndex){
12395         var ns = this.nodes;
12396         startIndex = startIndex || 0;
12397         endIndex = endIndex || ns.length - 1;
12398         for(var i = startIndex; i <= endIndex; i++){
12399             ns[i].nodeIndex = i;
12400         }
12401     },
12402
12403     /**
12404      * Changes the data store this view uses and refresh the view.
12405      * @param {Store} store
12406      */
12407     setStore : function(store, initial){
12408         if(!initial && this.store){
12409             this.store.un("datachanged", this.refresh);
12410             this.store.un("add", this.onAdd);
12411             this.store.un("remove", this.onRemove);
12412             this.store.un("update", this.onUpdate);
12413             this.store.un("clear", this.refresh);
12414             this.store.un("beforeload", this.onBeforeLoad);
12415             this.store.un("load", this.onLoad);
12416             this.store.un("loadexception", this.onLoad);
12417         }
12418         if(store){
12419           
12420             store.on("datachanged", this.refresh, this);
12421             store.on("add", this.onAdd, this);
12422             store.on("remove", this.onRemove, this);
12423             store.on("update", this.onUpdate, this);
12424             store.on("clear", this.refresh, this);
12425             store.on("beforeload", this.onBeforeLoad, this);
12426             store.on("load", this.onLoad, this);
12427             store.on("loadexception", this.onLoad, this);
12428         }
12429         
12430         if(store){
12431             this.refresh();
12432         }
12433     },
12434     /**
12435      * onbeforeLoad - masks the loading area.
12436      *
12437      */
12438     onBeforeLoad : function(store,opts)
12439     {
12440          Roo.log('onBeforeLoad');   
12441         if (!opts.add) {
12442             this.el.update("");
12443         }
12444         this.el.mask(this.mask ? this.mask : "Loading" ); 
12445     },
12446     onLoad : function ()
12447     {
12448         this.el.unmask();
12449     },
12450     
12451
12452     /**
12453      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12454      * @param {HTMLElement} node
12455      * @return {HTMLElement} The template node
12456      */
12457     findItemFromChild : function(node){
12458         var el = this.dataName  ?
12459             this.el.child('.roo-tpl-' + this.dataName,true) :
12460             this.el.dom; 
12461         
12462         if(!node || node.parentNode == el){
12463                     return node;
12464             }
12465             var p = node.parentNode;
12466             while(p && p != el){
12467             if(p.parentNode == el){
12468                 return p;
12469             }
12470             p = p.parentNode;
12471         }
12472             return null;
12473     },
12474
12475     /** @ignore */
12476     onClick : function(e){
12477         var item = this.findItemFromChild(e.getTarget());
12478         if(item){
12479             var index = this.indexOf(item);
12480             if(this.onItemClick(item, index, e) !== false){
12481                 this.fireEvent("click", this, index, item, e);
12482             }
12483         }else{
12484             this.clearSelections();
12485         }
12486     },
12487
12488     /** @ignore */
12489     onContextMenu : function(e){
12490         var item = this.findItemFromChild(e.getTarget());
12491         if(item){
12492             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12493         }
12494     },
12495
12496     /** @ignore */
12497     onDblClick : function(e){
12498         var item = this.findItemFromChild(e.getTarget());
12499         if(item){
12500             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12501         }
12502     },
12503
12504     onItemClick : function(item, index, e)
12505     {
12506         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12507             return false;
12508         }
12509         if (this.toggleSelect) {
12510             var m = this.isSelected(item) ? 'unselect' : 'select';
12511             Roo.log(m);
12512             var _t = this;
12513             _t[m](item, true, false);
12514             return true;
12515         }
12516         if(this.multiSelect || this.singleSelect){
12517             if(this.multiSelect && e.shiftKey && this.lastSelection){
12518                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12519             }else{
12520                 this.select(item, this.multiSelect && e.ctrlKey);
12521                 this.lastSelection = item;
12522             }
12523             
12524             if(!this.tickable){
12525                 e.preventDefault();
12526             }
12527             
12528         }
12529         return true;
12530     },
12531
12532     /**
12533      * Get the number of selected nodes.
12534      * @return {Number}
12535      */
12536     getSelectionCount : function(){
12537         return this.selections.length;
12538     },
12539
12540     /**
12541      * Get the currently selected nodes.
12542      * @return {Array} An array of HTMLElements
12543      */
12544     getSelectedNodes : function(){
12545         return this.selections;
12546     },
12547
12548     /**
12549      * Get the indexes of the selected nodes.
12550      * @return {Array}
12551      */
12552     getSelectedIndexes : function(){
12553         var indexes = [], s = this.selections;
12554         for(var i = 0, len = s.length; i < len; i++){
12555             indexes.push(s[i].nodeIndex);
12556         }
12557         return indexes;
12558     },
12559
12560     /**
12561      * Clear all selections
12562      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12563      */
12564     clearSelections : function(suppressEvent){
12565         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12566             this.cmp.elements = this.selections;
12567             this.cmp.removeClass(this.selectedClass);
12568             this.selections = [];
12569             if(!suppressEvent){
12570                 this.fireEvent("selectionchange", this, this.selections);
12571             }
12572         }
12573     },
12574
12575     /**
12576      * Returns true if the passed node is selected
12577      * @param {HTMLElement/Number} node The node or node index
12578      * @return {Boolean}
12579      */
12580     isSelected : function(node){
12581         var s = this.selections;
12582         if(s.length < 1){
12583             return false;
12584         }
12585         node = this.getNode(node);
12586         return s.indexOf(node) !== -1;
12587     },
12588
12589     /**
12590      * Selects 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 to keep existing selections
12593      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12594      */
12595     select : function(nodeInfo, keepExisting, suppressEvent){
12596         if(nodeInfo instanceof Array){
12597             if(!keepExisting){
12598                 this.clearSelections(true);
12599             }
12600             for(var i = 0, len = nodeInfo.length; i < len; i++){
12601                 this.select(nodeInfo[i], true, true);
12602             }
12603             return;
12604         } 
12605         var node = this.getNode(nodeInfo);
12606         if(!node || this.isSelected(node)){
12607             return; // already selected.
12608         }
12609         if(!keepExisting){
12610             this.clearSelections(true);
12611         }
12612         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12613             Roo.fly(node).addClass(this.selectedClass);
12614             this.selections.push(node);
12615             if(!suppressEvent){
12616                 this.fireEvent("selectionchange", this, this.selections);
12617             }
12618         }
12619         
12620         
12621     },
12622       /**
12623      * Unselects nodes.
12624      * @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
12625      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12626      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12627      */
12628     unselect : function(nodeInfo, keepExisting, suppressEvent)
12629     {
12630         if(nodeInfo instanceof Array){
12631             Roo.each(this.selections, function(s) {
12632                 this.unselect(s, nodeInfo);
12633             }, this);
12634             return;
12635         }
12636         var node = this.getNode(nodeInfo);
12637         if(!node || !this.isSelected(node)){
12638             Roo.log("not selected");
12639             return; // not selected.
12640         }
12641         // fireevent???
12642         var ns = [];
12643         Roo.each(this.selections, function(s) {
12644             if (s == node ) {
12645                 Roo.fly(node).removeClass(this.selectedClass);
12646
12647                 return;
12648             }
12649             ns.push(s);
12650         },this);
12651         
12652         this.selections= ns;
12653         this.fireEvent("selectionchange", this, this.selections);
12654     },
12655
12656     /**
12657      * Gets a template node.
12658      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12659      * @return {HTMLElement} The node or null if it wasn't found
12660      */
12661     getNode : function(nodeInfo){
12662         if(typeof nodeInfo == "string"){
12663             return document.getElementById(nodeInfo);
12664         }else if(typeof nodeInfo == "number"){
12665             return this.nodes[nodeInfo];
12666         }
12667         return nodeInfo;
12668     },
12669
12670     /**
12671      * Gets a range template nodes.
12672      * @param {Number} startIndex
12673      * @param {Number} endIndex
12674      * @return {Array} An array of nodes
12675      */
12676     getNodes : function(start, end){
12677         var ns = this.nodes;
12678         start = start || 0;
12679         end = typeof end == "undefined" ? ns.length - 1 : end;
12680         var nodes = [];
12681         if(start <= end){
12682             for(var i = start; i <= end; i++){
12683                 nodes.push(ns[i]);
12684             }
12685         } else{
12686             for(var i = start; i >= end; i--){
12687                 nodes.push(ns[i]);
12688             }
12689         }
12690         return nodes;
12691     },
12692
12693     /**
12694      * Finds the index of the passed node
12695      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12696      * @return {Number} The index of the node or -1
12697      */
12698     indexOf : function(node){
12699         node = this.getNode(node);
12700         if(typeof node.nodeIndex == "number"){
12701             return node.nodeIndex;
12702         }
12703         var ns = this.nodes;
12704         for(var i = 0, len = ns.length; i < len; i++){
12705             if(ns[i] == node){
12706                 return i;
12707             }
12708         }
12709         return -1;
12710     }
12711 });
12712 /*
12713  * - LGPL
12714  *
12715  * based on jquery fullcalendar
12716  * 
12717  */
12718
12719 Roo.bootstrap = Roo.bootstrap || {};
12720 /**
12721  * @class Roo.bootstrap.Calendar
12722  * @extends Roo.bootstrap.Component
12723  * Bootstrap Calendar class
12724  * @cfg {Boolean} loadMask (true|false) default false
12725  * @cfg {Object} header generate the user specific header of the calendar, default false
12726
12727  * @constructor
12728  * Create a new Container
12729  * @param {Object} config The config object
12730  */
12731
12732
12733
12734 Roo.bootstrap.Calendar = function(config){
12735     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12736      this.addEvents({
12737         /**
12738              * @event select
12739              * Fires when a date is selected
12740              * @param {DatePicker} this
12741              * @param {Date} date The selected date
12742              */
12743         'select': true,
12744         /**
12745              * @event monthchange
12746              * Fires when the displayed month changes 
12747              * @param {DatePicker} this
12748              * @param {Date} date The selected month
12749              */
12750         'monthchange': true,
12751         /**
12752              * @event evententer
12753              * Fires when mouse over an event
12754              * @param {Calendar} this
12755              * @param {event} Event
12756              */
12757         'evententer': true,
12758         /**
12759              * @event eventleave
12760              * Fires when the mouse leaves an
12761              * @param {Calendar} this
12762              * @param {event}
12763              */
12764         'eventleave': true,
12765         /**
12766              * @event eventclick
12767              * Fires when the mouse click an
12768              * @param {Calendar} this
12769              * @param {event}
12770              */
12771         'eventclick': true
12772         
12773     });
12774
12775 };
12776
12777 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12778     
12779      /**
12780      * @cfg {Number} startDay
12781      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12782      */
12783     startDay : 0,
12784     
12785     loadMask : false,
12786     
12787     header : false,
12788       
12789     getAutoCreate : function(){
12790         
12791         
12792         var fc_button = function(name, corner, style, content ) {
12793             return Roo.apply({},{
12794                 tag : 'span',
12795                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12796                          (corner.length ?
12797                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12798                             ''
12799                         ),
12800                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12801                 unselectable: 'on'
12802             });
12803         };
12804         
12805         var header = {};
12806         
12807         if(!this.header){
12808             header = {
12809                 tag : 'table',
12810                 cls : 'fc-header',
12811                 style : 'width:100%',
12812                 cn : [
12813                     {
12814                         tag: 'tr',
12815                         cn : [
12816                             {
12817                                 tag : 'td',
12818                                 cls : 'fc-header-left',
12819                                 cn : [
12820                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12821                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12822                                     { tag: 'span', cls: 'fc-header-space' },
12823                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12824
12825
12826                                 ]
12827                             },
12828
12829                             {
12830                                 tag : 'td',
12831                                 cls : 'fc-header-center',
12832                                 cn : [
12833                                     {
12834                                         tag: 'span',
12835                                         cls: 'fc-header-title',
12836                                         cn : {
12837                                             tag: 'H2',
12838                                             html : 'month / year'
12839                                         }
12840                                     }
12841
12842                                 ]
12843                             },
12844                             {
12845                                 tag : 'td',
12846                                 cls : 'fc-header-right',
12847                                 cn : [
12848                               /*      fc_button('month', 'left', '', 'month' ),
12849                                     fc_button('week', '', '', 'week' ),
12850                                     fc_button('day', 'right', '', 'day' )
12851                                 */    
12852
12853                                 ]
12854                             }
12855
12856                         ]
12857                     }
12858                 ]
12859             };
12860         }
12861         
12862         header = this.header;
12863         
12864        
12865         var cal_heads = function() {
12866             var ret = [];
12867             // fixme - handle this.
12868             
12869             for (var i =0; i < Date.dayNames.length; i++) {
12870                 var d = Date.dayNames[i];
12871                 ret.push({
12872                     tag: 'th',
12873                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12874                     html : d.substring(0,3)
12875                 });
12876                 
12877             }
12878             ret[0].cls += ' fc-first';
12879             ret[6].cls += ' fc-last';
12880             return ret;
12881         };
12882         var cal_cell = function(n) {
12883             return  {
12884                 tag: 'td',
12885                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12886                 cn : [
12887                     {
12888                         cn : [
12889                             {
12890                                 cls: 'fc-day-number',
12891                                 html: 'D'
12892                             },
12893                             {
12894                                 cls: 'fc-day-content',
12895                              
12896                                 cn : [
12897                                      {
12898                                         style: 'position: relative;' // height: 17px;
12899                                     }
12900                                 ]
12901                             }
12902                             
12903                             
12904                         ]
12905                     }
12906                 ]
12907                 
12908             }
12909         };
12910         var cal_rows = function() {
12911             
12912             var ret = []
12913             for (var r = 0; r < 6; r++) {
12914                 var row= {
12915                     tag : 'tr',
12916                     cls : 'fc-week',
12917                     cn : []
12918                 };
12919                 
12920                 for (var i =0; i < Date.dayNames.length; i++) {
12921                     var d = Date.dayNames[i];
12922                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12923
12924                 }
12925                 row.cn[0].cls+=' fc-first';
12926                 row.cn[0].cn[0].style = 'min-height:90px';
12927                 row.cn[6].cls+=' fc-last';
12928                 ret.push(row);
12929                 
12930             }
12931             ret[0].cls += ' fc-first';
12932             ret[4].cls += ' fc-prev-last';
12933             ret[5].cls += ' fc-last';
12934             return ret;
12935             
12936         };
12937         
12938         var cal_table = {
12939             tag: 'table',
12940             cls: 'fc-border-separate',
12941             style : 'width:100%',
12942             cellspacing  : 0,
12943             cn : [
12944                 { 
12945                     tag: 'thead',
12946                     cn : [
12947                         { 
12948                             tag: 'tr',
12949                             cls : 'fc-first fc-last',
12950                             cn : cal_heads()
12951                         }
12952                     ]
12953                 },
12954                 { 
12955                     tag: 'tbody',
12956                     cn : cal_rows()
12957                 }
12958                   
12959             ]
12960         };
12961          
12962          var cfg = {
12963             cls : 'fc fc-ltr',
12964             cn : [
12965                 header,
12966                 {
12967                     cls : 'fc-content',
12968                     style : "position: relative;",
12969                     cn : [
12970                         {
12971                             cls : 'fc-view fc-view-month fc-grid',
12972                             style : 'position: relative',
12973                             unselectable : 'on',
12974                             cn : [
12975                                 {
12976                                     cls : 'fc-event-container',
12977                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12978                                 },
12979                                 cal_table
12980                             ]
12981                         }
12982                     ]
12983     
12984                 }
12985            ] 
12986             
12987         };
12988         
12989          
12990         
12991         return cfg;
12992     },
12993     
12994     
12995     initEvents : function()
12996     {
12997         if(!this.store){
12998             throw "can not find store for calendar";
12999         }
13000         
13001         var mark = {
13002             tag: "div",
13003             cls:"x-dlg-mask",
13004             style: "text-align:center",
13005             cn: [
13006                 {
13007                     tag: "div",
13008                     style: "background-color:white;width:50%;margin:250 auto",
13009                     cn: [
13010                         {
13011                             tag: "img",
13012                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13013                         },
13014                         {
13015                             tag: "span",
13016                             html: "Loading"
13017                         }
13018                         
13019                     ]
13020                 }
13021             ]
13022         }
13023         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13024         
13025         var size = this.el.select('.fc-content', true).first().getSize();
13026         this.maskEl.setSize(size.width, size.height);
13027         this.maskEl.enableDisplayMode("block");
13028         if(!this.loadMask){
13029             this.maskEl.hide();
13030         }
13031         
13032         this.store = Roo.factory(this.store, Roo.data);
13033         this.store.on('load', this.onLoad, this);
13034         this.store.on('beforeload', this.onBeforeLoad, this);
13035         
13036         this.resize();
13037         
13038         this.cells = this.el.select('.fc-day',true);
13039         //Roo.log(this.cells);
13040         this.textNodes = this.el.query('.fc-day-number');
13041         this.cells.addClassOnOver('fc-state-hover');
13042         
13043         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13044         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13045         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13046         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13047         
13048         this.on('monthchange', this.onMonthChange, this);
13049         
13050         this.update(new Date().clearTime());
13051     },
13052     
13053     resize : function() {
13054         var sz  = this.el.getSize();
13055         
13056         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13057         this.el.select('.fc-day-content div',true).setHeight(34);
13058     },
13059     
13060     
13061     // private
13062     showPrevMonth : function(e){
13063         this.update(this.activeDate.add("mo", -1));
13064     },
13065     showToday : function(e){
13066         this.update(new Date().clearTime());
13067     },
13068     // private
13069     showNextMonth : function(e){
13070         this.update(this.activeDate.add("mo", 1));
13071     },
13072
13073     // private
13074     showPrevYear : function(){
13075         this.update(this.activeDate.add("y", -1));
13076     },
13077
13078     // private
13079     showNextYear : function(){
13080         this.update(this.activeDate.add("y", 1));
13081     },
13082
13083     
13084    // private
13085     update : function(date)
13086     {
13087         var vd = this.activeDate;
13088         this.activeDate = date;
13089 //        if(vd && this.el){
13090 //            var t = date.getTime();
13091 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13092 //                Roo.log('using add remove');
13093 //                
13094 //                this.fireEvent('monthchange', this, date);
13095 //                
13096 //                this.cells.removeClass("fc-state-highlight");
13097 //                this.cells.each(function(c){
13098 //                   if(c.dateValue == t){
13099 //                       c.addClass("fc-state-highlight");
13100 //                       setTimeout(function(){
13101 //                            try{c.dom.firstChild.focus();}catch(e){}
13102 //                       }, 50);
13103 //                       return false;
13104 //                   }
13105 //                   return true;
13106 //                });
13107 //                return;
13108 //            }
13109 //        }
13110         
13111         var days = date.getDaysInMonth();
13112         
13113         var firstOfMonth = date.getFirstDateOfMonth();
13114         var startingPos = firstOfMonth.getDay()-this.startDay;
13115         
13116         if(startingPos < this.startDay){
13117             startingPos += 7;
13118         }
13119         
13120         var pm = date.add(Date.MONTH, -1);
13121         var prevStart = pm.getDaysInMonth()-startingPos;
13122 //        
13123         this.cells = this.el.select('.fc-day',true);
13124         this.textNodes = this.el.query('.fc-day-number');
13125         this.cells.addClassOnOver('fc-state-hover');
13126         
13127         var cells = this.cells.elements;
13128         var textEls = this.textNodes;
13129         
13130         Roo.each(cells, function(cell){
13131             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13132         });
13133         
13134         days += startingPos;
13135
13136         // convert everything to numbers so it's fast
13137         var day = 86400000;
13138         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13139         //Roo.log(d);
13140         //Roo.log(pm);
13141         //Roo.log(prevStart);
13142         
13143         var today = new Date().clearTime().getTime();
13144         var sel = date.clearTime().getTime();
13145         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13146         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13147         var ddMatch = this.disabledDatesRE;
13148         var ddText = this.disabledDatesText;
13149         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13150         var ddaysText = this.disabledDaysText;
13151         var format = this.format;
13152         
13153         var setCellClass = function(cal, cell){
13154             cell.row = 0;
13155             cell.events = [];
13156             cell.more = [];
13157             //Roo.log('set Cell Class');
13158             cell.title = "";
13159             var t = d.getTime();
13160             
13161             //Roo.log(d);
13162             
13163             cell.dateValue = t;
13164             if(t == today){
13165                 cell.className += " fc-today";
13166                 cell.className += " fc-state-highlight";
13167                 cell.title = cal.todayText;
13168             }
13169             if(t == sel){
13170                 // disable highlight in other month..
13171                 //cell.className += " fc-state-highlight";
13172                 
13173             }
13174             // disabling
13175             if(t < min) {
13176                 cell.className = " fc-state-disabled";
13177                 cell.title = cal.minText;
13178                 return;
13179             }
13180             if(t > max) {
13181                 cell.className = " fc-state-disabled";
13182                 cell.title = cal.maxText;
13183                 return;
13184             }
13185             if(ddays){
13186                 if(ddays.indexOf(d.getDay()) != -1){
13187                     cell.title = ddaysText;
13188                     cell.className = " fc-state-disabled";
13189                 }
13190             }
13191             if(ddMatch && format){
13192                 var fvalue = d.dateFormat(format);
13193                 if(ddMatch.test(fvalue)){
13194                     cell.title = ddText.replace("%0", fvalue);
13195                     cell.className = " fc-state-disabled";
13196                 }
13197             }
13198             
13199             if (!cell.initialClassName) {
13200                 cell.initialClassName = cell.dom.className;
13201             }
13202             
13203             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13204         };
13205
13206         var i = 0;
13207         
13208         for(; i < startingPos; i++) {
13209             textEls[i].innerHTML = (++prevStart);
13210             d.setDate(d.getDate()+1);
13211             
13212             cells[i].className = "fc-past fc-other-month";
13213             setCellClass(this, cells[i]);
13214         }
13215         
13216         var intDay = 0;
13217         
13218         for(; i < days; i++){
13219             intDay = i - startingPos + 1;
13220             textEls[i].innerHTML = (intDay);
13221             d.setDate(d.getDate()+1);
13222             
13223             cells[i].className = ''; // "x-date-active";
13224             setCellClass(this, cells[i]);
13225         }
13226         var extraDays = 0;
13227         
13228         for(; i < 42; i++) {
13229             textEls[i].innerHTML = (++extraDays);
13230             d.setDate(d.getDate()+1);
13231             
13232             cells[i].className = "fc-future fc-other-month";
13233             setCellClass(this, cells[i]);
13234         }
13235         
13236         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13237         
13238         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13239         
13240         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13241         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13242         
13243         if(totalRows != 6){
13244             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13245             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13246         }
13247         
13248         this.fireEvent('monthchange', this, date);
13249         
13250         
13251         /*
13252         if(!this.internalRender){
13253             var main = this.el.dom.firstChild;
13254             var w = main.offsetWidth;
13255             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13256             Roo.fly(main).setWidth(w);
13257             this.internalRender = true;
13258             // opera does not respect the auto grow header center column
13259             // then, after it gets a width opera refuses to recalculate
13260             // without a second pass
13261             if(Roo.isOpera && !this.secondPass){
13262                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13263                 this.secondPass = true;
13264                 this.update.defer(10, this, [date]);
13265             }
13266         }
13267         */
13268         
13269     },
13270     
13271     findCell : function(dt) {
13272         dt = dt.clearTime().getTime();
13273         var ret = false;
13274         this.cells.each(function(c){
13275             //Roo.log("check " +c.dateValue + '?=' + dt);
13276             if(c.dateValue == dt){
13277                 ret = c;
13278                 return false;
13279             }
13280             return true;
13281         });
13282         
13283         return ret;
13284     },
13285     
13286     findCells : function(ev) {
13287         var s = ev.start.clone().clearTime().getTime();
13288        // Roo.log(s);
13289         var e= ev.end.clone().clearTime().getTime();
13290        // Roo.log(e);
13291         var ret = [];
13292         this.cells.each(function(c){
13293              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13294             
13295             if(c.dateValue > e){
13296                 return ;
13297             }
13298             if(c.dateValue < s){
13299                 return ;
13300             }
13301             ret.push(c);
13302         });
13303         
13304         return ret;    
13305     },
13306     
13307 //    findBestRow: function(cells)
13308 //    {
13309 //        var ret = 0;
13310 //        
13311 //        for (var i =0 ; i < cells.length;i++) {
13312 //            ret  = Math.max(cells[i].rows || 0,ret);
13313 //        }
13314 //        return ret;
13315 //        
13316 //    },
13317     
13318     
13319     addItem : function(ev)
13320     {
13321         // look for vertical location slot in
13322         var cells = this.findCells(ev);
13323         
13324 //        ev.row = this.findBestRow(cells);
13325         
13326         // work out the location.
13327         
13328         var crow = false;
13329         var rows = [];
13330         for(var i =0; i < cells.length; i++) {
13331             
13332             cells[i].row = cells[0].row;
13333             
13334             if(i == 0){
13335                 cells[i].row = cells[i].row + 1;
13336             }
13337             
13338             if (!crow) {
13339                 crow = {
13340                     start : cells[i],
13341                     end :  cells[i]
13342                 };
13343                 continue;
13344             }
13345             if (crow.start.getY() == cells[i].getY()) {
13346                 // on same row.
13347                 crow.end = cells[i];
13348                 continue;
13349             }
13350             // different row.
13351             rows.push(crow);
13352             crow = {
13353                 start: cells[i],
13354                 end : cells[i]
13355             };
13356             
13357         }
13358         
13359         rows.push(crow);
13360         ev.els = [];
13361         ev.rows = rows;
13362         ev.cells = cells;
13363         
13364         cells[0].events.push(ev);
13365         
13366         this.calevents.push(ev);
13367     },
13368     
13369     clearEvents: function() {
13370         
13371         if(!this.calevents){
13372             return;
13373         }
13374         
13375         Roo.each(this.cells.elements, function(c){
13376             c.row = 0;
13377             c.events = [];
13378             c.more = [];
13379         });
13380         
13381         Roo.each(this.calevents, function(e) {
13382             Roo.each(e.els, function(el) {
13383                 el.un('mouseenter' ,this.onEventEnter, this);
13384                 el.un('mouseleave' ,this.onEventLeave, this);
13385                 el.remove();
13386             },this);
13387         },this);
13388         
13389         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13390             e.remove();
13391         });
13392         
13393     },
13394     
13395     renderEvents: function()
13396     {   
13397         var _this = this;
13398         
13399         this.cells.each(function(c) {
13400             
13401             if(c.row < 5){
13402                 return;
13403             }
13404             
13405             var ev = c.events;
13406             
13407             var r = 4;
13408             if(c.row != c.events.length){
13409                 r = 4 - (4 - (c.row - c.events.length));
13410             }
13411             
13412             c.events = ev.slice(0, r);
13413             c.more = ev.slice(r);
13414             
13415             if(c.more.length && c.more.length == 1){
13416                 c.events.push(c.more.pop());
13417             }
13418             
13419             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13420             
13421         });
13422             
13423         this.cells.each(function(c) {
13424             
13425             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13426             
13427             
13428             for (var e = 0; e < c.events.length; e++){
13429                 var ev = c.events[e];
13430                 var rows = ev.rows;
13431                 
13432                 for(var i = 0; i < rows.length; i++) {
13433                 
13434                     // how many rows should it span..
13435
13436                     var  cfg = {
13437                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13438                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13439
13440                         unselectable : "on",
13441                         cn : [
13442                             {
13443                                 cls: 'fc-event-inner',
13444                                 cn : [
13445     //                                {
13446     //                                  tag:'span',
13447     //                                  cls: 'fc-event-time',
13448     //                                  html : cells.length > 1 ? '' : ev.time
13449     //                                },
13450                                     {
13451                                       tag:'span',
13452                                       cls: 'fc-event-title',
13453                                       html : String.format('{0}', ev.title)
13454                                     }
13455
13456
13457                                 ]
13458                             },
13459                             {
13460                                 cls: 'ui-resizable-handle ui-resizable-e',
13461                                 html : '&nbsp;&nbsp;&nbsp'
13462                             }
13463
13464                         ]
13465                     };
13466
13467                     if (i == 0) {
13468                         cfg.cls += ' fc-event-start';
13469                     }
13470                     if ((i+1) == rows.length) {
13471                         cfg.cls += ' fc-event-end';
13472                     }
13473
13474                     var ctr = _this.el.select('.fc-event-container',true).first();
13475                     var cg = ctr.createChild(cfg);
13476
13477                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13478                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13479
13480                     var r = (c.more.length) ? 1 : 0;
13481                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13482                     cg.setWidth(ebox.right - sbox.x -2);
13483
13484                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13485                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13486                     cg.on('click', _this.onEventClick, _this, ev);
13487
13488                     ev.els.push(cg);
13489                     
13490                 }
13491                 
13492             }
13493             
13494             
13495             if(c.more.length){
13496                 var  cfg = {
13497                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13498                     style : 'position: absolute',
13499                     unselectable : "on",
13500                     cn : [
13501                         {
13502                             cls: 'fc-event-inner',
13503                             cn : [
13504                                 {
13505                                   tag:'span',
13506                                   cls: 'fc-event-title',
13507                                   html : 'More'
13508                                 }
13509
13510
13511                             ]
13512                         },
13513                         {
13514                             cls: 'ui-resizable-handle ui-resizable-e',
13515                             html : '&nbsp;&nbsp;&nbsp'
13516                         }
13517
13518                     ]
13519                 };
13520
13521                 var ctr = _this.el.select('.fc-event-container',true).first();
13522                 var cg = ctr.createChild(cfg);
13523
13524                 var sbox = c.select('.fc-day-content',true).first().getBox();
13525                 var ebox = c.select('.fc-day-content',true).first().getBox();
13526                 //Roo.log(cg);
13527                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13528                 cg.setWidth(ebox.right - sbox.x -2);
13529
13530                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13531                 
13532             }
13533             
13534         });
13535         
13536         
13537         
13538     },
13539     
13540     onEventEnter: function (e, el,event,d) {
13541         this.fireEvent('evententer', this, el, event);
13542     },
13543     
13544     onEventLeave: function (e, el,event,d) {
13545         this.fireEvent('eventleave', this, el, event);
13546     },
13547     
13548     onEventClick: function (e, el,event,d) {
13549         this.fireEvent('eventclick', this, el, event);
13550     },
13551     
13552     onMonthChange: function () {
13553         this.store.load();
13554     },
13555     
13556     onMoreEventClick: function(e, el, more)
13557     {
13558         var _this = this;
13559         
13560         this.calpopover.placement = 'right';
13561         this.calpopover.setTitle('More');
13562         
13563         this.calpopover.setContent('');
13564         
13565         var ctr = this.calpopover.el.select('.popover-content', true).first();
13566         
13567         Roo.each(more, function(m){
13568             var cfg = {
13569                 cls : 'fc-event-hori fc-event-draggable',
13570                 html : m.title
13571             }
13572             var cg = ctr.createChild(cfg);
13573             
13574             cg.on('click', _this.onEventClick, _this, m);
13575         });
13576         
13577         this.calpopover.show(el);
13578         
13579         
13580     },
13581     
13582     onLoad: function () 
13583     {   
13584         this.calevents = [];
13585         var cal = this;
13586         
13587         if(this.store.getCount() > 0){
13588             this.store.data.each(function(d){
13589                cal.addItem({
13590                     id : d.data.id,
13591                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13592                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13593                     time : d.data.start_time,
13594                     title : d.data.title,
13595                     description : d.data.description,
13596                     venue : d.data.venue
13597                 });
13598             });
13599         }
13600         
13601         this.renderEvents();
13602         
13603         if(this.calevents.length && this.loadMask){
13604             this.maskEl.hide();
13605         }
13606     },
13607     
13608     onBeforeLoad: function()
13609     {
13610         this.clearEvents();
13611         if(this.loadMask){
13612             this.maskEl.show();
13613         }
13614     }
13615 });
13616
13617  
13618  /*
13619  * - LGPL
13620  *
13621  * element
13622  * 
13623  */
13624
13625 /**
13626  * @class Roo.bootstrap.Popover
13627  * @extends Roo.bootstrap.Component
13628  * Bootstrap Popover class
13629  * @cfg {String} html contents of the popover   (or false to use children..)
13630  * @cfg {String} title of popover (or false to hide)
13631  * @cfg {String} placement how it is placed
13632  * @cfg {String} trigger click || hover (or false to trigger manually)
13633  * @cfg {String} over what (parent or false to trigger manually.)
13634  * 
13635  * @constructor
13636  * Create a new Popover
13637  * @param {Object} config The config object
13638  */
13639
13640 Roo.bootstrap.Popover = function(config){
13641     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13642 };
13643
13644 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13645     
13646     title: 'Fill in a title',
13647     html: false,
13648     
13649     placement : 'right',
13650     trigger : 'hover', // hover
13651     
13652     over: 'parent',
13653     
13654     can_build_overlaid : false,
13655     
13656     getChildContainer : function()
13657     {
13658         return this.el.select('.popover-content',true).first();
13659     },
13660     
13661     getAutoCreate : function(){
13662          Roo.log('make popover?');
13663         var cfg = {
13664            cls : 'popover roo-dynamic',
13665            style: 'display:block',
13666            cn : [
13667                 {
13668                     cls : 'arrow'
13669                 },
13670                 {
13671                     cls : 'popover-inner',
13672                     cn : [
13673                         {
13674                             tag: 'h3',
13675                             cls: 'popover-title',
13676                             html : this.title
13677                         },
13678                         {
13679                             cls : 'popover-content',
13680                             html : this.html
13681                         }
13682                     ]
13683                     
13684                 }
13685            ]
13686         };
13687         
13688         return cfg;
13689     },
13690     setTitle: function(str)
13691     {
13692         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13693     },
13694     setContent: function(str)
13695     {
13696         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13697     },
13698     // as it get's added to the bottom of the page.
13699     onRender : function(ct, position)
13700     {
13701         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13702         if(!this.el){
13703             var cfg = Roo.apply({},  this.getAutoCreate());
13704             cfg.id = Roo.id();
13705             
13706             if (this.cls) {
13707                 cfg.cls += ' ' + this.cls;
13708             }
13709             if (this.style) {
13710                 cfg.style = this.style;
13711             }
13712             Roo.log("adding to ")
13713             this.el = Roo.get(document.body).createChild(cfg, position);
13714             Roo.log(this.el);
13715         }
13716         this.initEvents();
13717     },
13718     
13719     initEvents : function()
13720     {
13721         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13722         this.el.enableDisplayMode('block');
13723         this.el.hide();
13724         if (this.over === false) {
13725             return; 
13726         }
13727         if (this.triggers === false) {
13728             return;
13729         }
13730         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13731         var triggers = this.trigger ? this.trigger.split(' ') : [];
13732         Roo.each(triggers, function(trigger) {
13733         
13734             if (trigger == 'click') {
13735                 on_el.on('click', this.toggle, this);
13736             } else if (trigger != 'manual') {
13737                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13738                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13739       
13740                 on_el.on(eventIn  ,this.enter, this);
13741                 on_el.on(eventOut, this.leave, this);
13742             }
13743         }, this);
13744         
13745     },
13746     
13747     
13748     // private
13749     timeout : null,
13750     hoverState : null,
13751     
13752     toggle : function () {
13753         this.hoverState == 'in' ? this.leave() : this.enter();
13754     },
13755     
13756     enter : function () {
13757        
13758     
13759         clearTimeout(this.timeout);
13760     
13761         this.hoverState = 'in'
13762     
13763         if (!this.delay || !this.delay.show) {
13764             this.show();
13765             return 
13766         }
13767         var _t = this;
13768         this.timeout = setTimeout(function () {
13769             if (_t.hoverState == 'in') {
13770                 _t.show();
13771             }
13772         }, this.delay.show)
13773     },
13774     leave : function() {
13775         clearTimeout(this.timeout);
13776     
13777         this.hoverState = 'out'
13778     
13779         if (!this.delay || !this.delay.hide) {
13780             this.hide();
13781             return 
13782         }
13783         var _t = this;
13784         this.timeout = setTimeout(function () {
13785             if (_t.hoverState == 'out') {
13786                 _t.hide();
13787             }
13788         }, this.delay.hide)
13789     },
13790     
13791     show : function (on_el)
13792     {
13793         if (!on_el) {
13794             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13795         }
13796         // set content.
13797         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13798         if (this.html !== false) {
13799             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13800         }
13801         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13802         if (!this.title.length) {
13803             this.el.select('.popover-title',true).hide();
13804         }
13805         
13806         var placement = typeof this.placement == 'function' ?
13807             this.placement.call(this, this.el, on_el) :
13808             this.placement;
13809             
13810         var autoToken = /\s?auto?\s?/i;
13811         var autoPlace = autoToken.test(placement);
13812         if (autoPlace) {
13813             placement = placement.replace(autoToken, '') || 'top';
13814         }
13815         
13816         //this.el.detach()
13817         //this.el.setXY([0,0]);
13818         this.el.show();
13819         this.el.dom.style.display='block';
13820         this.el.addClass(placement);
13821         
13822         //this.el.appendTo(on_el);
13823         
13824         var p = this.getPosition();
13825         var box = this.el.getBox();
13826         
13827         if (autoPlace) {
13828             // fixme..
13829         }
13830         var align = Roo.bootstrap.Popover.alignment[placement]
13831         this.el.alignTo(on_el, align[0],align[1]);
13832         //var arrow = this.el.select('.arrow',true).first();
13833         //arrow.set(align[2], 
13834         
13835         this.el.addClass('in');
13836         this.hoverState = null;
13837         
13838         if (this.el.hasClass('fade')) {
13839             // fade it?
13840         }
13841         
13842     },
13843     hide : function()
13844     {
13845         this.el.setXY([0,0]);
13846         this.el.removeClass('in');
13847         this.el.hide();
13848         
13849     }
13850     
13851 });
13852
13853 Roo.bootstrap.Popover.alignment = {
13854     'left' : ['r-l', [-10,0], 'right'],
13855     'right' : ['l-r', [10,0], 'left'],
13856     'bottom' : ['t-b', [0,10], 'top'],
13857     'top' : [ 'b-t', [0,-10], 'bottom']
13858 };
13859
13860  /*
13861  * - LGPL
13862  *
13863  * Progress
13864  * 
13865  */
13866
13867 /**
13868  * @class Roo.bootstrap.Progress
13869  * @extends Roo.bootstrap.Component
13870  * Bootstrap Progress class
13871  * @cfg {Boolean} striped striped of the progress bar
13872  * @cfg {Boolean} active animated of the progress bar
13873  * 
13874  * 
13875  * @constructor
13876  * Create a new Progress
13877  * @param {Object} config The config object
13878  */
13879
13880 Roo.bootstrap.Progress = function(config){
13881     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13882 };
13883
13884 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13885     
13886     striped : false,
13887     active: false,
13888     
13889     getAutoCreate : function(){
13890         var cfg = {
13891             tag: 'div',
13892             cls: 'progress'
13893         };
13894         
13895         
13896         if(this.striped){
13897             cfg.cls += ' progress-striped';
13898         }
13899       
13900         if(this.active){
13901             cfg.cls += ' active';
13902         }
13903         
13904         
13905         return cfg;
13906     }
13907    
13908 });
13909
13910  
13911
13912  /*
13913  * - LGPL
13914  *
13915  * ProgressBar
13916  * 
13917  */
13918
13919 /**
13920  * @class Roo.bootstrap.ProgressBar
13921  * @extends Roo.bootstrap.Component
13922  * Bootstrap ProgressBar class
13923  * @cfg {Number} aria_valuenow aria-value now
13924  * @cfg {Number} aria_valuemin aria-value min
13925  * @cfg {Number} aria_valuemax aria-value max
13926  * @cfg {String} label label for the progress bar
13927  * @cfg {String} panel (success | info | warning | danger )
13928  * @cfg {String} role role of the progress bar
13929  * @cfg {String} sr_only text
13930  * 
13931  * 
13932  * @constructor
13933  * Create a new ProgressBar
13934  * @param {Object} config The config object
13935  */
13936
13937 Roo.bootstrap.ProgressBar = function(config){
13938     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13939 };
13940
13941 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13942     
13943     aria_valuenow : 0,
13944     aria_valuemin : 0,
13945     aria_valuemax : 100,
13946     label : false,
13947     panel : false,
13948     role : false,
13949     sr_only: false,
13950     
13951     getAutoCreate : function()
13952     {
13953         
13954         var cfg = {
13955             tag: 'div',
13956             cls: 'progress-bar',
13957             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13958         };
13959         
13960         if(this.sr_only){
13961             cfg.cn = {
13962                 tag: 'span',
13963                 cls: 'sr-only',
13964                 html: this.sr_only
13965             }
13966         }
13967         
13968         if(this.role){
13969             cfg.role = this.role;
13970         }
13971         
13972         if(this.aria_valuenow){
13973             cfg['aria-valuenow'] = this.aria_valuenow;
13974         }
13975         
13976         if(this.aria_valuemin){
13977             cfg['aria-valuemin'] = this.aria_valuemin;
13978         }
13979         
13980         if(this.aria_valuemax){
13981             cfg['aria-valuemax'] = this.aria_valuemax;
13982         }
13983         
13984         if(this.label && !this.sr_only){
13985             cfg.html = this.label;
13986         }
13987         
13988         if(this.panel){
13989             cfg.cls += ' progress-bar-' + this.panel;
13990         }
13991         
13992         return cfg;
13993     },
13994     
13995     update : function(aria_valuenow)
13996     {
13997         this.aria_valuenow = aria_valuenow;
13998         
13999         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14000     }
14001    
14002 });
14003
14004  
14005
14006  /*
14007  * - LGPL
14008  *
14009  * column
14010  * 
14011  */
14012
14013 /**
14014  * @class Roo.bootstrap.TabGroup
14015  * @extends Roo.bootstrap.Column
14016  * Bootstrap Column class
14017  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14018  * @cfg {Boolean} carousel true to make the group behave like a carousel
14019  * 
14020  * @constructor
14021  * Create a new TabGroup
14022  * @param {Object} config The config object
14023  */
14024
14025 Roo.bootstrap.TabGroup = function(config){
14026     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14027     if (!this.navId) {
14028         this.navId = Roo.id();
14029     }
14030     this.tabs = [];
14031     Roo.bootstrap.TabGroup.register(this);
14032     
14033 };
14034
14035 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14036     
14037     carousel : false,
14038     transition : false,
14039      
14040     getAutoCreate : function()
14041     {
14042         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14043         
14044         cfg.cls += ' tab-content';
14045         
14046         if (this.carousel) {
14047             cfg.cls += ' carousel slide';
14048             cfg.cn = [{
14049                cls : 'carousel-inner'
14050             }]
14051         }
14052         
14053         
14054         return cfg;
14055     },
14056     getChildContainer : function()
14057     {
14058         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14059     },
14060     
14061     /**
14062     * register a Navigation item
14063     * @param {Roo.bootstrap.NavItem} the navitem to add
14064     */
14065     register : function(item)
14066     {
14067         this.tabs.push( item);
14068         item.navId = this.navId; // not really needed..
14069     
14070     },
14071     
14072     getActivePanel : function()
14073     {
14074         var r = false;
14075         Roo.each(this.tabs, function(t) {
14076             if (t.active) {
14077                 r = t;
14078                 return false;
14079             }
14080             return null;
14081         });
14082         return r;
14083         
14084     },
14085     getPanelByName : function(n)
14086     {
14087         var r = false;
14088         Roo.each(this.tabs, function(t) {
14089             if (t.tabId == n) {
14090                 r = t;
14091                 return false;
14092             }
14093             return null;
14094         });
14095         return r;
14096     },
14097     indexOfPanel : function(p)
14098     {
14099         var r = false;
14100         Roo.each(this.tabs, function(t,i) {
14101             if (t.tabId == p.tabId) {
14102                 r = i;
14103                 return false;
14104             }
14105             return null;
14106         });
14107         return r;
14108     },
14109     /**
14110      * show a specific panel
14111      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14112      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14113      */
14114     showPanel : function (pan)
14115     {
14116         
14117         if (typeof(pan) == 'number') {
14118             pan = this.tabs[pan];
14119         }
14120         if (typeof(pan) == 'string') {
14121             pan = this.getPanelByName(pan);
14122         }
14123         if (pan.tabId == this.getActivePanel().tabId) {
14124             return true;
14125         }
14126         var cur = this.getActivePanel();
14127         
14128         if (false === cur.fireEvent('beforedeactivate')) {
14129             return false;
14130         }
14131         
14132         if (this.carousel) {
14133             this.transition = true;
14134             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14135             var lr = dir == 'next' ? 'left' : 'right';
14136             pan.el.addClass(dir); // or prev
14137             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14138             cur.el.addClass(lr); // or right
14139             pan.el.addClass(lr);
14140             
14141             var _this = this;
14142             cur.el.on('transitionend', function() {
14143                 Roo.log("trans end?");
14144                 
14145                 pan.el.removeClass([lr,dir]);
14146                 pan.setActive(true);
14147                 
14148                 cur.el.removeClass([lr]);
14149                 cur.setActive(false);
14150                 
14151                 _this.transition = false;
14152                 
14153             }, this, { single:  true } );
14154             return true;
14155         }
14156         
14157         cur.setActive(false);
14158         pan.setActive(true);
14159         return true;
14160         
14161     },
14162     showPanelNext : function()
14163     {
14164         var i = this.indexOfPanel(this.getActivePanel());
14165         if (i > this.tabs.length) {
14166             return;
14167         }
14168         this.showPanel(this.tabs[i+1]);
14169     },
14170     showPanelPrev : function()
14171     {
14172         var i = this.indexOfPanel(this.getActivePanel());
14173         if (i  < 1) {
14174             return;
14175         }
14176         this.showPanel(this.tabs[i-1]);
14177     }
14178     
14179     
14180   
14181 });
14182
14183  
14184
14185  
14186  
14187 Roo.apply(Roo.bootstrap.TabGroup, {
14188     
14189     groups: {},
14190      /**
14191     * register a Navigation Group
14192     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14193     */
14194     register : function(navgrp)
14195     {
14196         this.groups[navgrp.navId] = navgrp;
14197         
14198     },
14199     /**
14200     * fetch a Navigation Group based on the navigation ID
14201     * if one does not exist , it will get created.
14202     * @param {string} the navgroup to add
14203     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14204     */
14205     get: function(navId) {
14206         if (typeof(this.groups[navId]) == 'undefined') {
14207             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14208         }
14209         return this.groups[navId] ;
14210     }
14211     
14212     
14213     
14214 });
14215
14216  /*
14217  * - LGPL
14218  *
14219  * TabPanel
14220  * 
14221  */
14222
14223 /**
14224  * @class Roo.bootstrap.TabPanel
14225  * @extends Roo.bootstrap.Component
14226  * Bootstrap TabPanel class
14227  * @cfg {Boolean} active panel active
14228  * @cfg {String} html panel content
14229  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14230  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14231  * 
14232  * 
14233  * @constructor
14234  * Create a new TabPanel
14235  * @param {Object} config The config object
14236  */
14237
14238 Roo.bootstrap.TabPanel = function(config){
14239     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14240     this.addEvents({
14241         /**
14242              * @event changed
14243              * Fires when the active status changes
14244              * @param {Roo.bootstrap.TabPanel} this
14245              * @param {Boolean} state the new state
14246             
14247          */
14248         'changed': true,
14249         /**
14250              * @event beforedeactivate
14251              * Fires before a tab is de-activated - can be used to do validation on a form.
14252              * @param {Roo.bootstrap.TabPanel} this
14253              * @return {Boolean} false if there is an error
14254             
14255          */
14256         'beforedeactivate': true
14257      });
14258     
14259     this.tabId = this.tabId || Roo.id();
14260   
14261 };
14262
14263 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14264     
14265     active: false,
14266     html: false,
14267     tabId: false,
14268     navId : false,
14269     
14270     getAutoCreate : function(){
14271         var cfg = {
14272             tag: 'div',
14273             // item is needed for carousel - not sure if it has any effect otherwise
14274             cls: 'tab-pane item',
14275             html: this.html || ''
14276         };
14277         
14278         if(this.active){
14279             cfg.cls += ' active';
14280         }
14281         
14282         if(this.tabId){
14283             cfg.tabId = this.tabId;
14284         }
14285         
14286         
14287         return cfg;
14288     },
14289     
14290     initEvents:  function()
14291     {
14292         Roo.log('-------- init events on tab panel ---------');
14293         
14294         var p = this.parent();
14295         this.navId = this.navId || p.navId;
14296         
14297         if (typeof(this.navId) != 'undefined') {
14298             // not really needed.. but just in case.. parent should be a NavGroup.
14299             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14300             Roo.log(['register', tg, this]);
14301             tg.register(this);
14302         }
14303     },
14304     
14305     
14306     onRender : function(ct, position)
14307     {
14308        // Roo.log("Call onRender: " + this.xtype);
14309         
14310         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14311         
14312         
14313         
14314         
14315         
14316     },
14317     
14318     setActive: function(state)
14319     {
14320         Roo.log("panel - set active " + this.tabId + "=" + state);
14321         
14322         this.active = state;
14323         if (!state) {
14324             this.el.removeClass('active');
14325             
14326         } else  if (!this.el.hasClass('active')) {
14327             this.el.addClass('active');
14328         }
14329         this.fireEvent('changed', this, state);
14330     }
14331     
14332     
14333 });
14334  
14335
14336  
14337
14338  /*
14339  * - LGPL
14340  *
14341  * DateField
14342  * 
14343  */
14344
14345 /**
14346  * @class Roo.bootstrap.DateField
14347  * @extends Roo.bootstrap.Input
14348  * Bootstrap DateField class
14349  * @cfg {Number} weekStart default 0
14350  * @cfg {Number} weekStart default 0
14351  * @cfg {Number} viewMode default empty, (months|years)
14352  * @cfg {Number} minViewMode default empty, (months|years)
14353  * @cfg {Number} startDate default -Infinity
14354  * @cfg {Number} endDate default Infinity
14355  * @cfg {Boolean} todayHighlight default false
14356  * @cfg {Boolean} todayBtn default false
14357  * @cfg {Boolean} calendarWeeks default false
14358  * @cfg {Object} daysOfWeekDisabled default empty
14359  * 
14360  * @cfg {Boolean} keyboardNavigation default true
14361  * @cfg {String} language default en
14362  * 
14363  * @constructor
14364  * Create a new DateField
14365  * @param {Object} config The config object
14366  */
14367
14368 Roo.bootstrap.DateField = function(config){
14369     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14370      this.addEvents({
14371             /**
14372              * @event show
14373              * Fires when this field show.
14374              * @param {Roo.bootstrap.DateField} this
14375              * @param {Mixed} date The date value
14376              */
14377             show : true,
14378             /**
14379              * @event show
14380              * Fires when this field hide.
14381              * @param {Roo.bootstrap.DateField} this
14382              * @param {Mixed} date The date value
14383              */
14384             hide : true,
14385             /**
14386              * @event select
14387              * Fires when select a date.
14388              * @param {Roo.bootstrap.DateField} this
14389              * @param {Mixed} date The date value
14390              */
14391             select : true
14392         });
14393 };
14394
14395 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14396     
14397     /**
14398      * @cfg {String} format
14399      * The default date format string which can be overriden for localization support.  The format must be
14400      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14401      */
14402     format : "m/d/y",
14403     /**
14404      * @cfg {String} altFormats
14405      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14406      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14407      */
14408     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14409     
14410     weekStart : 0,
14411     
14412     viewMode : '',
14413     
14414     minViewMode : '',
14415     
14416     todayHighlight : false,
14417     
14418     todayBtn: false,
14419     
14420     language: 'en',
14421     
14422     keyboardNavigation: true,
14423     
14424     calendarWeeks: false,
14425     
14426     startDate: -Infinity,
14427     
14428     endDate: Infinity,
14429     
14430     daysOfWeekDisabled: [],
14431     
14432     _events: [],
14433     
14434     UTCDate: function()
14435     {
14436         return new Date(Date.UTC.apply(Date, arguments));
14437     },
14438     
14439     UTCToday: function()
14440     {
14441         var today = new Date();
14442         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14443     },
14444     
14445     getDate: function() {
14446             var d = this.getUTCDate();
14447             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14448     },
14449     
14450     getUTCDate: function() {
14451             return this.date;
14452     },
14453     
14454     setDate: function(d) {
14455             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14456     },
14457     
14458     setUTCDate: function(d) {
14459             this.date = d;
14460             this.setValue(this.formatDate(this.date));
14461     },
14462         
14463     onRender: function(ct, position)
14464     {
14465         
14466         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14467         
14468         this.language = this.language || 'en';
14469         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14470         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14471         
14472         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14473         this.format = this.format || 'm/d/y';
14474         this.isInline = false;
14475         this.isInput = true;
14476         this.component = this.el.select('.add-on', true).first() || false;
14477         this.component = (this.component && this.component.length === 0) ? false : this.component;
14478         this.hasInput = this.component && this.inputEL().length;
14479         
14480         if (typeof(this.minViewMode === 'string')) {
14481             switch (this.minViewMode) {
14482                 case 'months':
14483                     this.minViewMode = 1;
14484                     break;
14485                 case 'years':
14486                     this.minViewMode = 2;
14487                     break;
14488                 default:
14489                     this.minViewMode = 0;
14490                     break;
14491             }
14492         }
14493         
14494         if (typeof(this.viewMode === 'string')) {
14495             switch (this.viewMode) {
14496                 case 'months':
14497                     this.viewMode = 1;
14498                     break;
14499                 case 'years':
14500                     this.viewMode = 2;
14501                     break;
14502                 default:
14503                     this.viewMode = 0;
14504                     break;
14505             }
14506         }
14507                 
14508         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14509         
14510 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14511         
14512         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14513         
14514         this.picker().on('mousedown', this.onMousedown, this);
14515         this.picker().on('click', this.onClick, this);
14516         
14517         this.picker().addClass('datepicker-dropdown');
14518         
14519         this.startViewMode = this.viewMode;
14520         
14521         
14522         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14523             if(!this.calendarWeeks){
14524                 v.remove();
14525                 return;
14526             };
14527             
14528             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14529             v.attr('colspan', function(i, val){
14530                 return parseInt(val) + 1;
14531             });
14532         })
14533                         
14534         
14535         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14536         
14537         this.setStartDate(this.startDate);
14538         this.setEndDate(this.endDate);
14539         
14540         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14541         
14542         this.fillDow();
14543         this.fillMonths();
14544         this.update();
14545         this.showMode();
14546         
14547         if(this.isInline) {
14548             this.show();
14549         }
14550     },
14551     
14552     picker : function()
14553     {
14554         return this.pickerEl;
14555 //        return this.el.select('.datepicker', true).first();
14556     },
14557     
14558     fillDow: function()
14559     {
14560         var dowCnt = this.weekStart;
14561         
14562         var dow = {
14563             tag: 'tr',
14564             cn: [
14565                 
14566             ]
14567         };
14568         
14569         if(this.calendarWeeks){
14570             dow.cn.push({
14571                 tag: 'th',
14572                 cls: 'cw',
14573                 html: '&nbsp;'
14574             })
14575         }
14576         
14577         while (dowCnt < this.weekStart + 7) {
14578             dow.cn.push({
14579                 tag: 'th',
14580                 cls: 'dow',
14581                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14582             });
14583         }
14584         
14585         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14586     },
14587     
14588     fillMonths: function()
14589     {    
14590         var i = 0
14591         var months = this.picker().select('>.datepicker-months td', true).first();
14592         
14593         months.dom.innerHTML = '';
14594         
14595         while (i < 12) {
14596             var month = {
14597                 tag: 'span',
14598                 cls: 'month',
14599                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14600             }
14601             
14602             months.createChild(month);
14603         }
14604         
14605     },
14606     
14607     update: function()
14608     {
14609         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;
14610         
14611         if (this.date < this.startDate) {
14612             this.viewDate = new Date(this.startDate);
14613         } else if (this.date > this.endDate) {
14614             this.viewDate = new Date(this.endDate);
14615         } else {
14616             this.viewDate = new Date(this.date);
14617         }
14618         
14619         this.fill();
14620     },
14621     
14622     fill: function() 
14623     {
14624         var d = new Date(this.viewDate),
14625                 year = d.getUTCFullYear(),
14626                 month = d.getUTCMonth(),
14627                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14628                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14629                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14630                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14631                 currentDate = this.date && this.date.valueOf(),
14632                 today = this.UTCToday();
14633         
14634         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14635         
14636 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14637         
14638 //        this.picker.select('>tfoot th.today').
14639 //                                              .text(dates[this.language].today)
14640 //                                              .toggle(this.todayBtn !== false);
14641     
14642         this.updateNavArrows();
14643         this.fillMonths();
14644                                                 
14645         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14646         
14647         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14648          
14649         prevMonth.setUTCDate(day);
14650         
14651         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14652         
14653         var nextMonth = new Date(prevMonth);
14654         
14655         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14656         
14657         nextMonth = nextMonth.valueOf();
14658         
14659         var fillMonths = false;
14660         
14661         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14662         
14663         while(prevMonth.valueOf() < nextMonth) {
14664             var clsName = '';
14665             
14666             if (prevMonth.getUTCDay() === this.weekStart) {
14667                 if(fillMonths){
14668                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14669                 }
14670                     
14671                 fillMonths = {
14672                     tag: 'tr',
14673                     cn: []
14674                 };
14675                 
14676                 if(this.calendarWeeks){
14677                     // ISO 8601: First week contains first thursday.
14678                     // ISO also states week starts on Monday, but we can be more abstract here.
14679                     var
14680                     // Start of current week: based on weekstart/current date
14681                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14682                     // Thursday of this week
14683                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14684                     // First Thursday of year, year from thursday
14685                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14686                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14687                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14688                     
14689                     fillMonths.cn.push({
14690                         tag: 'td',
14691                         cls: 'cw',
14692                         html: calWeek
14693                     });
14694                 }
14695             }
14696             
14697             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14698                 clsName += ' old';
14699             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14700                 clsName += ' new';
14701             }
14702             if (this.todayHighlight &&
14703                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14704                 prevMonth.getUTCMonth() == today.getMonth() &&
14705                 prevMonth.getUTCDate() == today.getDate()) {
14706                 clsName += ' today';
14707             }
14708             
14709             if (currentDate && prevMonth.valueOf() === currentDate) {
14710                 clsName += ' active';
14711             }
14712             
14713             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14714                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14715                     clsName += ' disabled';
14716             }
14717             
14718             fillMonths.cn.push({
14719                 tag: 'td',
14720                 cls: 'day ' + clsName,
14721                 html: prevMonth.getDate()
14722             })
14723             
14724             prevMonth.setDate(prevMonth.getDate()+1);
14725         }
14726           
14727         var currentYear = this.date && this.date.getUTCFullYear();
14728         var currentMonth = this.date && this.date.getUTCMonth();
14729         
14730         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14731         
14732         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14733             v.removeClass('active');
14734             
14735             if(currentYear === year && k === currentMonth){
14736                 v.addClass('active');
14737             }
14738             
14739             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14740                 v.addClass('disabled');
14741             }
14742             
14743         });
14744         
14745         
14746         year = parseInt(year/10, 10) * 10;
14747         
14748         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14749         
14750         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14751         
14752         year -= 1;
14753         for (var i = -1; i < 11; i++) {
14754             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14755                 tag: 'span',
14756                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14757                 html: year
14758             })
14759             
14760             year += 1;
14761         }
14762     },
14763     
14764     showMode: function(dir) 
14765     {
14766         if (dir) {
14767             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14768         }
14769         Roo.each(this.picker().select('>div',true).elements, function(v){
14770             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14771             v.hide();
14772         });
14773         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14774     },
14775     
14776     place: function()
14777     {
14778         if(this.isInline) return;
14779         
14780         this.picker().removeClass(['bottom', 'top']);
14781         
14782         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14783             /*
14784              * place to the top of element!
14785              *
14786              */
14787             
14788             this.picker().addClass('top');
14789             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14790             
14791             return;
14792         }
14793         
14794         this.picker().addClass('bottom');
14795         
14796         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14797     },
14798     
14799     parseDate : function(value)
14800     {
14801         if(!value || value instanceof Date){
14802             return value;
14803         }
14804         var v = Date.parseDate(value, this.format);
14805         if (!v && this.useIso) {
14806             v = Date.parseDate(value, 'Y-m-d');
14807         }
14808         if(!v && this.altFormats){
14809             if(!this.altFormatsArray){
14810                 this.altFormatsArray = this.altFormats.split("|");
14811             }
14812             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14813                 v = Date.parseDate(value, this.altFormatsArray[i]);
14814             }
14815         }
14816         return v;
14817     },
14818     
14819     formatDate : function(date, fmt)
14820     {
14821         return (!date || !(date instanceof Date)) ?
14822         date : date.dateFormat(fmt || this.format);
14823     },
14824     
14825     onFocus : function()
14826     {
14827         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14828         this.show();
14829     },
14830     
14831     onBlur : function()
14832     {
14833         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14834         
14835         var d = this.inputEl().getValue();
14836         
14837         this.setValue(d);
14838                 
14839         this.hide();
14840     },
14841     
14842     show : function()
14843     {
14844         this.picker().show();
14845         this.update();
14846         this.place();
14847         
14848         this.fireEvent('show', this, this.date);
14849     },
14850     
14851     hide : function()
14852     {
14853         if(this.isInline) return;
14854         this.picker().hide();
14855         this.viewMode = this.startViewMode;
14856         this.showMode();
14857         
14858         this.fireEvent('hide', this, this.date);
14859         
14860     },
14861     
14862     onMousedown: function(e)
14863     {
14864         e.stopPropagation();
14865         e.preventDefault();
14866     },
14867     
14868     keyup: function(e)
14869     {
14870         Roo.bootstrap.DateField.superclass.keyup.call(this);
14871         this.update();
14872     },
14873
14874     setValue: function(v)
14875     {
14876         var d = new Date(v).clearTime();
14877         
14878         if(isNaN(d.getTime())){
14879             this.date = this.viewDate = '';
14880             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14881             return;
14882         }
14883         
14884         v = this.formatDate(d);
14885         
14886         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14887         
14888         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14889      
14890         this.update();
14891
14892         this.fireEvent('select', this, this.date);
14893         
14894     },
14895     
14896     getValue: function()
14897     {
14898         return this.formatDate(this.date);
14899     },
14900     
14901     fireKey: function(e)
14902     {
14903         if (!this.picker().isVisible()){
14904             if (e.keyCode == 27) // allow escape to hide and re-show picker
14905                 this.show();
14906             return;
14907         }
14908         
14909         var dateChanged = false,
14910         dir, day, month,
14911         newDate, newViewDate;
14912         
14913         switch(e.keyCode){
14914             case 27: // escape
14915                 this.hide();
14916                 e.preventDefault();
14917                 break;
14918             case 37: // left
14919             case 39: // right
14920                 if (!this.keyboardNavigation) break;
14921                 dir = e.keyCode == 37 ? -1 : 1;
14922                 
14923                 if (e.ctrlKey){
14924                     newDate = this.moveYear(this.date, dir);
14925                     newViewDate = this.moveYear(this.viewDate, dir);
14926                 } else if (e.shiftKey){
14927                     newDate = this.moveMonth(this.date, dir);
14928                     newViewDate = this.moveMonth(this.viewDate, dir);
14929                 } else {
14930                     newDate = new Date(this.date);
14931                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14932                     newViewDate = new Date(this.viewDate);
14933                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14934                 }
14935                 if (this.dateWithinRange(newDate)){
14936                     this.date = newDate;
14937                     this.viewDate = newViewDate;
14938                     this.setValue(this.formatDate(this.date));
14939 //                    this.update();
14940                     e.preventDefault();
14941                     dateChanged = true;
14942                 }
14943                 break;
14944             case 38: // up
14945             case 40: // down
14946                 if (!this.keyboardNavigation) break;
14947                 dir = e.keyCode == 38 ? -1 : 1;
14948                 if (e.ctrlKey){
14949                     newDate = this.moveYear(this.date, dir);
14950                     newViewDate = this.moveYear(this.viewDate, dir);
14951                 } else if (e.shiftKey){
14952                     newDate = this.moveMonth(this.date, dir);
14953                     newViewDate = this.moveMonth(this.viewDate, dir);
14954                 } else {
14955                     newDate = new Date(this.date);
14956                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14957                     newViewDate = new Date(this.viewDate);
14958                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14959                 }
14960                 if (this.dateWithinRange(newDate)){
14961                     this.date = newDate;
14962                     this.viewDate = newViewDate;
14963                     this.setValue(this.formatDate(this.date));
14964 //                    this.update();
14965                     e.preventDefault();
14966                     dateChanged = true;
14967                 }
14968                 break;
14969             case 13: // enter
14970                 this.setValue(this.formatDate(this.date));
14971                 this.hide();
14972                 e.preventDefault();
14973                 break;
14974             case 9: // tab
14975                 this.setValue(this.formatDate(this.date));
14976                 this.hide();
14977                 break;
14978             case 16: // shift
14979             case 17: // ctrl
14980             case 18: // alt
14981                 break;
14982             default :
14983                 this.hide();
14984                 
14985         }
14986     },
14987     
14988     
14989     onClick: function(e) 
14990     {
14991         e.stopPropagation();
14992         e.preventDefault();
14993         
14994         var target = e.getTarget();
14995         
14996         if(target.nodeName.toLowerCase() === 'i'){
14997             target = Roo.get(target).dom.parentNode;
14998         }
14999         
15000         var nodeName = target.nodeName;
15001         var className = target.className;
15002         var html = target.innerHTML;
15003         
15004         switch(nodeName.toLowerCase()) {
15005             case 'th':
15006                 switch(className) {
15007                     case 'switch':
15008                         this.showMode(1);
15009                         break;
15010                     case 'prev':
15011                     case 'next':
15012                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15013                         switch(this.viewMode){
15014                                 case 0:
15015                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15016                                         break;
15017                                 case 1:
15018                                 case 2:
15019                                         this.viewDate = this.moveYear(this.viewDate, dir);
15020                                         break;
15021                         }
15022                         this.fill();
15023                         break;
15024                     case 'today':
15025                         var date = new Date();
15026                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15027 //                        this.fill()
15028                         this.setValue(this.formatDate(this.date));
15029                         
15030                         this.hide();
15031                         break;
15032                 }
15033                 break;
15034             case 'span':
15035                 if (className.indexOf('disabled') === -1) {
15036                     this.viewDate.setUTCDate(1);
15037                     if (className.indexOf('month') !== -1) {
15038                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15039                     } else {
15040                         var year = parseInt(html, 10) || 0;
15041                         this.viewDate.setUTCFullYear(year);
15042                         
15043                     }
15044                     this.showMode(-1);
15045                     this.fill();
15046                 }
15047                 break;
15048                 
15049             case 'td':
15050                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15051                     var day = parseInt(html, 10) || 1;
15052                     var year = this.viewDate.getUTCFullYear(),
15053                         month = this.viewDate.getUTCMonth();
15054
15055                     if (className.indexOf('old') !== -1) {
15056                         if(month === 0 ){
15057                             month = 11;
15058                             year -= 1;
15059                         }else{
15060                             month -= 1;
15061                         }
15062                     } else if (className.indexOf('new') !== -1) {
15063                         if (month == 11) {
15064                             month = 0;
15065                             year += 1;
15066                         } else {
15067                             month += 1;
15068                         }
15069                     }
15070                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15071                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15072 //                    this.fill();
15073                     this.setValue(this.formatDate(this.date));
15074                     this.hide();
15075                 }
15076                 break;
15077         }
15078     },
15079     
15080     setStartDate: function(startDate)
15081     {
15082         this.startDate = startDate || -Infinity;
15083         if (this.startDate !== -Infinity) {
15084             this.startDate = this.parseDate(this.startDate);
15085         }
15086         this.update();
15087         this.updateNavArrows();
15088     },
15089
15090     setEndDate: function(endDate)
15091     {
15092         this.endDate = endDate || Infinity;
15093         if (this.endDate !== Infinity) {
15094             this.endDate = this.parseDate(this.endDate);
15095         }
15096         this.update();
15097         this.updateNavArrows();
15098     },
15099     
15100     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15101     {
15102         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15103         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15104             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15105         }
15106         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15107             return parseInt(d, 10);
15108         });
15109         this.update();
15110         this.updateNavArrows();
15111     },
15112     
15113     updateNavArrows: function() 
15114     {
15115         var d = new Date(this.viewDate),
15116         year = d.getUTCFullYear(),
15117         month = d.getUTCMonth();
15118         
15119         Roo.each(this.picker().select('.prev', true).elements, function(v){
15120             v.show();
15121             switch (this.viewMode) {
15122                 case 0:
15123
15124                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15125                         v.hide();
15126                     }
15127                     break;
15128                 case 1:
15129                 case 2:
15130                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15131                         v.hide();
15132                     }
15133                     break;
15134             }
15135         });
15136         
15137         Roo.each(this.picker().select('.next', true).elements, function(v){
15138             v.show();
15139             switch (this.viewMode) {
15140                 case 0:
15141
15142                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15143                         v.hide();
15144                     }
15145                     break;
15146                 case 1:
15147                 case 2:
15148                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15149                         v.hide();
15150                     }
15151                     break;
15152             }
15153         })
15154     },
15155     
15156     moveMonth: function(date, dir)
15157     {
15158         if (!dir) return date;
15159         var new_date = new Date(date.valueOf()),
15160         day = new_date.getUTCDate(),
15161         month = new_date.getUTCMonth(),
15162         mag = Math.abs(dir),
15163         new_month, test;
15164         dir = dir > 0 ? 1 : -1;
15165         if (mag == 1){
15166             test = dir == -1
15167             // If going back one month, make sure month is not current month
15168             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15169             ? function(){
15170                 return new_date.getUTCMonth() == month;
15171             }
15172             // If going forward one month, make sure month is as expected
15173             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15174             : function(){
15175                 return new_date.getUTCMonth() != new_month;
15176             };
15177             new_month = month + dir;
15178             new_date.setUTCMonth(new_month);
15179             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15180             if (new_month < 0 || new_month > 11)
15181                 new_month = (new_month + 12) % 12;
15182         } else {
15183             // For magnitudes >1, move one month at a time...
15184             for (var i=0; i<mag; i++)
15185                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15186                 new_date = this.moveMonth(new_date, dir);
15187             // ...then reset the day, keeping it in the new month
15188             new_month = new_date.getUTCMonth();
15189             new_date.setUTCDate(day);
15190             test = function(){
15191                 return new_month != new_date.getUTCMonth();
15192             };
15193         }
15194         // Common date-resetting loop -- if date is beyond end of month, make it
15195         // end of month
15196         while (test()){
15197             new_date.setUTCDate(--day);
15198             new_date.setUTCMonth(new_month);
15199         }
15200         return new_date;
15201     },
15202
15203     moveYear: function(date, dir)
15204     {
15205         return this.moveMonth(date, dir*12);
15206     },
15207
15208     dateWithinRange: function(date)
15209     {
15210         return date >= this.startDate && date <= this.endDate;
15211     },
15212
15213     
15214     remove: function() 
15215     {
15216         this.picker().remove();
15217     }
15218    
15219 });
15220
15221 Roo.apply(Roo.bootstrap.DateField,  {
15222     
15223     head : {
15224         tag: 'thead',
15225         cn: [
15226         {
15227             tag: 'tr',
15228             cn: [
15229             {
15230                 tag: 'th',
15231                 cls: 'prev',
15232                 html: '<i class="fa fa-arrow-left"/>'
15233             },
15234             {
15235                 tag: 'th',
15236                 cls: 'switch',
15237                 colspan: '5'
15238             },
15239             {
15240                 tag: 'th',
15241                 cls: 'next',
15242                 html: '<i class="fa fa-arrow-right"/>'
15243             }
15244
15245             ]
15246         }
15247         ]
15248     },
15249     
15250     content : {
15251         tag: 'tbody',
15252         cn: [
15253         {
15254             tag: 'tr',
15255             cn: [
15256             {
15257                 tag: 'td',
15258                 colspan: '7'
15259             }
15260             ]
15261         }
15262         ]
15263     },
15264     
15265     footer : {
15266         tag: 'tfoot',
15267         cn: [
15268         {
15269             tag: 'tr',
15270             cn: [
15271             {
15272                 tag: 'th',
15273                 colspan: '7',
15274                 cls: 'today'
15275             }
15276                     
15277             ]
15278         }
15279         ]
15280     },
15281     
15282     dates:{
15283         en: {
15284             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15285             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15286             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15287             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15288             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15289             today: "Today"
15290         }
15291     },
15292     
15293     modes: [
15294     {
15295         clsName: 'days',
15296         navFnc: 'Month',
15297         navStep: 1
15298     },
15299     {
15300         clsName: 'months',
15301         navFnc: 'FullYear',
15302         navStep: 1
15303     },
15304     {
15305         clsName: 'years',
15306         navFnc: 'FullYear',
15307         navStep: 10
15308     }]
15309 });
15310
15311 Roo.apply(Roo.bootstrap.DateField,  {
15312   
15313     template : {
15314         tag: 'div',
15315         cls: 'datepicker dropdown-menu',
15316         cn: [
15317         {
15318             tag: 'div',
15319             cls: 'datepicker-days',
15320             cn: [
15321             {
15322                 tag: 'table',
15323                 cls: 'table-condensed',
15324                 cn:[
15325                 Roo.bootstrap.DateField.head,
15326                 {
15327                     tag: 'tbody'
15328                 },
15329                 Roo.bootstrap.DateField.footer
15330                 ]
15331             }
15332             ]
15333         },
15334         {
15335             tag: 'div',
15336             cls: 'datepicker-months',
15337             cn: [
15338             {
15339                 tag: 'table',
15340                 cls: 'table-condensed',
15341                 cn:[
15342                 Roo.bootstrap.DateField.head,
15343                 Roo.bootstrap.DateField.content,
15344                 Roo.bootstrap.DateField.footer
15345                 ]
15346             }
15347             ]
15348         },
15349         {
15350             tag: 'div',
15351             cls: 'datepicker-years',
15352             cn: [
15353             {
15354                 tag: 'table',
15355                 cls: 'table-condensed',
15356                 cn:[
15357                 Roo.bootstrap.DateField.head,
15358                 Roo.bootstrap.DateField.content,
15359                 Roo.bootstrap.DateField.footer
15360                 ]
15361             }
15362             ]
15363         }
15364         ]
15365     }
15366 });
15367
15368  
15369
15370  /*
15371  * - LGPL
15372  *
15373  * TimeField
15374  * 
15375  */
15376
15377 /**
15378  * @class Roo.bootstrap.TimeField
15379  * @extends Roo.bootstrap.Input
15380  * Bootstrap DateField class
15381  * 
15382  * 
15383  * @constructor
15384  * Create a new TimeField
15385  * @param {Object} config The config object
15386  */
15387
15388 Roo.bootstrap.TimeField = function(config){
15389     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15390     this.addEvents({
15391             /**
15392              * @event show
15393              * Fires when this field show.
15394              * @param {Roo.bootstrap.DateField} this
15395              * @param {Mixed} date The date value
15396              */
15397             show : true,
15398             /**
15399              * @event show
15400              * Fires when this field hide.
15401              * @param {Roo.bootstrap.DateField} this
15402              * @param {Mixed} date The date value
15403              */
15404             hide : true,
15405             /**
15406              * @event select
15407              * Fires when select a date.
15408              * @param {Roo.bootstrap.DateField} this
15409              * @param {Mixed} date The date value
15410              */
15411             select : true
15412         });
15413 };
15414
15415 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15416     
15417     /**
15418      * @cfg {String} format
15419      * The default time format string which can be overriden for localization support.  The format must be
15420      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15421      */
15422     format : "H:i",
15423        
15424     onRender: function(ct, position)
15425     {
15426         
15427         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15428                 
15429         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15430         
15431         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15432         
15433         this.pop = this.picker().select('>.datepicker-time',true).first();
15434         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15435         
15436         this.picker().on('mousedown', this.onMousedown, this);
15437         this.picker().on('click', this.onClick, this);
15438         
15439         this.picker().addClass('datepicker-dropdown');
15440     
15441         this.fillTime();
15442         this.update();
15443             
15444         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15445         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15446         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15447         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15448         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15449         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15450
15451     },
15452     
15453     fireKey: function(e){
15454         if (!this.picker().isVisible()){
15455             if (e.keyCode == 27) // allow escape to hide and re-show picker
15456                 this.show();
15457             return;
15458         }
15459
15460         e.preventDefault();
15461         
15462         switch(e.keyCode){
15463             case 27: // escape
15464                 this.hide();
15465                 break;
15466             case 37: // left
15467             case 39: // right
15468                 this.onTogglePeriod();
15469                 break;
15470             case 38: // up
15471                 this.onIncrementMinutes();
15472                 break;
15473             case 40: // down
15474                 this.onDecrementMinutes();
15475                 break;
15476             case 13: // enter
15477             case 9: // tab
15478                 this.setTime();
15479                 break;
15480         }
15481     },
15482     
15483     onClick: function(e) {
15484         e.stopPropagation();
15485         e.preventDefault();
15486     },
15487     
15488     picker : function()
15489     {
15490         return this.el.select('.datepicker', true).first();
15491     },
15492     
15493     fillTime: function()
15494     {    
15495         var time = this.pop.select('tbody', true).first();
15496         
15497         time.dom.innerHTML = '';
15498         
15499         time.createChild({
15500             tag: 'tr',
15501             cn: [
15502                 {
15503                     tag: 'td',
15504                     cn: [
15505                         {
15506                             tag: 'a',
15507                             href: '#',
15508                             cls: 'btn',
15509                             cn: [
15510                                 {
15511                                     tag: 'span',
15512                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15513                                 }
15514                             ]
15515                         } 
15516                     ]
15517                 },
15518                 {
15519                     tag: 'td',
15520                     cls: 'separator'
15521                 },
15522                 {
15523                     tag: 'td',
15524                     cn: [
15525                         {
15526                             tag: 'a',
15527                             href: '#',
15528                             cls: 'btn',
15529                             cn: [
15530                                 {
15531                                     tag: 'span',
15532                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15533                                 }
15534                             ]
15535                         }
15536                     ]
15537                 },
15538                 {
15539                     tag: 'td',
15540                     cls: 'separator'
15541                 }
15542             ]
15543         });
15544         
15545         time.createChild({
15546             tag: 'tr',
15547             cn: [
15548                 {
15549                     tag: 'td',
15550                     cn: [
15551                         {
15552                             tag: 'span',
15553                             cls: 'timepicker-hour',
15554                             html: '00'
15555                         }  
15556                     ]
15557                 },
15558                 {
15559                     tag: 'td',
15560                     cls: 'separator',
15561                     html: ':'
15562                 },
15563                 {
15564                     tag: 'td',
15565                     cn: [
15566                         {
15567                             tag: 'span',
15568                             cls: 'timepicker-minute',
15569                             html: '00'
15570                         }  
15571                     ]
15572                 },
15573                 {
15574                     tag: 'td',
15575                     cls: 'separator'
15576                 },
15577                 {
15578                     tag: 'td',
15579                     cn: [
15580                         {
15581                             tag: 'button',
15582                             type: 'button',
15583                             cls: 'btn btn-primary period',
15584                             html: 'AM'
15585                             
15586                         }
15587                     ]
15588                 }
15589             ]
15590         });
15591         
15592         time.createChild({
15593             tag: 'tr',
15594             cn: [
15595                 {
15596                     tag: 'td',
15597                     cn: [
15598                         {
15599                             tag: 'a',
15600                             href: '#',
15601                             cls: 'btn',
15602                             cn: [
15603                                 {
15604                                     tag: 'span',
15605                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15606                                 }
15607                             ]
15608                         }
15609                     ]
15610                 },
15611                 {
15612                     tag: 'td',
15613                     cls: 'separator'
15614                 },
15615                 {
15616                     tag: 'td',
15617                     cn: [
15618                         {
15619                             tag: 'a',
15620                             href: '#',
15621                             cls: 'btn',
15622                             cn: [
15623                                 {
15624                                     tag: 'span',
15625                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15626                                 }
15627                             ]
15628                         }
15629                     ]
15630                 },
15631                 {
15632                     tag: 'td',
15633                     cls: 'separator'
15634                 }
15635             ]
15636         });
15637         
15638     },
15639     
15640     update: function()
15641     {
15642         
15643         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15644         
15645         this.fill();
15646     },
15647     
15648     fill: function() 
15649     {
15650         var hours = this.time.getHours();
15651         var minutes = this.time.getMinutes();
15652         var period = 'AM';
15653         
15654         if(hours > 11){
15655             period = 'PM';
15656         }
15657         
15658         if(hours == 0){
15659             hours = 12;
15660         }
15661         
15662         
15663         if(hours > 12){
15664             hours = hours - 12;
15665         }
15666         
15667         if(hours < 10){
15668             hours = '0' + hours;
15669         }
15670         
15671         if(minutes < 10){
15672             minutes = '0' + minutes;
15673         }
15674         
15675         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15676         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15677         this.pop.select('button', true).first().dom.innerHTML = period;
15678         
15679     },
15680     
15681     place: function()
15682     {   
15683         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15684         
15685         var cls = ['bottom'];
15686         
15687         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15688             cls.pop();
15689             cls.push('top');
15690         }
15691         
15692         cls.push('right');
15693         
15694         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15695             cls.pop();
15696             cls.push('left');
15697         }
15698         
15699         this.picker().addClass(cls.join('-'));
15700         
15701         var _this = this;
15702         
15703         Roo.each(cls, function(c){
15704             if(c == 'bottom'){
15705                 _this.picker().setTop(_this.inputEl().getHeight());
15706                 return;
15707             }
15708             if(c == 'top'){
15709                 _this.picker().setTop(0 - _this.picker().getHeight());
15710                 return;
15711             }
15712             
15713             if(c == 'left'){
15714                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15715                 return;
15716             }
15717             if(c == 'right'){
15718                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15719                 return;
15720             }
15721         });
15722         
15723     },
15724   
15725     onFocus : function()
15726     {
15727         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15728         this.show();
15729     },
15730     
15731     onBlur : function()
15732     {
15733         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15734         this.hide();
15735     },
15736     
15737     show : function()
15738     {
15739         this.picker().show();
15740         this.pop.show();
15741         this.update();
15742         this.place();
15743         
15744         this.fireEvent('show', this, this.date);
15745     },
15746     
15747     hide : function()
15748     {
15749         this.picker().hide();
15750         this.pop.hide();
15751         
15752         this.fireEvent('hide', this, this.date);
15753     },
15754     
15755     setTime : function()
15756     {
15757         this.hide();
15758         this.setValue(this.time.format(this.format));
15759         
15760         this.fireEvent('select', this, this.date);
15761         
15762         
15763     },
15764     
15765     onMousedown: function(e){
15766         e.stopPropagation();
15767         e.preventDefault();
15768     },
15769     
15770     onIncrementHours: function()
15771     {
15772         Roo.log('onIncrementHours');
15773         this.time = this.time.add(Date.HOUR, 1);
15774         this.update();
15775         
15776     },
15777     
15778     onDecrementHours: function()
15779     {
15780         Roo.log('onDecrementHours');
15781         this.time = this.time.add(Date.HOUR, -1);
15782         this.update();
15783     },
15784     
15785     onIncrementMinutes: function()
15786     {
15787         Roo.log('onIncrementMinutes');
15788         this.time = this.time.add(Date.MINUTE, 1);
15789         this.update();
15790     },
15791     
15792     onDecrementMinutes: function()
15793     {
15794         Roo.log('onDecrementMinutes');
15795         this.time = this.time.add(Date.MINUTE, -1);
15796         this.update();
15797     },
15798     
15799     onTogglePeriod: function()
15800     {
15801         Roo.log('onTogglePeriod');
15802         this.time = this.time.add(Date.HOUR, 12);
15803         this.update();
15804     }
15805     
15806    
15807 });
15808
15809 Roo.apply(Roo.bootstrap.TimeField,  {
15810     
15811     content : {
15812         tag: 'tbody',
15813         cn: [
15814             {
15815                 tag: 'tr',
15816                 cn: [
15817                 {
15818                     tag: 'td',
15819                     colspan: '7'
15820                 }
15821                 ]
15822             }
15823         ]
15824     },
15825     
15826     footer : {
15827         tag: 'tfoot',
15828         cn: [
15829             {
15830                 tag: 'tr',
15831                 cn: [
15832                 {
15833                     tag: 'th',
15834                     colspan: '7',
15835                     cls: '',
15836                     cn: [
15837                         {
15838                             tag: 'button',
15839                             cls: 'btn btn-info ok',
15840                             html: 'OK'
15841                         }
15842                     ]
15843                 }
15844
15845                 ]
15846             }
15847         ]
15848     }
15849 });
15850
15851 Roo.apply(Roo.bootstrap.TimeField,  {
15852   
15853     template : {
15854         tag: 'div',
15855         cls: 'datepicker dropdown-menu',
15856         cn: [
15857             {
15858                 tag: 'div',
15859                 cls: 'datepicker-time',
15860                 cn: [
15861                 {
15862                     tag: 'table',
15863                     cls: 'table-condensed',
15864                     cn:[
15865                     Roo.bootstrap.TimeField.content,
15866                     Roo.bootstrap.TimeField.footer
15867                     ]
15868                 }
15869                 ]
15870             }
15871         ]
15872     }
15873 });
15874
15875  
15876
15877  /*
15878  * - LGPL
15879  *
15880  * CheckBox
15881  * 
15882  */
15883
15884 /**
15885  * @class Roo.bootstrap.CheckBox
15886  * @extends Roo.bootstrap.Input
15887  * Bootstrap CheckBox class
15888  * 
15889  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15890  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15891  * @cfg {String} boxLabel The text that appears beside the checkbox
15892  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15893  * @cfg {Boolean} checked initnal the element
15894  * 
15895  * 
15896  * @constructor
15897  * Create a new CheckBox
15898  * @param {Object} config The config object
15899  */
15900
15901 Roo.bootstrap.CheckBox = function(config){
15902     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15903    
15904         this.addEvents({
15905             /**
15906             * @event check
15907             * Fires when the element is checked or unchecked.
15908             * @param {Roo.bootstrap.CheckBox} this This input
15909             * @param {Boolean} checked The new checked value
15910             */
15911            check : true
15912         });
15913 };
15914
15915 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15916     
15917     inputType: 'checkbox',
15918     inputValue: 1,
15919     valueOff: 0,
15920     boxLabel: false,
15921     checked: false,
15922     weight : false,
15923     
15924     getAutoCreate : function()
15925     {
15926         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15927         
15928         var id = Roo.id();
15929         
15930         var cfg = {};
15931         
15932         cfg.cls = 'form-group checkbox' //input-group
15933         
15934         
15935         
15936         
15937         var input =  {
15938             tag: 'input',
15939             id : id,
15940             type : this.inputType,
15941             value : (!this.checked) ? this.valueOff : this.inputValue,
15942             cls : 'roo-checkbox', //'form-box',
15943             placeholder : this.placeholder || ''
15944             
15945         };
15946         
15947         if (this.weight) { // Validity check?
15948             cfg.cls += " checkbox-" + this.weight;
15949         }
15950         
15951         if (this.disabled) {
15952             input.disabled=true;
15953         }
15954         
15955         if(this.checked){
15956             input.checked = this.checked;
15957         }
15958         
15959         if (this.name) {
15960             input.name = this.name;
15961         }
15962         
15963         if (this.size) {
15964             input.cls += ' input-' + this.size;
15965         }
15966         
15967         var settings=this;
15968         ['xs','sm','md','lg'].map(function(size){
15969             if (settings[size]) {
15970                 cfg.cls += ' col-' + size + '-' + settings[size];
15971             }
15972         });
15973         
15974        
15975         
15976         var inputblock = input;
15977         
15978         
15979         
15980         
15981         if (this.before || this.after) {
15982             
15983             inputblock = {
15984                 cls : 'input-group',
15985                 cn :  [] 
15986             };
15987             if (this.before) {
15988                 inputblock.cn.push({
15989                     tag :'span',
15990                     cls : 'input-group-addon',
15991                     html : this.before
15992                 });
15993             }
15994             inputblock.cn.push(input);
15995             if (this.after) {
15996                 inputblock.cn.push({
15997                     tag :'span',
15998                     cls : 'input-group-addon',
15999                     html : this.after
16000                 });
16001             }
16002             
16003         };
16004         
16005         if (align ==='left' && this.fieldLabel.length) {
16006                 Roo.log("left and has label");
16007                 cfg.cn = [
16008                     
16009                     {
16010                         tag: 'label',
16011                         'for' :  id,
16012                         cls : 'control-label col-md-' + this.labelWidth,
16013                         html : this.fieldLabel
16014                         
16015                     },
16016                     {
16017                         cls : "col-md-" + (12 - this.labelWidth), 
16018                         cn: [
16019                             inputblock
16020                         ]
16021                     }
16022                     
16023                 ];
16024         } else if ( this.fieldLabel.length) {
16025                 Roo.log(" label");
16026                 cfg.cn = [
16027                    
16028                     {
16029                         tag: this.boxLabel ? 'span' : 'label',
16030                         'for': id,
16031                         cls: 'control-label box-input-label',
16032                         //cls : 'input-group-addon',
16033                         html : this.fieldLabel
16034                         
16035                     },
16036                     
16037                     inputblock
16038                     
16039                 ];
16040
16041         } else {
16042             
16043                 Roo.log(" no label && no align");
16044                 cfg.cn = [  inputblock ] ;
16045                 
16046                 
16047         };
16048          if(this.boxLabel){
16049             cfg.cn.push( {
16050                 tag: 'label',
16051                 'for': id,
16052                 cls: 'box-label',
16053                 html: this.boxLabel
16054                 
16055             });
16056         }
16057         
16058         
16059        
16060         return cfg;
16061         
16062     },
16063     
16064     /**
16065      * return the real input element.
16066      */
16067     inputEl: function ()
16068     {
16069         return this.el.select('input.roo-checkbox',true).first();
16070     },
16071     
16072     label: function()
16073     {
16074         return this.el.select('label.control-label',true).first();
16075     },
16076     
16077     initEvents : function()
16078     {
16079 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16080         
16081         this.inputEl().on('click', this.onClick,  this);
16082         
16083     },
16084     
16085     onClick : function()
16086     {   
16087         this.setChecked(!this.checked);
16088     },
16089     
16090     setChecked : function(state,suppressEvent)
16091     {
16092         this.checked = state;
16093         
16094         this.inputEl().dom.checked = state;
16095         
16096         if(suppressEvent !== true){
16097             this.fireEvent('check', this, state);
16098         }
16099         
16100         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16101         
16102     },
16103     
16104     setValue : function(v,suppressEvent)
16105     {
16106         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16107     }
16108     
16109 });
16110
16111  
16112 /*
16113  * - LGPL
16114  *
16115  * Radio
16116  * 
16117  */
16118
16119 /**
16120  * @class Roo.bootstrap.Radio
16121  * @extends Roo.bootstrap.CheckBox
16122  * Bootstrap Radio class
16123
16124  * @constructor
16125  * Create a new Radio
16126  * @param {Object} config The config object
16127  */
16128
16129 Roo.bootstrap.Radio = function(config){
16130     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16131    
16132 };
16133
16134 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16135     
16136     inputType: 'radio',
16137     inputValue: '',
16138     valueOff: '',
16139     
16140     getAutoCreate : function()
16141     {
16142         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16143         
16144         var id = Roo.id();
16145         
16146         var cfg = {};
16147         
16148         cfg.cls = 'form-group radio' //input-group
16149         
16150         var input =  {
16151             tag: 'input',
16152             id : id,
16153             type : this.inputType,
16154             value : (!this.checked) ? this.valueOff : this.inputValue,
16155             cls : 'roo-radio',
16156             placeholder : this.placeholder || ''
16157             
16158         };
16159           if (this.weight) { // Validity check?
16160             cfg.cls += " radio-" + this.weight;
16161         }
16162         if (this.disabled) {
16163             input.disabled=true;
16164         }
16165         
16166         if(this.checked){
16167             input.checked = this.checked;
16168         }
16169         
16170         if (this.name) {
16171             input.name = this.name;
16172         }
16173         
16174         if (this.size) {
16175             input.cls += ' input-' + this.size;
16176         }
16177         
16178         var settings=this;
16179         ['xs','sm','md','lg'].map(function(size){
16180             if (settings[size]) {
16181                 cfg.cls += ' col-' + size + '-' + settings[size];
16182             }
16183         });
16184         
16185         var inputblock = input;
16186         
16187         if (this.before || this.after) {
16188             
16189             inputblock = {
16190                 cls : 'input-group',
16191                 cn :  [] 
16192             };
16193             if (this.before) {
16194                 inputblock.cn.push({
16195                     tag :'span',
16196                     cls : 'input-group-addon',
16197                     html : this.before
16198                 });
16199             }
16200             inputblock.cn.push(input);
16201             if (this.after) {
16202                 inputblock.cn.push({
16203                     tag :'span',
16204                     cls : 'input-group-addon',
16205                     html : this.after
16206                 });
16207             }
16208             
16209         };
16210         
16211         if (align ==='left' && this.fieldLabel.length) {
16212                 Roo.log("left and has label");
16213                 cfg.cn = [
16214                     
16215                     {
16216                         tag: 'label',
16217                         'for' :  id,
16218                         cls : 'control-label col-md-' + this.labelWidth,
16219                         html : this.fieldLabel
16220                         
16221                     },
16222                     {
16223                         cls : "col-md-" + (12 - this.labelWidth), 
16224                         cn: [
16225                             inputblock
16226                         ]
16227                     }
16228                     
16229                 ];
16230         } else if ( this.fieldLabel.length) {
16231                 Roo.log(" label");
16232                  cfg.cn = [
16233                    
16234                     {
16235                         tag: 'label',
16236                         'for': id,
16237                         cls: 'control-label box-input-label',
16238                         //cls : 'input-group-addon',
16239                         html : this.fieldLabel
16240                         
16241                     },
16242                     
16243                     inputblock
16244                     
16245                 ];
16246
16247         } else {
16248             
16249                    Roo.log(" no label && no align");
16250                 cfg.cn = [
16251                     
16252                         inputblock
16253                     
16254                 ];
16255                 
16256                 
16257         };
16258         
16259         if(this.boxLabel){
16260             cfg.cn.push({
16261                 tag: 'label',
16262                 'for': id,
16263                 cls: 'box-label',
16264                 html: this.boxLabel
16265             })
16266         }
16267         
16268         return cfg;
16269         
16270     },
16271     inputEl: function ()
16272     {
16273         return this.el.select('input.roo-radio',true).first();
16274     },
16275     onClick : function()
16276     {   
16277         this.setChecked(true);
16278     },
16279     
16280     setChecked : function(state,suppressEvent)
16281     {
16282         if(state){
16283             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16284                 v.dom.checked = false;
16285             });
16286         }
16287         
16288         this.checked = state;
16289         this.inputEl().dom.checked = state;
16290         
16291         if(suppressEvent !== true){
16292             this.fireEvent('check', this, state);
16293         }
16294         
16295         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16296         
16297     },
16298     
16299     getGroupValue : function()
16300     {
16301         var value = ''
16302         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16303             if(v.dom.checked == true){
16304                 value = v.dom.value;
16305             }
16306         });
16307         
16308         return value;
16309     },
16310     
16311     /**
16312      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16313      * @return {Mixed} value The field value
16314      */
16315     getValue : function(){
16316         return this.getGroupValue();
16317     }
16318     
16319 });
16320
16321  
16322 //<script type="text/javascript">
16323
16324 /*
16325  * Based  Ext JS Library 1.1.1
16326  * Copyright(c) 2006-2007, Ext JS, LLC.
16327  * LGPL
16328  *
16329  */
16330  
16331 /**
16332  * @class Roo.HtmlEditorCore
16333  * @extends Roo.Component
16334  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16335  *
16336  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16337  */
16338
16339 Roo.HtmlEditorCore = function(config){
16340     
16341     
16342     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16343     
16344     
16345     this.addEvents({
16346         /**
16347          * @event initialize
16348          * Fires when the editor is fully initialized (including the iframe)
16349          * @param {Roo.HtmlEditorCore} this
16350          */
16351         initialize: true,
16352         /**
16353          * @event activate
16354          * Fires when the editor is first receives the focus. Any insertion must wait
16355          * until after this event.
16356          * @param {Roo.HtmlEditorCore} this
16357          */
16358         activate: true,
16359          /**
16360          * @event beforesync
16361          * Fires before the textarea is updated with content from the editor iframe. Return false
16362          * to cancel the sync.
16363          * @param {Roo.HtmlEditorCore} this
16364          * @param {String} html
16365          */
16366         beforesync: true,
16367          /**
16368          * @event beforepush
16369          * Fires before the iframe editor is updated with content from the textarea. Return false
16370          * to cancel the push.
16371          * @param {Roo.HtmlEditorCore} this
16372          * @param {String} html
16373          */
16374         beforepush: true,
16375          /**
16376          * @event sync
16377          * Fires when the textarea is updated with content from the editor iframe.
16378          * @param {Roo.HtmlEditorCore} this
16379          * @param {String} html
16380          */
16381         sync: true,
16382          /**
16383          * @event push
16384          * Fires when the iframe editor is updated with content from the textarea.
16385          * @param {Roo.HtmlEditorCore} this
16386          * @param {String} html
16387          */
16388         push: true,
16389         
16390         /**
16391          * @event editorevent
16392          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16393          * @param {Roo.HtmlEditorCore} this
16394          */
16395         editorevent: true
16396     });
16397     
16398     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16399     
16400     // defaults : white / black...
16401     this.applyBlacklists();
16402     
16403     
16404     
16405 };
16406
16407
16408 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16409
16410
16411      /**
16412      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16413      */
16414     
16415     owner : false,
16416     
16417      /**
16418      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16419      *                        Roo.resizable.
16420      */
16421     resizable : false,
16422      /**
16423      * @cfg {Number} height (in pixels)
16424      */   
16425     height: 300,
16426    /**
16427      * @cfg {Number} width (in pixels)
16428      */   
16429     width: 500,
16430     
16431     /**
16432      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16433      * 
16434      */
16435     stylesheets: false,
16436     
16437     // id of frame..
16438     frameId: false,
16439     
16440     // private properties
16441     validationEvent : false,
16442     deferHeight: true,
16443     initialized : false,
16444     activated : false,
16445     sourceEditMode : false,
16446     onFocus : Roo.emptyFn,
16447     iframePad:3,
16448     hideMode:'offsets',
16449     
16450     clearUp: true,
16451     
16452     // blacklist + whitelisted elements..
16453     black: false,
16454     white: false,
16455      
16456     
16457
16458     /**
16459      * Protected method that will not generally be called directly. It
16460      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16461      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16462      */
16463     getDocMarkup : function(){
16464         // body styles..
16465         var st = '';
16466         Roo.log(this.stylesheets);
16467         
16468         // inherit styels from page...?? 
16469         if (this.stylesheets === false) {
16470             
16471             Roo.get(document.head).select('style').each(function(node) {
16472                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16473             });
16474             
16475             Roo.get(document.head).select('link').each(function(node) { 
16476                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16477             });
16478             
16479         } else if (!this.stylesheets.length) {
16480                 // simple..
16481                 st = '<style type="text/css">' +
16482                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16483                    '</style>';
16484         } else {
16485             Roo.each(this.stylesheets, function(s) {
16486                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16487             });
16488             
16489         }
16490         
16491         st +=  '<style type="text/css">' +
16492             'IMG { cursor: pointer } ' +
16493         '</style>';
16494
16495         
16496         return '<html><head>' + st  +
16497             //<style type="text/css">' +
16498             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16499             //'</style>' +
16500             ' </head><body class="roo-htmleditor-body"></body></html>';
16501     },
16502
16503     // private
16504     onRender : function(ct, position)
16505     {
16506         var _t = this;
16507         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16508         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16509         
16510         
16511         this.el.dom.style.border = '0 none';
16512         this.el.dom.setAttribute('tabIndex', -1);
16513         this.el.addClass('x-hidden hide');
16514         
16515         
16516         
16517         if(Roo.isIE){ // fix IE 1px bogus margin
16518             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16519         }
16520        
16521         
16522         this.frameId = Roo.id();
16523         
16524          
16525         
16526         var iframe = this.owner.wrap.createChild({
16527             tag: 'iframe',
16528             cls: 'form-control', // bootstrap..
16529             id: this.frameId,
16530             name: this.frameId,
16531             frameBorder : 'no',
16532             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16533         }, this.el
16534         );
16535         
16536         
16537         this.iframe = iframe.dom;
16538
16539          this.assignDocWin();
16540         
16541         this.doc.designMode = 'on';
16542        
16543         this.doc.open();
16544         this.doc.write(this.getDocMarkup());
16545         this.doc.close();
16546
16547         
16548         var task = { // must defer to wait for browser to be ready
16549             run : function(){
16550                 //console.log("run task?" + this.doc.readyState);
16551                 this.assignDocWin();
16552                 if(this.doc.body || this.doc.readyState == 'complete'){
16553                     try {
16554                         this.doc.designMode="on";
16555                     } catch (e) {
16556                         return;
16557                     }
16558                     Roo.TaskMgr.stop(task);
16559                     this.initEditor.defer(10, this);
16560                 }
16561             },
16562             interval : 10,
16563             duration: 10000,
16564             scope: this
16565         };
16566         Roo.TaskMgr.start(task);
16567
16568         
16569          
16570     },
16571
16572     // private
16573     onResize : function(w, h)
16574     {
16575          Roo.log('resize: ' +w + ',' + h );
16576         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16577         if(!this.iframe){
16578             return;
16579         }
16580         if(typeof w == 'number'){
16581             
16582             this.iframe.style.width = w + 'px';
16583         }
16584         if(typeof h == 'number'){
16585             
16586             this.iframe.style.height = h + 'px';
16587             if(this.doc){
16588                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16589             }
16590         }
16591         
16592     },
16593
16594     /**
16595      * Toggles the editor between standard and source edit mode.
16596      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16597      */
16598     toggleSourceEdit : function(sourceEditMode){
16599         
16600         this.sourceEditMode = sourceEditMode === true;
16601         
16602         if(this.sourceEditMode){
16603  
16604             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16605             
16606         }else{
16607             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16608             //this.iframe.className = '';
16609             this.deferFocus();
16610         }
16611         //this.setSize(this.owner.wrap.getSize());
16612         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16613     },
16614
16615     
16616   
16617
16618     /**
16619      * Protected method that will not generally be called directly. If you need/want
16620      * custom HTML cleanup, this is the method you should override.
16621      * @param {String} html The HTML to be cleaned
16622      * return {String} The cleaned HTML
16623      */
16624     cleanHtml : function(html){
16625         html = String(html);
16626         if(html.length > 5){
16627             if(Roo.isSafari){ // strip safari nonsense
16628                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16629             }
16630         }
16631         if(html == '&nbsp;'){
16632             html = '';
16633         }
16634         return html;
16635     },
16636
16637     /**
16638      * HTML Editor -> Textarea
16639      * Protected method that will not generally be called directly. Syncs the contents
16640      * of the editor iframe with the textarea.
16641      */
16642     syncValue : function(){
16643         if(this.initialized){
16644             var bd = (this.doc.body || this.doc.documentElement);
16645             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16646             var html = bd.innerHTML;
16647             if(Roo.isSafari){
16648                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16649                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16650                 if(m && m[1]){
16651                     html = '<div style="'+m[0]+'">' + html + '</div>';
16652                 }
16653             }
16654             html = this.cleanHtml(html);
16655             // fix up the special chars.. normaly like back quotes in word...
16656             // however we do not want to do this with chinese..
16657             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16658                 var cc = b.charCodeAt();
16659                 if (
16660                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16661                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16662                     (cc >= 0xf900 && cc < 0xfb00 )
16663                 ) {
16664                         return b;
16665                 }
16666                 return "&#"+cc+";" 
16667             });
16668             if(this.owner.fireEvent('beforesync', this, html) !== false){
16669                 this.el.dom.value = html;
16670                 this.owner.fireEvent('sync', this, html);
16671             }
16672         }
16673     },
16674
16675     /**
16676      * Protected method that will not generally be called directly. Pushes the value of the textarea
16677      * into the iframe editor.
16678      */
16679     pushValue : function(){
16680         if(this.initialized){
16681             var v = this.el.dom.value.trim();
16682             
16683 //            if(v.length < 1){
16684 //                v = '&#160;';
16685 //            }
16686             
16687             if(this.owner.fireEvent('beforepush', this, v) !== false){
16688                 var d = (this.doc.body || this.doc.documentElement);
16689                 d.innerHTML = v;
16690                 this.cleanUpPaste();
16691                 this.el.dom.value = d.innerHTML;
16692                 this.owner.fireEvent('push', this, v);
16693             }
16694         }
16695     },
16696
16697     // private
16698     deferFocus : function(){
16699         this.focus.defer(10, this);
16700     },
16701
16702     // doc'ed in Field
16703     focus : function(){
16704         if(this.win && !this.sourceEditMode){
16705             this.win.focus();
16706         }else{
16707             this.el.focus();
16708         }
16709     },
16710     
16711     assignDocWin: function()
16712     {
16713         var iframe = this.iframe;
16714         
16715          if(Roo.isIE){
16716             this.doc = iframe.contentWindow.document;
16717             this.win = iframe.contentWindow;
16718         } else {
16719 //            if (!Roo.get(this.frameId)) {
16720 //                return;
16721 //            }
16722 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16723 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16724             
16725             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16726                 return;
16727             }
16728             
16729             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16730             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16731         }
16732     },
16733     
16734     // private
16735     initEditor : function(){
16736         //console.log("INIT EDITOR");
16737         this.assignDocWin();
16738         
16739         
16740         
16741         this.doc.designMode="on";
16742         this.doc.open();
16743         this.doc.write(this.getDocMarkup());
16744         this.doc.close();
16745         
16746         var dbody = (this.doc.body || this.doc.documentElement);
16747         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16748         // this copies styles from the containing element into thsi one..
16749         // not sure why we need all of this..
16750         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16751         
16752         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16753         //ss['background-attachment'] = 'fixed'; // w3c
16754         dbody.bgProperties = 'fixed'; // ie
16755         //Roo.DomHelper.applyStyles(dbody, ss);
16756         Roo.EventManager.on(this.doc, {
16757             //'mousedown': this.onEditorEvent,
16758             'mouseup': this.onEditorEvent,
16759             'dblclick': this.onEditorEvent,
16760             'click': this.onEditorEvent,
16761             'keyup': this.onEditorEvent,
16762             buffer:100,
16763             scope: this
16764         });
16765         if(Roo.isGecko){
16766             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16767         }
16768         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16769             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16770         }
16771         this.initialized = true;
16772
16773         this.owner.fireEvent('initialize', this);
16774         this.pushValue();
16775     },
16776
16777     // private
16778     onDestroy : function(){
16779         
16780         
16781         
16782         if(this.rendered){
16783             
16784             //for (var i =0; i < this.toolbars.length;i++) {
16785             //    // fixme - ask toolbars for heights?
16786             //    this.toolbars[i].onDestroy();
16787            // }
16788             
16789             //this.wrap.dom.innerHTML = '';
16790             //this.wrap.remove();
16791         }
16792     },
16793
16794     // private
16795     onFirstFocus : function(){
16796         
16797         this.assignDocWin();
16798         
16799         
16800         this.activated = true;
16801          
16802     
16803         if(Roo.isGecko){ // prevent silly gecko errors
16804             this.win.focus();
16805             var s = this.win.getSelection();
16806             if(!s.focusNode || s.focusNode.nodeType != 3){
16807                 var r = s.getRangeAt(0);
16808                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16809                 r.collapse(true);
16810                 this.deferFocus();
16811             }
16812             try{
16813                 this.execCmd('useCSS', true);
16814                 this.execCmd('styleWithCSS', false);
16815             }catch(e){}
16816         }
16817         this.owner.fireEvent('activate', this);
16818     },
16819
16820     // private
16821     adjustFont: function(btn){
16822         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16823         //if(Roo.isSafari){ // safari
16824         //    adjust *= 2;
16825        // }
16826         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16827         if(Roo.isSafari){ // safari
16828             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16829             v =  (v < 10) ? 10 : v;
16830             v =  (v > 48) ? 48 : v;
16831             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16832             
16833         }
16834         
16835         
16836         v = Math.max(1, v+adjust);
16837         
16838         this.execCmd('FontSize', v  );
16839     },
16840
16841     onEditorEvent : function(e){
16842         this.owner.fireEvent('editorevent', this, e);
16843       //  this.updateToolbar();
16844         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16845     },
16846
16847     insertTag : function(tg)
16848     {
16849         // could be a bit smarter... -> wrap the current selected tRoo..
16850         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16851             
16852             range = this.createRange(this.getSelection());
16853             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16854             wrappingNode.appendChild(range.extractContents());
16855             range.insertNode(wrappingNode);
16856
16857             return;
16858             
16859             
16860             
16861         }
16862         this.execCmd("formatblock",   tg);
16863         
16864     },
16865     
16866     insertText : function(txt)
16867     {
16868         
16869         
16870         var range = this.createRange();
16871         range.deleteContents();
16872                //alert(Sender.getAttribute('label'));
16873                
16874         range.insertNode(this.doc.createTextNode(txt));
16875     } ,
16876     
16877      
16878
16879     /**
16880      * Executes a Midas editor command on the editor document and performs necessary focus and
16881      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16882      * @param {String} cmd The Midas command
16883      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16884      */
16885     relayCmd : function(cmd, value){
16886         this.win.focus();
16887         this.execCmd(cmd, value);
16888         this.owner.fireEvent('editorevent', this);
16889         //this.updateToolbar();
16890         this.owner.deferFocus();
16891     },
16892
16893     /**
16894      * Executes a Midas editor command directly on the editor document.
16895      * For visual commands, you should use {@link #relayCmd} instead.
16896      * <b>This should only be called after the editor is initialized.</b>
16897      * @param {String} cmd The Midas command
16898      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16899      */
16900     execCmd : function(cmd, value){
16901         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16902         this.syncValue();
16903     },
16904  
16905  
16906    
16907     /**
16908      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16909      * to insert tRoo.
16910      * @param {String} text | dom node.. 
16911      */
16912     insertAtCursor : function(text)
16913     {
16914         
16915         
16916         
16917         if(!this.activated){
16918             return;
16919         }
16920         /*
16921         if(Roo.isIE){
16922             this.win.focus();
16923             var r = this.doc.selection.createRange();
16924             if(r){
16925                 r.collapse(true);
16926                 r.pasteHTML(text);
16927                 this.syncValue();
16928                 this.deferFocus();
16929             
16930             }
16931             return;
16932         }
16933         */
16934         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16935             this.win.focus();
16936             
16937             
16938             // from jquery ui (MIT licenced)
16939             var range, node;
16940             var win = this.win;
16941             
16942             if (win.getSelection && win.getSelection().getRangeAt) {
16943                 range = win.getSelection().getRangeAt(0);
16944                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16945                 range.insertNode(node);
16946             } else if (win.document.selection && win.document.selection.createRange) {
16947                 // no firefox support
16948                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16949                 win.document.selection.createRange().pasteHTML(txt);
16950             } else {
16951                 // no firefox support
16952                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16953                 this.execCmd('InsertHTML', txt);
16954             } 
16955             
16956             this.syncValue();
16957             
16958             this.deferFocus();
16959         }
16960     },
16961  // private
16962     mozKeyPress : function(e){
16963         if(e.ctrlKey){
16964             var c = e.getCharCode(), cmd;
16965           
16966             if(c > 0){
16967                 c = String.fromCharCode(c).toLowerCase();
16968                 switch(c){
16969                     case 'b':
16970                         cmd = 'bold';
16971                         break;
16972                     case 'i':
16973                         cmd = 'italic';
16974                         break;
16975                     
16976                     case 'u':
16977                         cmd = 'underline';
16978                         break;
16979                     
16980                     case 'v':
16981                         this.cleanUpPaste.defer(100, this);
16982                         return;
16983                         
16984                 }
16985                 if(cmd){
16986                     this.win.focus();
16987                     this.execCmd(cmd);
16988                     this.deferFocus();
16989                     e.preventDefault();
16990                 }
16991                 
16992             }
16993         }
16994     },
16995
16996     // private
16997     fixKeys : function(){ // load time branching for fastest keydown performance
16998         if(Roo.isIE){
16999             return function(e){
17000                 var k = e.getKey(), r;
17001                 if(k == e.TAB){
17002                     e.stopEvent();
17003                     r = this.doc.selection.createRange();
17004                     if(r){
17005                         r.collapse(true);
17006                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17007                         this.deferFocus();
17008                     }
17009                     return;
17010                 }
17011                 
17012                 if(k == e.ENTER){
17013                     r = this.doc.selection.createRange();
17014                     if(r){
17015                         var target = r.parentElement();
17016                         if(!target || target.tagName.toLowerCase() != 'li'){
17017                             e.stopEvent();
17018                             r.pasteHTML('<br />');
17019                             r.collapse(false);
17020                             r.select();
17021                         }
17022                     }
17023                 }
17024                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17025                     this.cleanUpPaste.defer(100, this);
17026                     return;
17027                 }
17028                 
17029                 
17030             };
17031         }else if(Roo.isOpera){
17032             return function(e){
17033                 var k = e.getKey();
17034                 if(k == e.TAB){
17035                     e.stopEvent();
17036                     this.win.focus();
17037                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17038                     this.deferFocus();
17039                 }
17040                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17041                     this.cleanUpPaste.defer(100, this);
17042                     return;
17043                 }
17044                 
17045             };
17046         }else if(Roo.isSafari){
17047             return function(e){
17048                 var k = e.getKey();
17049                 
17050                 if(k == e.TAB){
17051                     e.stopEvent();
17052                     this.execCmd('InsertText','\t');
17053                     this.deferFocus();
17054                     return;
17055                 }
17056                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17057                     this.cleanUpPaste.defer(100, this);
17058                     return;
17059                 }
17060                 
17061              };
17062         }
17063     }(),
17064     
17065     getAllAncestors: function()
17066     {
17067         var p = this.getSelectedNode();
17068         var a = [];
17069         if (!p) {
17070             a.push(p); // push blank onto stack..
17071             p = this.getParentElement();
17072         }
17073         
17074         
17075         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17076             a.push(p);
17077             p = p.parentNode;
17078         }
17079         a.push(this.doc.body);
17080         return a;
17081     },
17082     lastSel : false,
17083     lastSelNode : false,
17084     
17085     
17086     getSelection : function() 
17087     {
17088         this.assignDocWin();
17089         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17090     },
17091     
17092     getSelectedNode: function() 
17093     {
17094         // this may only work on Gecko!!!
17095         
17096         // should we cache this!!!!
17097         
17098         
17099         
17100          
17101         var range = this.createRange(this.getSelection()).cloneRange();
17102         
17103         if (Roo.isIE) {
17104             var parent = range.parentElement();
17105             while (true) {
17106                 var testRange = range.duplicate();
17107                 testRange.moveToElementText(parent);
17108                 if (testRange.inRange(range)) {
17109                     break;
17110                 }
17111                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17112                     break;
17113                 }
17114                 parent = parent.parentElement;
17115             }
17116             return parent;
17117         }
17118         
17119         // is ancestor a text element.
17120         var ac =  range.commonAncestorContainer;
17121         if (ac.nodeType == 3) {
17122             ac = ac.parentNode;
17123         }
17124         
17125         var ar = ac.childNodes;
17126          
17127         var nodes = [];
17128         var other_nodes = [];
17129         var has_other_nodes = false;
17130         for (var i=0;i<ar.length;i++) {
17131             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17132                 continue;
17133             }
17134             // fullly contained node.
17135             
17136             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17137                 nodes.push(ar[i]);
17138                 continue;
17139             }
17140             
17141             // probably selected..
17142             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17143                 other_nodes.push(ar[i]);
17144                 continue;
17145             }
17146             // outer..
17147             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17148                 continue;
17149             }
17150             
17151             
17152             has_other_nodes = true;
17153         }
17154         if (!nodes.length && other_nodes.length) {
17155             nodes= other_nodes;
17156         }
17157         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17158             return false;
17159         }
17160         
17161         return nodes[0];
17162     },
17163     createRange: function(sel)
17164     {
17165         // this has strange effects when using with 
17166         // top toolbar - not sure if it's a great idea.
17167         //this.editor.contentWindow.focus();
17168         if (typeof sel != "undefined") {
17169             try {
17170                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17171             } catch(e) {
17172                 return this.doc.createRange();
17173             }
17174         } else {
17175             return this.doc.createRange();
17176         }
17177     },
17178     getParentElement: function()
17179     {
17180         
17181         this.assignDocWin();
17182         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17183         
17184         var range = this.createRange(sel);
17185          
17186         try {
17187             var p = range.commonAncestorContainer;
17188             while (p.nodeType == 3) { // text node
17189                 p = p.parentNode;
17190             }
17191             return p;
17192         } catch (e) {
17193             return null;
17194         }
17195     
17196     },
17197     /***
17198      *
17199      * Range intersection.. the hard stuff...
17200      *  '-1' = before
17201      *  '0' = hits..
17202      *  '1' = after.
17203      *         [ -- selected range --- ]
17204      *   [fail]                        [fail]
17205      *
17206      *    basically..
17207      *      if end is before start or  hits it. fail.
17208      *      if start is after end or hits it fail.
17209      *
17210      *   if either hits (but other is outside. - then it's not 
17211      *   
17212      *    
17213      **/
17214     
17215     
17216     // @see http://www.thismuchiknow.co.uk/?p=64.
17217     rangeIntersectsNode : function(range, node)
17218     {
17219         var nodeRange = node.ownerDocument.createRange();
17220         try {
17221             nodeRange.selectNode(node);
17222         } catch (e) {
17223             nodeRange.selectNodeContents(node);
17224         }
17225     
17226         var rangeStartRange = range.cloneRange();
17227         rangeStartRange.collapse(true);
17228     
17229         var rangeEndRange = range.cloneRange();
17230         rangeEndRange.collapse(false);
17231     
17232         var nodeStartRange = nodeRange.cloneRange();
17233         nodeStartRange.collapse(true);
17234     
17235         var nodeEndRange = nodeRange.cloneRange();
17236         nodeEndRange.collapse(false);
17237     
17238         return rangeStartRange.compareBoundaryPoints(
17239                  Range.START_TO_START, nodeEndRange) == -1 &&
17240                rangeEndRange.compareBoundaryPoints(
17241                  Range.START_TO_START, nodeStartRange) == 1;
17242         
17243          
17244     },
17245     rangeCompareNode : function(range, node)
17246     {
17247         var nodeRange = node.ownerDocument.createRange();
17248         try {
17249             nodeRange.selectNode(node);
17250         } catch (e) {
17251             nodeRange.selectNodeContents(node);
17252         }
17253         
17254         
17255         range.collapse(true);
17256     
17257         nodeRange.collapse(true);
17258      
17259         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17260         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17261          
17262         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17263         
17264         var nodeIsBefore   =  ss == 1;
17265         var nodeIsAfter    = ee == -1;
17266         
17267         if (nodeIsBefore && nodeIsAfter)
17268             return 0; // outer
17269         if (!nodeIsBefore && nodeIsAfter)
17270             return 1; //right trailed.
17271         
17272         if (nodeIsBefore && !nodeIsAfter)
17273             return 2;  // left trailed.
17274         // fully contined.
17275         return 3;
17276     },
17277
17278     // private? - in a new class?
17279     cleanUpPaste :  function()
17280     {
17281         // cleans up the whole document..
17282         Roo.log('cleanuppaste');
17283         
17284         this.cleanUpChildren(this.doc.body);
17285         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17286         if (clean != this.doc.body.innerHTML) {
17287             this.doc.body.innerHTML = clean;
17288         }
17289         
17290     },
17291     
17292     cleanWordChars : function(input) {// change the chars to hex code
17293         var he = Roo.HtmlEditorCore;
17294         
17295         var output = input;
17296         Roo.each(he.swapCodes, function(sw) { 
17297             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17298             
17299             output = output.replace(swapper, sw[1]);
17300         });
17301         
17302         return output;
17303     },
17304     
17305     
17306     cleanUpChildren : function (n)
17307     {
17308         if (!n.childNodes.length) {
17309             return;
17310         }
17311         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17312            this.cleanUpChild(n.childNodes[i]);
17313         }
17314     },
17315     
17316     
17317         
17318     
17319     cleanUpChild : function (node)
17320     {
17321         var ed = this;
17322         //console.log(node);
17323         if (node.nodeName == "#text") {
17324             // clean up silly Windows -- stuff?
17325             return; 
17326         }
17327         if (node.nodeName == "#comment") {
17328             node.parentNode.removeChild(node);
17329             // clean up silly Windows -- stuff?
17330             return; 
17331         }
17332         var lcname = node.tagName.toLowerCase();
17333         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17334         // whitelist of tags..
17335         
17336         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17337             // remove node.
17338             node.parentNode.removeChild(node);
17339             return;
17340             
17341         }
17342         
17343         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17344         
17345         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17346         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17347         
17348         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17349         //    remove_keep_children = true;
17350         //}
17351         
17352         if (remove_keep_children) {
17353             this.cleanUpChildren(node);
17354             // inserts everything just before this node...
17355             while (node.childNodes.length) {
17356                 var cn = node.childNodes[0];
17357                 node.removeChild(cn);
17358                 node.parentNode.insertBefore(cn, node);
17359             }
17360             node.parentNode.removeChild(node);
17361             return;
17362         }
17363         
17364         if (!node.attributes || !node.attributes.length) {
17365             this.cleanUpChildren(node);
17366             return;
17367         }
17368         
17369         function cleanAttr(n,v)
17370         {
17371             
17372             if (v.match(/^\./) || v.match(/^\//)) {
17373                 return;
17374             }
17375             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17376                 return;
17377             }
17378             if (v.match(/^#/)) {
17379                 return;
17380             }
17381 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17382             node.removeAttribute(n);
17383             
17384         }
17385         
17386         var cwhite = this.cwhite;
17387         var cblack = this.cblack;
17388             
17389         function cleanStyle(n,v)
17390         {
17391             if (v.match(/expression/)) { //XSS?? should we even bother..
17392                 node.removeAttribute(n);
17393                 return;
17394             }
17395             
17396             var parts = v.split(/;/);
17397             var clean = [];
17398             
17399             Roo.each(parts, function(p) {
17400                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17401                 if (!p.length) {
17402                     return true;
17403                 }
17404                 var l = p.split(':').shift().replace(/\s+/g,'');
17405                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17406                 
17407                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17408 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17409                     //node.removeAttribute(n);
17410                     return true;
17411                 }
17412                 //Roo.log()
17413                 // only allow 'c whitelisted system attributes'
17414                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17415 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17416                     //node.removeAttribute(n);
17417                     return true;
17418                 }
17419                 
17420                 
17421                  
17422                 
17423                 clean.push(p);
17424                 return true;
17425             });
17426             if (clean.length) { 
17427                 node.setAttribute(n, clean.join(';'));
17428             } else {
17429                 node.removeAttribute(n);
17430             }
17431             
17432         }
17433         
17434         
17435         for (var i = node.attributes.length-1; i > -1 ; i--) {
17436             var a = node.attributes[i];
17437             //console.log(a);
17438             
17439             if (a.name.toLowerCase().substr(0,2)=='on')  {
17440                 node.removeAttribute(a.name);
17441                 continue;
17442             }
17443             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17444                 node.removeAttribute(a.name);
17445                 continue;
17446             }
17447             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17448                 cleanAttr(a.name,a.value); // fixme..
17449                 continue;
17450             }
17451             if (a.name == 'style') {
17452                 cleanStyle(a.name,a.value);
17453                 continue;
17454             }
17455             /// clean up MS crap..
17456             // tecnically this should be a list of valid class'es..
17457             
17458             
17459             if (a.name == 'class') {
17460                 if (a.value.match(/^Mso/)) {
17461                     node.className = '';
17462                 }
17463                 
17464                 if (a.value.match(/body/)) {
17465                     node.className = '';
17466                 }
17467                 continue;
17468             }
17469             
17470             // style cleanup!?
17471             // class cleanup?
17472             
17473         }
17474         
17475         
17476         this.cleanUpChildren(node);
17477         
17478         
17479     },
17480     /**
17481      * Clean up MS wordisms...
17482      */
17483     cleanWord : function(node)
17484     {
17485         var _t = this;
17486         var cleanWordChildren = function()
17487         {
17488             if (!node.childNodes.length) {
17489                 return;
17490             }
17491             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17492                _t.cleanWord(node.childNodes[i]);
17493             }
17494         }
17495         
17496         
17497         if (!node) {
17498             this.cleanWord(this.doc.body);
17499             return;
17500         }
17501         if (node.nodeName == "#text") {
17502             // clean up silly Windows -- stuff?
17503             return; 
17504         }
17505         if (node.nodeName == "#comment") {
17506             node.parentNode.removeChild(node);
17507             // clean up silly Windows -- stuff?
17508             return; 
17509         }
17510         
17511         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17512             node.parentNode.removeChild(node);
17513             return;
17514         }
17515         
17516         // remove - but keep children..
17517         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17518             while (node.childNodes.length) {
17519                 var cn = node.childNodes[0];
17520                 node.removeChild(cn);
17521                 node.parentNode.insertBefore(cn, node);
17522             }
17523             node.parentNode.removeChild(node);
17524             cleanWordChildren();
17525             return;
17526         }
17527         // clean styles
17528         if (node.className.length) {
17529             
17530             var cn = node.className.split(/\W+/);
17531             var cna = [];
17532             Roo.each(cn, function(cls) {
17533                 if (cls.match(/Mso[a-zA-Z]+/)) {
17534                     return;
17535                 }
17536                 cna.push(cls);
17537             });
17538             node.className = cna.length ? cna.join(' ') : '';
17539             if (!cna.length) {
17540                 node.removeAttribute("class");
17541             }
17542         }
17543         
17544         if (node.hasAttribute("lang")) {
17545             node.removeAttribute("lang");
17546         }
17547         
17548         if (node.hasAttribute("style")) {
17549             
17550             var styles = node.getAttribute("style").split(";");
17551             var nstyle = [];
17552             Roo.each(styles, function(s) {
17553                 if (!s.match(/:/)) {
17554                     return;
17555                 }
17556                 var kv = s.split(":");
17557                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17558                     return;
17559                 }
17560                 // what ever is left... we allow.
17561                 nstyle.push(s);
17562             });
17563             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17564             if (!nstyle.length) {
17565                 node.removeAttribute('style');
17566             }
17567         }
17568         
17569         cleanWordChildren();
17570         
17571         
17572     },
17573     domToHTML : function(currentElement, depth, nopadtext) {
17574         
17575         depth = depth || 0;
17576         nopadtext = nopadtext || false;
17577     
17578         if (!currentElement) {
17579             return this.domToHTML(this.doc.body);
17580         }
17581         
17582         //Roo.log(currentElement);
17583         var j;
17584         var allText = false;
17585         var nodeName = currentElement.nodeName;
17586         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17587         
17588         if  (nodeName == '#text') {
17589             return currentElement.nodeValue;
17590         }
17591         
17592         
17593         var ret = '';
17594         if (nodeName != 'BODY') {
17595              
17596             var i = 0;
17597             // Prints the node tagName, such as <A>, <IMG>, etc
17598             if (tagName) {
17599                 var attr = [];
17600                 for(i = 0; i < currentElement.attributes.length;i++) {
17601                     // quoting?
17602                     var aname = currentElement.attributes.item(i).name;
17603                     if (!currentElement.attributes.item(i).value.length) {
17604                         continue;
17605                     }
17606                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17607                 }
17608                 
17609                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17610             } 
17611             else {
17612                 
17613                 // eack
17614             }
17615         } else {
17616             tagName = false;
17617         }
17618         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17619             return ret;
17620         }
17621         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17622             nopadtext = true;
17623         }
17624         
17625         
17626         // Traverse the tree
17627         i = 0;
17628         var currentElementChild = currentElement.childNodes.item(i);
17629         var allText = true;
17630         var innerHTML  = '';
17631         lastnode = '';
17632         while (currentElementChild) {
17633             // Formatting code (indent the tree so it looks nice on the screen)
17634             var nopad = nopadtext;
17635             if (lastnode == 'SPAN') {
17636                 nopad  = true;
17637             }
17638             // text
17639             if  (currentElementChild.nodeName == '#text') {
17640                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17641                 if (!nopad && toadd.length > 80) {
17642                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17643                 }
17644                 innerHTML  += toadd;
17645                 
17646                 i++;
17647                 currentElementChild = currentElement.childNodes.item(i);
17648                 lastNode = '';
17649                 continue;
17650             }
17651             allText = false;
17652             
17653             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17654                 
17655             // Recursively traverse the tree structure of the child node
17656             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17657             lastnode = currentElementChild.nodeName;
17658             i++;
17659             currentElementChild=currentElement.childNodes.item(i);
17660         }
17661         
17662         ret += innerHTML;
17663         
17664         if (!allText) {
17665                 // The remaining code is mostly for formatting the tree
17666             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17667         }
17668         
17669         
17670         if (tagName) {
17671             ret+= "</"+tagName+">";
17672         }
17673         return ret;
17674         
17675     },
17676         
17677     applyBlacklists : function()
17678     {
17679         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17680         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17681         
17682         this.white = [];
17683         this.black = [];
17684         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17685             if (b.indexOf(tag) > -1) {
17686                 return;
17687             }
17688             this.white.push(tag);
17689             
17690         }, this);
17691         
17692         Roo.each(w, function(tag) {
17693             if (b.indexOf(tag) > -1) {
17694                 return;
17695             }
17696             if (this.white.indexOf(tag) > -1) {
17697                 return;
17698             }
17699             this.white.push(tag);
17700             
17701         }, this);
17702         
17703         
17704         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17705             if (w.indexOf(tag) > -1) {
17706                 return;
17707             }
17708             this.black.push(tag);
17709             
17710         }, this);
17711         
17712         Roo.each(b, function(tag) {
17713             if (w.indexOf(tag) > -1) {
17714                 return;
17715             }
17716             if (this.black.indexOf(tag) > -1) {
17717                 return;
17718             }
17719             this.black.push(tag);
17720             
17721         }, this);
17722         
17723         
17724         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17725         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17726         
17727         this.cwhite = [];
17728         this.cblack = [];
17729         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17730             if (b.indexOf(tag) > -1) {
17731                 return;
17732             }
17733             this.cwhite.push(tag);
17734             
17735         }, this);
17736         
17737         Roo.each(w, function(tag) {
17738             if (b.indexOf(tag) > -1) {
17739                 return;
17740             }
17741             if (this.cwhite.indexOf(tag) > -1) {
17742                 return;
17743             }
17744             this.cwhite.push(tag);
17745             
17746         }, this);
17747         
17748         
17749         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17750             if (w.indexOf(tag) > -1) {
17751                 return;
17752             }
17753             this.cblack.push(tag);
17754             
17755         }, this);
17756         
17757         Roo.each(b, function(tag) {
17758             if (w.indexOf(tag) > -1) {
17759                 return;
17760             }
17761             if (this.cblack.indexOf(tag) > -1) {
17762                 return;
17763             }
17764             this.cblack.push(tag);
17765             
17766         }, this);
17767     }
17768     
17769     // hide stuff that is not compatible
17770     /**
17771      * @event blur
17772      * @hide
17773      */
17774     /**
17775      * @event change
17776      * @hide
17777      */
17778     /**
17779      * @event focus
17780      * @hide
17781      */
17782     /**
17783      * @event specialkey
17784      * @hide
17785      */
17786     /**
17787      * @cfg {String} fieldClass @hide
17788      */
17789     /**
17790      * @cfg {String} focusClass @hide
17791      */
17792     /**
17793      * @cfg {String} autoCreate @hide
17794      */
17795     /**
17796      * @cfg {String} inputType @hide
17797      */
17798     /**
17799      * @cfg {String} invalidClass @hide
17800      */
17801     /**
17802      * @cfg {String} invalidText @hide
17803      */
17804     /**
17805      * @cfg {String} msgFx @hide
17806      */
17807     /**
17808      * @cfg {String} validateOnBlur @hide
17809      */
17810 });
17811
17812 Roo.HtmlEditorCore.white = [
17813         'area', 'br', 'img', 'input', 'hr', 'wbr',
17814         
17815        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17816        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17817        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17818        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17819        'table',   'ul',         'xmp', 
17820        
17821        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17822       'thead',   'tr', 
17823      
17824       'dir', 'menu', 'ol', 'ul', 'dl',
17825        
17826       'embed',  'object'
17827 ];
17828
17829
17830 Roo.HtmlEditorCore.black = [
17831     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17832         'applet', // 
17833         'base',   'basefont', 'bgsound', 'blink',  'body', 
17834         'frame',  'frameset', 'head',    'html',   'ilayer', 
17835         'iframe', 'layer',  'link',     'meta',    'object',   
17836         'script', 'style' ,'title',  'xml' // clean later..
17837 ];
17838 Roo.HtmlEditorCore.clean = [
17839     'script', 'style', 'title', 'xml'
17840 ];
17841 Roo.HtmlEditorCore.remove = [
17842     'font'
17843 ];
17844 // attributes..
17845
17846 Roo.HtmlEditorCore.ablack = [
17847     'on'
17848 ];
17849     
17850 Roo.HtmlEditorCore.aclean = [ 
17851     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17852 ];
17853
17854 // protocols..
17855 Roo.HtmlEditorCore.pwhite= [
17856         'http',  'https',  'mailto'
17857 ];
17858
17859 // white listed style attributes.
17860 Roo.HtmlEditorCore.cwhite= [
17861       //  'text-align', /// default is to allow most things..
17862       
17863          
17864 //        'font-size'//??
17865 ];
17866
17867 // black listed style attributes.
17868 Roo.HtmlEditorCore.cblack= [
17869       //  'font-size' -- this can be set by the project 
17870 ];
17871
17872
17873 Roo.HtmlEditorCore.swapCodes   =[ 
17874     [    8211, "--" ], 
17875     [    8212, "--" ], 
17876     [    8216,  "'" ],  
17877     [    8217, "'" ],  
17878     [    8220, '"' ],  
17879     [    8221, '"' ],  
17880     [    8226, "*" ],  
17881     [    8230, "..." ]
17882 ]; 
17883
17884     /*
17885  * - LGPL
17886  *
17887  * HtmlEditor
17888  * 
17889  */
17890
17891 /**
17892  * @class Roo.bootstrap.HtmlEditor
17893  * @extends Roo.bootstrap.TextArea
17894  * Bootstrap HtmlEditor class
17895
17896  * @constructor
17897  * Create a new HtmlEditor
17898  * @param {Object} config The config object
17899  */
17900
17901 Roo.bootstrap.HtmlEditor = function(config){
17902     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17903     if (!this.toolbars) {
17904         this.toolbars = [];
17905     }
17906     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17907     this.addEvents({
17908             /**
17909              * @event initialize
17910              * Fires when the editor is fully initialized (including the iframe)
17911              * @param {HtmlEditor} this
17912              */
17913             initialize: true,
17914             /**
17915              * @event activate
17916              * Fires when the editor is first receives the focus. Any insertion must wait
17917              * until after this event.
17918              * @param {HtmlEditor} this
17919              */
17920             activate: true,
17921              /**
17922              * @event beforesync
17923              * Fires before the textarea is updated with content from the editor iframe. Return false
17924              * to cancel the sync.
17925              * @param {HtmlEditor} this
17926              * @param {String} html
17927              */
17928             beforesync: true,
17929              /**
17930              * @event beforepush
17931              * Fires before the iframe editor is updated with content from the textarea. Return false
17932              * to cancel the push.
17933              * @param {HtmlEditor} this
17934              * @param {String} html
17935              */
17936             beforepush: true,
17937              /**
17938              * @event sync
17939              * Fires when the textarea is updated with content from the editor iframe.
17940              * @param {HtmlEditor} this
17941              * @param {String} html
17942              */
17943             sync: true,
17944              /**
17945              * @event push
17946              * Fires when the iframe editor is updated with content from the textarea.
17947              * @param {HtmlEditor} this
17948              * @param {String} html
17949              */
17950             push: true,
17951              /**
17952              * @event editmodechange
17953              * Fires when the editor switches edit modes
17954              * @param {HtmlEditor} this
17955              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17956              */
17957             editmodechange: true,
17958             /**
17959              * @event editorevent
17960              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17961              * @param {HtmlEditor} this
17962              */
17963             editorevent: true,
17964             /**
17965              * @event firstfocus
17966              * Fires when on first focus - needed by toolbars..
17967              * @param {HtmlEditor} this
17968              */
17969             firstfocus: true,
17970             /**
17971              * @event autosave
17972              * Auto save the htmlEditor value as a file into Events
17973              * @param {HtmlEditor} this
17974              */
17975             autosave: true,
17976             /**
17977              * @event savedpreview
17978              * preview the saved version of htmlEditor
17979              * @param {HtmlEditor} this
17980              */
17981             savedpreview: true
17982         });
17983 };
17984
17985
17986 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17987     
17988     
17989       /**
17990      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17991      */
17992     toolbars : false,
17993    
17994      /**
17995      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17996      *                        Roo.resizable.
17997      */
17998     resizable : false,
17999      /**
18000      * @cfg {Number} height (in pixels)
18001      */   
18002     height: 300,
18003    /**
18004      * @cfg {Number} width (in pixels)
18005      */   
18006     width: false,
18007     
18008     /**
18009      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18010      * 
18011      */
18012     stylesheets: false,
18013     
18014     // id of frame..
18015     frameId: false,
18016     
18017     // private properties
18018     validationEvent : false,
18019     deferHeight: true,
18020     initialized : false,
18021     activated : false,
18022     
18023     onFocus : Roo.emptyFn,
18024     iframePad:3,
18025     hideMode:'offsets',
18026     
18027     
18028     tbContainer : false,
18029     
18030     toolbarContainer :function() {
18031         return this.wrap.select('.x-html-editor-tb',true).first();
18032     },
18033
18034     /**
18035      * Protected method that will not generally be called directly. It
18036      * is called when the editor creates its toolbar. Override this method if you need to
18037      * add custom toolbar buttons.
18038      * @param {HtmlEditor} editor
18039      */
18040     createToolbar : function(){
18041         
18042         Roo.log("create toolbars");
18043         
18044         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18045         this.toolbars[0].render(this.toolbarContainer());
18046         
18047         return;
18048         
18049 //        if (!editor.toolbars || !editor.toolbars.length) {
18050 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18051 //        }
18052 //        
18053 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18054 //            editor.toolbars[i] = Roo.factory(
18055 //                    typeof(editor.toolbars[i]) == 'string' ?
18056 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18057 //                Roo.bootstrap.HtmlEditor);
18058 //            editor.toolbars[i].init(editor);
18059 //        }
18060     },
18061
18062      
18063     // private
18064     onRender : function(ct, position)
18065     {
18066        // Roo.log("Call onRender: " + this.xtype);
18067         var _t = this;
18068         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18069       
18070         this.wrap = this.inputEl().wrap({
18071             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18072         });
18073         
18074         this.editorcore.onRender(ct, position);
18075          
18076         if (this.resizable) {
18077             this.resizeEl = new Roo.Resizable(this.wrap, {
18078                 pinned : true,
18079                 wrap: true,
18080                 dynamic : true,
18081                 minHeight : this.height,
18082                 height: this.height,
18083                 handles : this.resizable,
18084                 width: this.width,
18085                 listeners : {
18086                     resize : function(r, w, h) {
18087                         _t.onResize(w,h); // -something
18088                     }
18089                 }
18090             });
18091             
18092         }
18093         this.createToolbar(this);
18094        
18095         
18096         if(!this.width && this.resizable){
18097             this.setSize(this.wrap.getSize());
18098         }
18099         if (this.resizeEl) {
18100             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18101             // should trigger onReize..
18102         }
18103         
18104     },
18105
18106     // private
18107     onResize : function(w, h)
18108     {
18109         Roo.log('resize: ' +w + ',' + h );
18110         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18111         var ew = false;
18112         var eh = false;
18113         
18114         if(this.inputEl() ){
18115             if(typeof w == 'number'){
18116                 var aw = w - this.wrap.getFrameWidth('lr');
18117                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18118                 ew = aw;
18119             }
18120             if(typeof h == 'number'){
18121                  var tbh = -11;  // fixme it needs to tool bar size!
18122                 for (var i =0; i < this.toolbars.length;i++) {
18123                     // fixme - ask toolbars for heights?
18124                     tbh += this.toolbars[i].el.getHeight();
18125                     //if (this.toolbars[i].footer) {
18126                     //    tbh += this.toolbars[i].footer.el.getHeight();
18127                     //}
18128                 }
18129               
18130                 
18131                 
18132                 
18133                 
18134                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18135                 ah -= 5; // knock a few pixes off for look..
18136                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18137                 var eh = ah;
18138             }
18139         }
18140         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18141         this.editorcore.onResize(ew,eh);
18142         
18143     },
18144
18145     /**
18146      * Toggles the editor between standard and source edit mode.
18147      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18148      */
18149     toggleSourceEdit : function(sourceEditMode)
18150     {
18151         this.editorcore.toggleSourceEdit(sourceEditMode);
18152         
18153         if(this.editorcore.sourceEditMode){
18154             Roo.log('editor - showing textarea');
18155             
18156 //            Roo.log('in');
18157 //            Roo.log(this.syncValue());
18158             this.syncValue();
18159             this.inputEl().removeClass(['hide', 'x-hidden']);
18160             this.inputEl().dom.removeAttribute('tabIndex');
18161             this.inputEl().focus();
18162         }else{
18163             Roo.log('editor - hiding textarea');
18164 //            Roo.log('out')
18165 //            Roo.log(this.pushValue()); 
18166             this.pushValue();
18167             
18168             this.inputEl().addClass(['hide', 'x-hidden']);
18169             this.inputEl().dom.setAttribute('tabIndex', -1);
18170             //this.deferFocus();
18171         }
18172          
18173         if(this.resizable){
18174             this.setSize(this.wrap.getSize());
18175         }
18176         
18177         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18178     },
18179  
18180     // private (for BoxComponent)
18181     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18182
18183     // private (for BoxComponent)
18184     getResizeEl : function(){
18185         return this.wrap;
18186     },
18187
18188     // private (for BoxComponent)
18189     getPositionEl : function(){
18190         return this.wrap;
18191     },
18192
18193     // private
18194     initEvents : function(){
18195         this.originalValue = this.getValue();
18196     },
18197
18198 //    /**
18199 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18200 //     * @method
18201 //     */
18202 //    markInvalid : Roo.emptyFn,
18203 //    /**
18204 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18205 //     * @method
18206 //     */
18207 //    clearInvalid : Roo.emptyFn,
18208
18209     setValue : function(v){
18210         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18211         this.editorcore.pushValue();
18212     },
18213
18214      
18215     // private
18216     deferFocus : function(){
18217         this.focus.defer(10, this);
18218     },
18219
18220     // doc'ed in Field
18221     focus : function(){
18222         this.editorcore.focus();
18223         
18224     },
18225       
18226
18227     // private
18228     onDestroy : function(){
18229         
18230         
18231         
18232         if(this.rendered){
18233             
18234             for (var i =0; i < this.toolbars.length;i++) {
18235                 // fixme - ask toolbars for heights?
18236                 this.toolbars[i].onDestroy();
18237             }
18238             
18239             this.wrap.dom.innerHTML = '';
18240             this.wrap.remove();
18241         }
18242     },
18243
18244     // private
18245     onFirstFocus : function(){
18246         //Roo.log("onFirstFocus");
18247         this.editorcore.onFirstFocus();
18248          for (var i =0; i < this.toolbars.length;i++) {
18249             this.toolbars[i].onFirstFocus();
18250         }
18251         
18252     },
18253     
18254     // private
18255     syncValue : function()
18256     {   
18257         this.editorcore.syncValue();
18258     },
18259     
18260     pushValue : function()
18261     {   
18262         this.editorcore.pushValue();
18263     }
18264      
18265     
18266     // hide stuff that is not compatible
18267     /**
18268      * @event blur
18269      * @hide
18270      */
18271     /**
18272      * @event change
18273      * @hide
18274      */
18275     /**
18276      * @event focus
18277      * @hide
18278      */
18279     /**
18280      * @event specialkey
18281      * @hide
18282      */
18283     /**
18284      * @cfg {String} fieldClass @hide
18285      */
18286     /**
18287      * @cfg {String} focusClass @hide
18288      */
18289     /**
18290      * @cfg {String} autoCreate @hide
18291      */
18292     /**
18293      * @cfg {String} inputType @hide
18294      */
18295     /**
18296      * @cfg {String} invalidClass @hide
18297      */
18298     /**
18299      * @cfg {String} invalidText @hide
18300      */
18301     /**
18302      * @cfg {String} msgFx @hide
18303      */
18304     /**
18305      * @cfg {String} validateOnBlur @hide
18306      */
18307 });
18308  
18309     
18310    
18311    
18312    
18313       
18314 Roo.namespace('Roo.bootstrap.htmleditor');
18315 /**
18316  * @class Roo.bootstrap.HtmlEditorToolbar1
18317  * Basic Toolbar
18318  * 
18319  * Usage:
18320  *
18321  new Roo.bootstrap.HtmlEditor({
18322     ....
18323     toolbars : [
18324         new Roo.bootstrap.HtmlEditorToolbar1({
18325             disable : { fonts: 1 , format: 1, ..., ... , ...],
18326             btns : [ .... ]
18327         })
18328     }
18329      
18330  * 
18331  * @cfg {Object} disable List of elements to disable..
18332  * @cfg {Array} btns List of additional buttons.
18333  * 
18334  * 
18335  * NEEDS Extra CSS? 
18336  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18337  */
18338  
18339 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18340 {
18341     
18342     Roo.apply(this, config);
18343     
18344     // default disabled, based on 'good practice'..
18345     this.disable = this.disable || {};
18346     Roo.applyIf(this.disable, {
18347         fontSize : true,
18348         colors : true,
18349         specialElements : true
18350     });
18351     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18352     
18353     this.editor = config.editor;
18354     this.editorcore = config.editor.editorcore;
18355     
18356     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18357     
18358     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18359     // dont call parent... till later.
18360 }
18361 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18362      
18363     bar : true,
18364     
18365     editor : false,
18366     editorcore : false,
18367     
18368     
18369     formats : [
18370         "p" ,  
18371         "h1","h2","h3","h4","h5","h6", 
18372         "pre", "code", 
18373         "abbr", "acronym", "address", "cite", "samp", "var",
18374         'div','span'
18375     ],
18376     
18377     onRender : function(ct, position)
18378     {
18379        // Roo.log("Call onRender: " + this.xtype);
18380         
18381        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18382        Roo.log(this.el);
18383        this.el.dom.style.marginBottom = '0';
18384        var _this = this;
18385        var editorcore = this.editorcore;
18386        var editor= this.editor;
18387        
18388        var children = [];
18389        var btn = function(id,cmd , toggle, handler){
18390        
18391             var  event = toggle ? 'toggle' : 'click';
18392        
18393             var a = {
18394                 size : 'sm',
18395                 xtype: 'Button',
18396                 xns: Roo.bootstrap,
18397                 glyphicon : id,
18398                 cmd : id || cmd,
18399                 enableToggle:toggle !== false,
18400                 //html : 'submit'
18401                 pressed : toggle ? false : null,
18402                 listeners : {}
18403             }
18404             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18405                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18406             }
18407             children.push(a);
18408             return a;
18409        }
18410         
18411         var style = {
18412                 xtype: 'Button',
18413                 size : 'sm',
18414                 xns: Roo.bootstrap,
18415                 glyphicon : 'font',
18416                 //html : 'submit'
18417                 menu : {
18418                     xtype: 'Menu',
18419                     xns: Roo.bootstrap,
18420                     items:  []
18421                 }
18422         };
18423         Roo.each(this.formats, function(f) {
18424             style.menu.items.push({
18425                 xtype :'MenuItem',
18426                 xns: Roo.bootstrap,
18427                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18428                 tagname : f,
18429                 listeners : {
18430                     click : function()
18431                     {
18432                         editorcore.insertTag(this.tagname);
18433                         editor.focus();
18434                     }
18435                 }
18436                 
18437             });
18438         });
18439          children.push(style);   
18440             
18441             
18442         btn('bold',false,true);
18443         btn('italic',false,true);
18444         btn('align-left', 'justifyleft',true);
18445         btn('align-center', 'justifycenter',true);
18446         btn('align-right' , 'justifyright',true);
18447         btn('link', false, false, function(btn) {
18448             //Roo.log("create link?");
18449             var url = prompt(this.createLinkText, this.defaultLinkValue);
18450             if(url && url != 'http:/'+'/'){
18451                 this.editorcore.relayCmd('createlink', url);
18452             }
18453         }),
18454         btn('list','insertunorderedlist',true);
18455         btn('pencil', false,true, function(btn){
18456                 Roo.log(this);
18457                 
18458                 this.toggleSourceEdit(btn.pressed);
18459         });
18460         /*
18461         var cog = {
18462                 xtype: 'Button',
18463                 size : 'sm',
18464                 xns: Roo.bootstrap,
18465                 glyphicon : 'cog',
18466                 //html : 'submit'
18467                 menu : {
18468                     xtype: 'Menu',
18469                     xns: Roo.bootstrap,
18470                     items:  []
18471                 }
18472         };
18473         
18474         cog.menu.items.push({
18475             xtype :'MenuItem',
18476             xns: Roo.bootstrap,
18477             html : Clean styles,
18478             tagname : f,
18479             listeners : {
18480                 click : function()
18481                 {
18482                     editorcore.insertTag(this.tagname);
18483                     editor.focus();
18484                 }
18485             }
18486             
18487         });
18488        */
18489         
18490          
18491        this.xtype = 'NavSimplebar';
18492         
18493         for(var i=0;i< children.length;i++) {
18494             
18495             this.buttons.add(this.addxtypeChild(children[i]));
18496             
18497         }
18498         
18499         editor.on('editorevent', this.updateToolbar, this);
18500     },
18501     onBtnClick : function(id)
18502     {
18503        this.editorcore.relayCmd(id);
18504        this.editorcore.focus();
18505     },
18506     
18507     /**
18508      * Protected method that will not generally be called directly. It triggers
18509      * a toolbar update by reading the markup state of the current selection in the editor.
18510      */
18511     updateToolbar: function(){
18512
18513         if(!this.editorcore.activated){
18514             this.editor.onFirstFocus(); // is this neeed?
18515             return;
18516         }
18517
18518         var btns = this.buttons; 
18519         var doc = this.editorcore.doc;
18520         btns.get('bold').setActive(doc.queryCommandState('bold'));
18521         btns.get('italic').setActive(doc.queryCommandState('italic'));
18522         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18523         
18524         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18525         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18526         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18527         
18528         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18529         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18530          /*
18531         
18532         var ans = this.editorcore.getAllAncestors();
18533         if (this.formatCombo) {
18534             
18535             
18536             var store = this.formatCombo.store;
18537             this.formatCombo.setValue("");
18538             for (var i =0; i < ans.length;i++) {
18539                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18540                     // select it..
18541                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18542                     break;
18543                 }
18544             }
18545         }
18546         
18547         
18548         
18549         // hides menus... - so this cant be on a menu...
18550         Roo.bootstrap.MenuMgr.hideAll();
18551         */
18552         Roo.bootstrap.MenuMgr.hideAll();
18553         //this.editorsyncValue();
18554     },
18555     onFirstFocus: function() {
18556         this.buttons.each(function(item){
18557            item.enable();
18558         });
18559     },
18560     toggleSourceEdit : function(sourceEditMode){
18561         
18562           
18563         if(sourceEditMode){
18564             Roo.log("disabling buttons");
18565            this.buttons.each( function(item){
18566                 if(item.cmd != 'pencil'){
18567                     item.disable();
18568                 }
18569             });
18570           
18571         }else{
18572             Roo.log("enabling buttons");
18573             if(this.editorcore.initialized){
18574                 this.buttons.each( function(item){
18575                     item.enable();
18576                 });
18577             }
18578             
18579         }
18580         Roo.log("calling toggole on editor");
18581         // tell the editor that it's been pressed..
18582         this.editor.toggleSourceEdit(sourceEditMode);
18583        
18584     }
18585 });
18586
18587
18588
18589
18590
18591 /**
18592  * @class Roo.bootstrap.Table.AbstractSelectionModel
18593  * @extends Roo.util.Observable
18594  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18595  * implemented by descendant classes.  This class should not be directly instantiated.
18596  * @constructor
18597  */
18598 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18599     this.locked = false;
18600     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18601 };
18602
18603
18604 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18605     /** @ignore Called by the grid automatically. Do not call directly. */
18606     init : function(grid){
18607         this.grid = grid;
18608         this.initEvents();
18609     },
18610
18611     /**
18612      * Locks the selections.
18613      */
18614     lock : function(){
18615         this.locked = true;
18616     },
18617
18618     /**
18619      * Unlocks the selections.
18620      */
18621     unlock : function(){
18622         this.locked = false;
18623     },
18624
18625     /**
18626      * Returns true if the selections are locked.
18627      * @return {Boolean}
18628      */
18629     isLocked : function(){
18630         return this.locked;
18631     }
18632 });
18633 /**
18634  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18635  * @class Roo.bootstrap.Table.RowSelectionModel
18636  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18637  * It supports multiple selections and keyboard selection/navigation. 
18638  * @constructor
18639  * @param {Object} config
18640  */
18641
18642 Roo.bootstrap.Table.RowSelectionModel = function(config){
18643     Roo.apply(this, config);
18644     this.selections = new Roo.util.MixedCollection(false, function(o){
18645         return o.id;
18646     });
18647
18648     this.last = false;
18649     this.lastActive = false;
18650
18651     this.addEvents({
18652         /**
18653              * @event selectionchange
18654              * Fires when the selection changes
18655              * @param {SelectionModel} this
18656              */
18657             "selectionchange" : true,
18658         /**
18659              * @event afterselectionchange
18660              * Fires after the selection changes (eg. by key press or clicking)
18661              * @param {SelectionModel} this
18662              */
18663             "afterselectionchange" : true,
18664         /**
18665              * @event beforerowselect
18666              * Fires when a row is selected being selected, return false to cancel.
18667              * @param {SelectionModel} this
18668              * @param {Number} rowIndex The selected index
18669              * @param {Boolean} keepExisting False if other selections will be cleared
18670              */
18671             "beforerowselect" : true,
18672         /**
18673              * @event rowselect
18674              * Fires when a row is selected.
18675              * @param {SelectionModel} this
18676              * @param {Number} rowIndex The selected index
18677              * @param {Roo.data.Record} r The record
18678              */
18679             "rowselect" : true,
18680         /**
18681              * @event rowdeselect
18682              * Fires when a row is deselected.
18683              * @param {SelectionModel} this
18684              * @param {Number} rowIndex The selected index
18685              */
18686         "rowdeselect" : true
18687     });
18688     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18689     this.locked = false;
18690 };
18691
18692 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18693     /**
18694      * @cfg {Boolean} singleSelect
18695      * True to allow selection of only one row at a time (defaults to false)
18696      */
18697     singleSelect : false,
18698
18699     // private
18700     initEvents : function(){
18701
18702         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18703             this.grid.on("mousedown", this.handleMouseDown, this);
18704         }else{ // allow click to work like normal
18705             this.grid.on("rowclick", this.handleDragableRowClick, this);
18706         }
18707
18708         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18709             "up" : function(e){
18710                 if(!e.shiftKey){
18711                     this.selectPrevious(e.shiftKey);
18712                 }else if(this.last !== false && this.lastActive !== false){
18713                     var last = this.last;
18714                     this.selectRange(this.last,  this.lastActive-1);
18715                     this.grid.getView().focusRow(this.lastActive);
18716                     if(last !== false){
18717                         this.last = last;
18718                     }
18719                 }else{
18720                     this.selectFirstRow();
18721                 }
18722                 this.fireEvent("afterselectionchange", this);
18723             },
18724             "down" : function(e){
18725                 if(!e.shiftKey){
18726                     this.selectNext(e.shiftKey);
18727                 }else if(this.last !== false && this.lastActive !== false){
18728                     var last = this.last;
18729                     this.selectRange(this.last,  this.lastActive+1);
18730                     this.grid.getView().focusRow(this.lastActive);
18731                     if(last !== false){
18732                         this.last = last;
18733                     }
18734                 }else{
18735                     this.selectFirstRow();
18736                 }
18737                 this.fireEvent("afterselectionchange", this);
18738             },
18739             scope: this
18740         });
18741
18742         var view = this.grid.view;
18743         view.on("refresh", this.onRefresh, this);
18744         view.on("rowupdated", this.onRowUpdated, this);
18745         view.on("rowremoved", this.onRemove, this);
18746     },
18747
18748     // private
18749     onRefresh : function(){
18750         var ds = this.grid.dataSource, i, v = this.grid.view;
18751         var s = this.selections;
18752         s.each(function(r){
18753             if((i = ds.indexOfId(r.id)) != -1){
18754                 v.onRowSelect(i);
18755             }else{
18756                 s.remove(r);
18757             }
18758         });
18759     },
18760
18761     // private
18762     onRemove : function(v, index, r){
18763         this.selections.remove(r);
18764     },
18765
18766     // private
18767     onRowUpdated : function(v, index, r){
18768         if(this.isSelected(r)){
18769             v.onRowSelect(index);
18770         }
18771     },
18772
18773     /**
18774      * Select records.
18775      * @param {Array} records The records to select
18776      * @param {Boolean} keepExisting (optional) True to keep existing selections
18777      */
18778     selectRecords : function(records, keepExisting){
18779         if(!keepExisting){
18780             this.clearSelections();
18781         }
18782         var ds = this.grid.dataSource;
18783         for(var i = 0, len = records.length; i < len; i++){
18784             this.selectRow(ds.indexOf(records[i]), true);
18785         }
18786     },
18787
18788     /**
18789      * Gets the number of selected rows.
18790      * @return {Number}
18791      */
18792     getCount : function(){
18793         return this.selections.length;
18794     },
18795
18796     /**
18797      * Selects the first row in the grid.
18798      */
18799     selectFirstRow : function(){
18800         this.selectRow(0);
18801     },
18802
18803     /**
18804      * Select the last row.
18805      * @param {Boolean} keepExisting (optional) True to keep existing selections
18806      */
18807     selectLastRow : function(keepExisting){
18808         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18809     },
18810
18811     /**
18812      * Selects the row immediately following the last selected row.
18813      * @param {Boolean} keepExisting (optional) True to keep existing selections
18814      */
18815     selectNext : function(keepExisting){
18816         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18817             this.selectRow(this.last+1, keepExisting);
18818             this.grid.getView().focusRow(this.last);
18819         }
18820     },
18821
18822     /**
18823      * Selects the row that precedes the last selected row.
18824      * @param {Boolean} keepExisting (optional) True to keep existing selections
18825      */
18826     selectPrevious : function(keepExisting){
18827         if(this.last){
18828             this.selectRow(this.last-1, keepExisting);
18829             this.grid.getView().focusRow(this.last);
18830         }
18831     },
18832
18833     /**
18834      * Returns the selected records
18835      * @return {Array} Array of selected records
18836      */
18837     getSelections : function(){
18838         return [].concat(this.selections.items);
18839     },
18840
18841     /**
18842      * Returns the first selected record.
18843      * @return {Record}
18844      */
18845     getSelected : function(){
18846         return this.selections.itemAt(0);
18847     },
18848
18849
18850     /**
18851      * Clears all selections.
18852      */
18853     clearSelections : function(fast){
18854         if(this.locked) return;
18855         if(fast !== true){
18856             var ds = this.grid.dataSource;
18857             var s = this.selections;
18858             s.each(function(r){
18859                 this.deselectRow(ds.indexOfId(r.id));
18860             }, this);
18861             s.clear();
18862         }else{
18863             this.selections.clear();
18864         }
18865         this.last = false;
18866     },
18867
18868
18869     /**
18870      * Selects all rows.
18871      */
18872     selectAll : function(){
18873         if(this.locked) return;
18874         this.selections.clear();
18875         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18876             this.selectRow(i, true);
18877         }
18878     },
18879
18880     /**
18881      * Returns True if there is a selection.
18882      * @return {Boolean}
18883      */
18884     hasSelection : function(){
18885         return this.selections.length > 0;
18886     },
18887
18888     /**
18889      * Returns True if the specified row is selected.
18890      * @param {Number/Record} record The record or index of the record to check
18891      * @return {Boolean}
18892      */
18893     isSelected : function(index){
18894         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18895         return (r && this.selections.key(r.id) ? true : false);
18896     },
18897
18898     /**
18899      * Returns True if the specified record id is selected.
18900      * @param {String} id The id of record to check
18901      * @return {Boolean}
18902      */
18903     isIdSelected : function(id){
18904         return (this.selections.key(id) ? true : false);
18905     },
18906
18907     // private
18908     handleMouseDown : function(e, t){
18909         var view = this.grid.getView(), rowIndex;
18910         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18911             return;
18912         };
18913         if(e.shiftKey && this.last !== false){
18914             var last = this.last;
18915             this.selectRange(last, rowIndex, e.ctrlKey);
18916             this.last = last; // reset the last
18917             view.focusRow(rowIndex);
18918         }else{
18919             var isSelected = this.isSelected(rowIndex);
18920             if(e.button !== 0 && isSelected){
18921                 view.focusRow(rowIndex);
18922             }else if(e.ctrlKey && isSelected){
18923                 this.deselectRow(rowIndex);
18924             }else if(!isSelected){
18925                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18926                 view.focusRow(rowIndex);
18927             }
18928         }
18929         this.fireEvent("afterselectionchange", this);
18930     },
18931     // private
18932     handleDragableRowClick :  function(grid, rowIndex, e) 
18933     {
18934         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18935             this.selectRow(rowIndex, false);
18936             grid.view.focusRow(rowIndex);
18937              this.fireEvent("afterselectionchange", this);
18938         }
18939     },
18940     
18941     /**
18942      * Selects multiple rows.
18943      * @param {Array} rows Array of the indexes of the row to select
18944      * @param {Boolean} keepExisting (optional) True to keep existing selections
18945      */
18946     selectRows : function(rows, keepExisting){
18947         if(!keepExisting){
18948             this.clearSelections();
18949         }
18950         for(var i = 0, len = rows.length; i < len; i++){
18951             this.selectRow(rows[i], true);
18952         }
18953     },
18954
18955     /**
18956      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18957      * @param {Number} startRow The index of the first row in the range
18958      * @param {Number} endRow The index of the last row in the range
18959      * @param {Boolean} keepExisting (optional) True to retain existing selections
18960      */
18961     selectRange : function(startRow, endRow, keepExisting){
18962         if(this.locked) return;
18963         if(!keepExisting){
18964             this.clearSelections();
18965         }
18966         if(startRow <= endRow){
18967             for(var i = startRow; i <= endRow; i++){
18968                 this.selectRow(i, true);
18969             }
18970         }else{
18971             for(var i = startRow; i >= endRow; i--){
18972                 this.selectRow(i, true);
18973             }
18974         }
18975     },
18976
18977     /**
18978      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18979      * @param {Number} startRow The index of the first row in the range
18980      * @param {Number} endRow The index of the last row in the range
18981      */
18982     deselectRange : function(startRow, endRow, preventViewNotify){
18983         if(this.locked) return;
18984         for(var i = startRow; i <= endRow; i++){
18985             this.deselectRow(i, preventViewNotify);
18986         }
18987     },
18988
18989     /**
18990      * Selects a row.
18991      * @param {Number} row The index of the row to select
18992      * @param {Boolean} keepExisting (optional) True to keep existing selections
18993      */
18994     selectRow : function(index, keepExisting, preventViewNotify){
18995         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18996         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18997             if(!keepExisting || this.singleSelect){
18998                 this.clearSelections();
18999             }
19000             var r = this.grid.dataSource.getAt(index);
19001             this.selections.add(r);
19002             this.last = this.lastActive = index;
19003             if(!preventViewNotify){
19004                 this.grid.getView().onRowSelect(index);
19005             }
19006             this.fireEvent("rowselect", this, index, r);
19007             this.fireEvent("selectionchange", this);
19008         }
19009     },
19010
19011     /**
19012      * Deselects a row.
19013      * @param {Number} row The index of the row to deselect
19014      */
19015     deselectRow : function(index, preventViewNotify){
19016         if(this.locked) return;
19017         if(this.last == index){
19018             this.last = false;
19019         }
19020         if(this.lastActive == index){
19021             this.lastActive = false;
19022         }
19023         var r = this.grid.dataSource.getAt(index);
19024         this.selections.remove(r);
19025         if(!preventViewNotify){
19026             this.grid.getView().onRowDeselect(index);
19027         }
19028         this.fireEvent("rowdeselect", this, index);
19029         this.fireEvent("selectionchange", this);
19030     },
19031
19032     // private
19033     restoreLast : function(){
19034         if(this._last){
19035             this.last = this._last;
19036         }
19037     },
19038
19039     // private
19040     acceptsNav : function(row, col, cm){
19041         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19042     },
19043
19044     // private
19045     onEditorKey : function(field, e){
19046         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19047         if(k == e.TAB){
19048             e.stopEvent();
19049             ed.completeEdit();
19050             if(e.shiftKey){
19051                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19052             }else{
19053                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19054             }
19055         }else if(k == e.ENTER && !e.ctrlKey){
19056             e.stopEvent();
19057             ed.completeEdit();
19058             if(e.shiftKey){
19059                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19060             }else{
19061                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19062             }
19063         }else if(k == e.ESC){
19064             ed.cancelEdit();
19065         }
19066         if(newCell){
19067             g.startEditing(newCell[0], newCell[1]);
19068         }
19069     }
19070 });/*
19071  * Based on:
19072  * Ext JS Library 1.1.1
19073  * Copyright(c) 2006-2007, Ext JS, LLC.
19074  *
19075  * Originally Released Under LGPL - original licence link has changed is not relivant.
19076  *
19077  * Fork - LGPL
19078  * <script type="text/javascript">
19079  */
19080  
19081 /**
19082  * @class Roo.bootstrap.PagingToolbar
19083  * @extends Roo.Row
19084  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19085  * @constructor
19086  * Create a new PagingToolbar
19087  * @param {Object} config The config object
19088  */
19089 Roo.bootstrap.PagingToolbar = function(config)
19090 {
19091     // old args format still supported... - xtype is prefered..
19092         // created from xtype...
19093     var ds = config.dataSource;
19094     this.toolbarItems = [];
19095     if (config.items) {
19096         this.toolbarItems = config.items;
19097 //        config.items = [];
19098     }
19099     
19100     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19101     this.ds = ds;
19102     this.cursor = 0;
19103     if (ds) { 
19104         this.bind(ds);
19105     }
19106     
19107     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19108     
19109 };
19110
19111 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19112     /**
19113      * @cfg {Roo.data.Store} dataSource
19114      * The underlying data store providing the paged data
19115      */
19116     /**
19117      * @cfg {String/HTMLElement/Element} container
19118      * container The id or element that will contain the toolbar
19119      */
19120     /**
19121      * @cfg {Boolean} displayInfo
19122      * True to display the displayMsg (defaults to false)
19123      */
19124     /**
19125      * @cfg {Number} pageSize
19126      * The number of records to display per page (defaults to 20)
19127      */
19128     pageSize: 20,
19129     /**
19130      * @cfg {String} displayMsg
19131      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19132      */
19133     displayMsg : 'Displaying {0} - {1} of {2}',
19134     /**
19135      * @cfg {String} emptyMsg
19136      * The message to display when no records are found (defaults to "No data to display")
19137      */
19138     emptyMsg : 'No data to display',
19139     /**
19140      * Customizable piece of the default paging text (defaults to "Page")
19141      * @type String
19142      */
19143     beforePageText : "Page",
19144     /**
19145      * Customizable piece of the default paging text (defaults to "of %0")
19146      * @type String
19147      */
19148     afterPageText : "of {0}",
19149     /**
19150      * Customizable piece of the default paging text (defaults to "First Page")
19151      * @type String
19152      */
19153     firstText : "First Page",
19154     /**
19155      * Customizable piece of the default paging text (defaults to "Previous Page")
19156      * @type String
19157      */
19158     prevText : "Previous Page",
19159     /**
19160      * Customizable piece of the default paging text (defaults to "Next Page")
19161      * @type String
19162      */
19163     nextText : "Next Page",
19164     /**
19165      * Customizable piece of the default paging text (defaults to "Last Page")
19166      * @type String
19167      */
19168     lastText : "Last Page",
19169     /**
19170      * Customizable piece of the default paging text (defaults to "Refresh")
19171      * @type String
19172      */
19173     refreshText : "Refresh",
19174
19175     buttons : false,
19176     // private
19177     onRender : function(ct, position) 
19178     {
19179         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19180         this.navgroup.parentId = this.id;
19181         this.navgroup.onRender(this.el, null);
19182         // add the buttons to the navgroup
19183         
19184         if(this.displayInfo){
19185             Roo.log(this.el.select('ul.navbar-nav',true).first());
19186             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19187             this.displayEl = this.el.select('.x-paging-info', true).first();
19188 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19189 //            this.displayEl = navel.el.select('span',true).first();
19190         }
19191         
19192         var _this = this;
19193         
19194         if(this.buttons){
19195             Roo.each(_this.buttons, function(e){
19196                Roo.factory(e).onRender(_this.el, null);
19197             });
19198         }
19199             
19200         Roo.each(_this.toolbarItems, function(e) {
19201             _this.navgroup.addItem(e);
19202         });
19203         
19204         this.first = this.navgroup.addItem({
19205             tooltip: this.firstText,
19206             cls: "prev",
19207             icon : 'fa fa-backward',
19208             disabled: true,
19209             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19210         });
19211         
19212         this.prev =  this.navgroup.addItem({
19213             tooltip: this.prevText,
19214             cls: "prev",
19215             icon : 'fa fa-step-backward',
19216             disabled: true,
19217             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19218         });
19219     //this.addSeparator();
19220         
19221         
19222         var field = this.navgroup.addItem( {
19223             tagtype : 'span',
19224             cls : 'x-paging-position',
19225             
19226             html : this.beforePageText  +
19227                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19228                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19229          } ); //?? escaped?
19230         
19231         this.field = field.el.select('input', true).first();
19232         this.field.on("keydown", this.onPagingKeydown, this);
19233         this.field.on("focus", function(){this.dom.select();});
19234     
19235     
19236         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19237         //this.field.setHeight(18);
19238         //this.addSeparator();
19239         this.next = this.navgroup.addItem({
19240             tooltip: this.nextText,
19241             cls: "next",
19242             html : ' <i class="fa fa-step-forward">',
19243             disabled: true,
19244             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19245         });
19246         this.last = this.navgroup.addItem({
19247             tooltip: this.lastText,
19248             icon : 'fa fa-forward',
19249             cls: "next",
19250             disabled: true,
19251             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19252         });
19253     //this.addSeparator();
19254         this.loading = this.navgroup.addItem({
19255             tooltip: this.refreshText,
19256             icon: 'fa fa-refresh',
19257             
19258             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19259         });
19260
19261     },
19262
19263     // private
19264     updateInfo : function(){
19265         if(this.displayEl){
19266             var count = this.ds.getCount();
19267             var msg = count == 0 ?
19268                 this.emptyMsg :
19269                 String.format(
19270                     this.displayMsg,
19271                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19272                 );
19273             this.displayEl.update(msg);
19274         }
19275     },
19276
19277     // private
19278     onLoad : function(ds, r, o){
19279        this.cursor = o.params ? o.params.start : 0;
19280        var d = this.getPageData(),
19281             ap = d.activePage,
19282             ps = d.pages;
19283         
19284        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19285        this.field.dom.value = ap;
19286        this.first.setDisabled(ap == 1);
19287        this.prev.setDisabled(ap == 1);
19288        this.next.setDisabled(ap == ps);
19289        this.last.setDisabled(ap == ps);
19290        this.loading.enable();
19291        this.updateInfo();
19292     },
19293
19294     // private
19295     getPageData : function(){
19296         var total = this.ds.getTotalCount();
19297         return {
19298             total : total,
19299             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19300             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19301         };
19302     },
19303
19304     // private
19305     onLoadError : function(){
19306         this.loading.enable();
19307     },
19308
19309     // private
19310     onPagingKeydown : function(e){
19311         var k = e.getKey();
19312         var d = this.getPageData();
19313         if(k == e.RETURN){
19314             var v = this.field.dom.value, pageNum;
19315             if(!v || isNaN(pageNum = parseInt(v, 10))){
19316                 this.field.dom.value = d.activePage;
19317                 return;
19318             }
19319             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19320             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19321             e.stopEvent();
19322         }
19323         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))
19324         {
19325           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19326           this.field.dom.value = pageNum;
19327           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19328           e.stopEvent();
19329         }
19330         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19331         {
19332           var v = this.field.dom.value, pageNum; 
19333           var increment = (e.shiftKey) ? 10 : 1;
19334           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19335             increment *= -1;
19336           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19337             this.field.dom.value = d.activePage;
19338             return;
19339           }
19340           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19341           {
19342             this.field.dom.value = parseInt(v, 10) + increment;
19343             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19344             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19345           }
19346           e.stopEvent();
19347         }
19348     },
19349
19350     // private
19351     beforeLoad : function(){
19352         if(this.loading){
19353             this.loading.disable();
19354         }
19355     },
19356
19357     // private
19358     onClick : function(which){
19359         var ds = this.ds;
19360         if (!ds) {
19361             return;
19362         }
19363         switch(which){
19364             case "first":
19365                 ds.load({params:{start: 0, limit: this.pageSize}});
19366             break;
19367             case "prev":
19368                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19369             break;
19370             case "next":
19371                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19372             break;
19373             case "last":
19374                 var total = ds.getTotalCount();
19375                 var extra = total % this.pageSize;
19376                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19377                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19378             break;
19379             case "refresh":
19380                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19381             break;
19382         }
19383     },
19384
19385     /**
19386      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19387      * @param {Roo.data.Store} store The data store to unbind
19388      */
19389     unbind : function(ds){
19390         ds.un("beforeload", this.beforeLoad, this);
19391         ds.un("load", this.onLoad, this);
19392         ds.un("loadexception", this.onLoadError, this);
19393         ds.un("remove", this.updateInfo, this);
19394         ds.un("add", this.updateInfo, this);
19395         this.ds = undefined;
19396     },
19397
19398     /**
19399      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19400      * @param {Roo.data.Store} store The data store to bind
19401      */
19402     bind : function(ds){
19403         ds.on("beforeload", this.beforeLoad, this);
19404         ds.on("load", this.onLoad, this);
19405         ds.on("loadexception", this.onLoadError, this);
19406         ds.on("remove", this.updateInfo, this);
19407         ds.on("add", this.updateInfo, this);
19408         this.ds = ds;
19409     }
19410 });/*
19411  * - LGPL
19412  *
19413  * element
19414  * 
19415  */
19416
19417 /**
19418  * @class Roo.bootstrap.MessageBar
19419  * @extends Roo.bootstrap.Component
19420  * Bootstrap MessageBar class
19421  * @cfg {String} html contents of the MessageBar
19422  * @cfg {String} weight (info | success | warning | danger) default info
19423  * @cfg {String} beforeClass insert the bar before the given class
19424  * @cfg {Boolean} closable (true | false) default false
19425  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19426  * 
19427  * @constructor
19428  * Create a new Element
19429  * @param {Object} config The config object
19430  */
19431
19432 Roo.bootstrap.MessageBar = function(config){
19433     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19434 };
19435
19436 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19437     
19438     html: '',
19439     weight: 'info',
19440     closable: false,
19441     fixed: false,
19442     beforeClass: 'bootstrap-sticky-wrap',
19443     
19444     getAutoCreate : function(){
19445         
19446         var cfg = {
19447             tag: 'div',
19448             cls: 'alert alert-dismissable alert-' + this.weight,
19449             cn: [
19450                 {
19451                     tag: 'span',
19452                     cls: 'message',
19453                     html: this.html || ''
19454                 }
19455             ]
19456         }
19457         
19458         if(this.fixed){
19459             cfg.cls += ' alert-messages-fixed';
19460         }
19461         
19462         if(this.closable){
19463             cfg.cn.push({
19464                 tag: 'button',
19465                 cls: 'close',
19466                 html: 'x'
19467             });
19468         }
19469         
19470         return cfg;
19471     },
19472     
19473     onRender : function(ct, position)
19474     {
19475         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19476         
19477         if(!this.el){
19478             var cfg = Roo.apply({},  this.getAutoCreate());
19479             cfg.id = Roo.id();
19480             
19481             if (this.cls) {
19482                 cfg.cls += ' ' + this.cls;
19483             }
19484             if (this.style) {
19485                 cfg.style = this.style;
19486             }
19487             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19488             
19489             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19490         }
19491         
19492         this.el.select('>button.close').on('click', this.hide, this);
19493         
19494     },
19495     
19496     show : function()
19497     {
19498         if (!this.rendered) {
19499             this.render();
19500         }
19501         
19502         this.el.show();
19503         
19504         this.fireEvent('show', this);
19505         
19506     },
19507     
19508     hide : function()
19509     {
19510         if (!this.rendered) {
19511             this.render();
19512         }
19513         
19514         this.el.hide();
19515         
19516         this.fireEvent('hide', this);
19517     },
19518     
19519     update : function()
19520     {
19521 //        var e = this.el.dom.firstChild;
19522 //        
19523 //        if(this.closable){
19524 //            e = e.nextSibling;
19525 //        }
19526 //        
19527 //        e.data = this.html || '';
19528
19529         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19530     }
19531    
19532 });
19533
19534  
19535
19536      /*
19537  * - LGPL
19538  *
19539  * Graph
19540  * 
19541  */
19542
19543
19544 /**
19545  * @class Roo.bootstrap.Graph
19546  * @extends Roo.bootstrap.Component
19547  * Bootstrap Graph class
19548 > Prameters
19549  -sm {number} sm 4
19550  -md {number} md 5
19551  @cfg {String} graphtype  bar | vbar | pie
19552  @cfg {number} g_x coodinator | centre x (pie)
19553  @cfg {number} g_y coodinator | centre y (pie)
19554  @cfg {number} g_r radius (pie)
19555  @cfg {number} g_height height of the chart (respected by all elements in the set)
19556  @cfg {number} g_width width of the chart (respected by all elements in the set)
19557  @cfg {Object} title The title of the chart
19558     
19559  -{Array}  values
19560  -opts (object) options for the chart 
19561      o {
19562      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19563      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19564      o vgutter (number)
19565      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.
19566      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19567      o to
19568      o stretch (boolean)
19569      o }
19570  -opts (object) options for the pie
19571      o{
19572      o cut
19573      o startAngle (number)
19574      o endAngle (number)
19575      } 
19576  *
19577  * @constructor
19578  * Create a new Input
19579  * @param {Object} config The config object
19580  */
19581
19582 Roo.bootstrap.Graph = function(config){
19583     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19584     
19585     this.addEvents({
19586         // img events
19587         /**
19588          * @event click
19589          * The img click event for the img.
19590          * @param {Roo.EventObject} e
19591          */
19592         "click" : true
19593     });
19594 };
19595
19596 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19597     
19598     sm: 4,
19599     md: 5,
19600     graphtype: 'bar',
19601     g_height: 250,
19602     g_width: 400,
19603     g_x: 50,
19604     g_y: 50,
19605     g_r: 30,
19606     opts:{
19607         //g_colors: this.colors,
19608         g_type: 'soft',
19609         g_gutter: '20%'
19610
19611     },
19612     title : false,
19613
19614     getAutoCreate : function(){
19615         
19616         var cfg = {
19617             tag: 'div',
19618             html : null
19619         }
19620         
19621         
19622         return  cfg;
19623     },
19624
19625     onRender : function(ct,position){
19626         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19627         this.raphael = Raphael(this.el.dom);
19628         
19629                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19630                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19631                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19632                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19633                 /*
19634                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19635                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19636                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19637                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19638                 
19639                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19640                 r.barchart(330, 10, 300, 220, data1);
19641                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19642                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19643                 */
19644                 
19645                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19646                 // r.barchart(30, 30, 560, 250,  xdata, {
19647                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19648                 //     axis : "0 0 1 1",
19649                 //     axisxlabels :  xdata
19650                 //     //yvalues : cols,
19651                    
19652                 // });
19653 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19654 //        
19655 //        this.load(null,xdata,{
19656 //                axis : "0 0 1 1",
19657 //                axisxlabels :  xdata
19658 //                });
19659
19660     },
19661
19662     load : function(graphtype,xdata,opts){
19663         this.raphael.clear();
19664         if(!graphtype) {
19665             graphtype = this.graphtype;
19666         }
19667         if(!opts){
19668             opts = this.opts;
19669         }
19670         var r = this.raphael,
19671             fin = function () {
19672                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19673             },
19674             fout = function () {
19675                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19676             },
19677             pfin = function() {
19678                 this.sector.stop();
19679                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19680
19681                 if (this.label) {
19682                     this.label[0].stop();
19683                     this.label[0].attr({ r: 7.5 });
19684                     this.label[1].attr({ "font-weight": 800 });
19685                 }
19686             },
19687             pfout = function() {
19688                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19689
19690                 if (this.label) {
19691                     this.label[0].animate({ r: 5 }, 500, "bounce");
19692                     this.label[1].attr({ "font-weight": 400 });
19693                 }
19694             };
19695
19696         switch(graphtype){
19697             case 'bar':
19698                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19699                 break;
19700             case 'hbar':
19701                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19702                 break;
19703             case 'pie':
19704 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19705 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19706 //            
19707                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19708                 
19709                 break;
19710
19711         }
19712         
19713         if(this.title){
19714             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19715         }
19716         
19717     },
19718     
19719     setTitle: function(o)
19720     {
19721         this.title = o;
19722     },
19723     
19724     initEvents: function() {
19725         
19726         if(!this.href){
19727             this.el.on('click', this.onClick, this);
19728         }
19729     },
19730     
19731     onClick : function(e)
19732     {
19733         Roo.log('img onclick');
19734         this.fireEvent('click', this, e);
19735     }
19736    
19737 });
19738
19739  
19740 /*
19741  * - LGPL
19742  *
19743  * numberBox
19744  * 
19745  */
19746 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19747
19748 /**
19749  * @class Roo.bootstrap.dash.NumberBox
19750  * @extends Roo.bootstrap.Component
19751  * Bootstrap NumberBox class
19752  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19753  * @cfg {String} headline Box headline
19754  * @cfg {String} content Box content
19755  * @cfg {String} icon Box icon
19756  * @cfg {String} footer Footer text
19757  * @cfg {String} fhref Footer href
19758  * 
19759  * @constructor
19760  * Create a new NumberBox
19761  * @param {Object} config The config object
19762  */
19763
19764
19765 Roo.bootstrap.dash.NumberBox = function(config){
19766     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19767     
19768 };
19769
19770 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19771     
19772     bgcolor : 'aqua',
19773     headline : '',
19774     content : '',
19775     icon : '',
19776     footer : '',
19777     fhref : '',
19778     ficon : '',
19779     
19780     getAutoCreate : function(){
19781         
19782         var cfg = {
19783             tag : 'div',
19784             cls : 'small-box bg-' + this.bgcolor,
19785             cn : [
19786                 {
19787                     tag : 'div',
19788                     cls : 'inner',
19789                     cn :[
19790                         {
19791                             tag : 'h3',
19792                             cls : 'roo-headline',
19793                             html : this.headline
19794                         },
19795                         {
19796                             tag : 'p',
19797                             cls : 'roo-content',
19798                             html : this.content
19799                         }
19800                     ]
19801                 }
19802             ]
19803         }
19804         
19805         if(this.icon){
19806             cfg.cn.push({
19807                 tag : 'div',
19808                 cls : 'icon',
19809                 cn :[
19810                     {
19811                         tag : 'i',
19812                         cls : 'ion ' + this.icon
19813                     }
19814                 ]
19815             });
19816         }
19817         
19818         if(this.footer){
19819             var footer = {
19820                 tag : 'a',
19821                 cls : 'small-box-footer',
19822                 href : this.fhref || '#',
19823                 html : this.footer
19824             };
19825             
19826             cfg.cn.push(footer);
19827             
19828         }
19829         
19830         return  cfg;
19831     },
19832
19833     onRender : function(ct,position){
19834         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19835
19836
19837        
19838                 
19839     },
19840
19841     setHeadline: function (value)
19842     {
19843         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19844     },
19845     
19846     setFooter: function (value, href)
19847     {
19848         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19849         
19850         if(href){
19851             this.el.select('a.small-box-footer',true).first().attr('href', href);
19852         }
19853         
19854     },
19855
19856     setContent: function (value)
19857     {
19858         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19859     },
19860
19861     initEvents: function() 
19862     {   
19863         
19864     }
19865     
19866 });
19867
19868  
19869 /*
19870  * - LGPL
19871  *
19872  * TabBox
19873  * 
19874  */
19875 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19876
19877 /**
19878  * @class Roo.bootstrap.dash.TabBox
19879  * @extends Roo.bootstrap.Component
19880  * Bootstrap TabBox class
19881  * @cfg {String} title Title of the TabBox
19882  * @cfg {String} icon Icon of the TabBox
19883  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19884  * 
19885  * @constructor
19886  * Create a new TabBox
19887  * @param {Object} config The config object
19888  */
19889
19890
19891 Roo.bootstrap.dash.TabBox = function(config){
19892     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19893     this.addEvents({
19894         // raw events
19895         /**
19896          * @event addpane
19897          * When a pane is added
19898          * @param {Roo.bootstrap.dash.TabPane} pane
19899          */
19900         "addpane" : true
19901          
19902     });
19903 };
19904
19905 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19906
19907     title : '',
19908     icon : false,
19909     showtabs : true,
19910     
19911     getChildContainer : function()
19912     {
19913         return this.el.select('.tab-content', true).first();
19914     },
19915     
19916     getAutoCreate : function(){
19917         
19918         var header = {
19919             tag: 'li',
19920             cls: 'pull-left header',
19921             html: this.title,
19922             cn : []
19923         };
19924         
19925         if(this.icon){
19926             header.cn.push({
19927                 tag: 'i',
19928                 cls: 'fa ' + this.icon
19929             });
19930         }
19931         
19932         
19933         var cfg = {
19934             tag: 'div',
19935             cls: 'nav-tabs-custom',
19936             cn: [
19937                 {
19938                     tag: 'ul',
19939                     cls: 'nav nav-tabs pull-right',
19940                     cn: [
19941                         header
19942                     ]
19943                 },
19944                 {
19945                     tag: 'div',
19946                     cls: 'tab-content no-padding',
19947                     cn: []
19948                 }
19949             ]
19950         }
19951
19952         return  cfg;
19953     },
19954     initEvents : function()
19955     {
19956         //Roo.log('add add pane handler');
19957         this.on('addpane', this.onAddPane, this);
19958     },
19959      /**
19960      * Updates the box title
19961      * @param {String} html to set the title to.
19962      */
19963     setTitle : function(value)
19964     {
19965         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19966     },
19967     onAddPane : function(pane)
19968     {
19969         //Roo.log('addpane');
19970         //Roo.log(pane);
19971         // tabs are rendere left to right..
19972         if(!this.showtabs){
19973             return;
19974         }
19975         
19976         var ctr = this.el.select('.nav-tabs', true).first();
19977          
19978          
19979         var existing = ctr.select('.nav-tab',true);
19980         var qty = existing.getCount();;
19981         
19982         
19983         var tab = ctr.createChild({
19984             tag : 'li',
19985             cls : 'nav-tab' + (qty ? '' : ' active'),
19986             cn : [
19987                 {
19988                     tag : 'a',
19989                     href:'#',
19990                     html : pane.title
19991                 }
19992             ]
19993         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19994         pane.tab = tab;
19995         
19996         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19997         if (!qty) {
19998             pane.el.addClass('active');
19999         }
20000         
20001                 
20002     },
20003     onTabClick : function(ev,un,ob,pane)
20004     {
20005         //Roo.log('tab - prev default');
20006         ev.preventDefault();
20007         
20008         
20009         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20010         pane.tab.addClass('active');
20011         //Roo.log(pane.title);
20012         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20013         // technically we should have a deactivate event.. but maybe add later.
20014         // and it should not de-activate the selected tab...
20015         
20016         pane.el.addClass('active');
20017         pane.fireEvent('activate');
20018         
20019         
20020     }
20021     
20022     
20023 });
20024
20025  
20026 /*
20027  * - LGPL
20028  *
20029  * Tab pane
20030  * 
20031  */
20032 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20033 /**
20034  * @class Roo.bootstrap.TabPane
20035  * @extends Roo.bootstrap.Component
20036  * Bootstrap TabPane class
20037  * @cfg {Boolean} active (false | true) Default false
20038  * @cfg {String} title title of panel
20039
20040  * 
20041  * @constructor
20042  * Create a new TabPane
20043  * @param {Object} config The config object
20044  */
20045
20046 Roo.bootstrap.dash.TabPane = function(config){
20047     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20048     
20049     this.addEvents({
20050         // raw events
20051         /**
20052          * @event activate
20053          * When a pane is activated
20054          * @param {Roo.bootstrap.dash.TabPane} pane
20055          */
20056         "activate" : true
20057          
20058     });
20059 };
20060
20061 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20062     
20063     active : false,
20064     title : '',
20065     
20066     // the tabBox that this is attached to.
20067     tab : false,
20068      
20069     getAutoCreate : function() 
20070     {
20071         var cfg = {
20072             tag: 'div',
20073             cls: 'tab-pane'
20074         }
20075         
20076         if(this.active){
20077             cfg.cls += ' active';
20078         }
20079         
20080         return cfg;
20081     },
20082     initEvents  : function()
20083     {
20084         //Roo.log('trigger add pane handler');
20085         this.parent().fireEvent('addpane', this)
20086     },
20087     
20088      /**
20089      * Updates the tab title 
20090      * @param {String} html to set the title to.
20091      */
20092     setTitle: function(str)
20093     {
20094         if (!this.tab) {
20095             return;
20096         }
20097         this.title = str;
20098         this.tab.select('a', true).first().dom.innerHTML = str;
20099         
20100     }
20101     
20102     
20103     
20104 });
20105
20106  
20107
20108
20109  /*
20110  * - LGPL
20111  *
20112  * menu
20113  * 
20114  */
20115 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20116
20117 /**
20118  * @class Roo.bootstrap.menu.Menu
20119  * @extends Roo.bootstrap.Component
20120  * Bootstrap Menu class - container for Menu
20121  * @cfg {String} html Text of the menu
20122  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20123  * @cfg {String} icon Font awesome icon
20124  * @cfg {String} pos Menu align to (top | bottom) default bottom
20125  * 
20126  * 
20127  * @constructor
20128  * Create a new Menu
20129  * @param {Object} config The config object
20130  */
20131
20132
20133 Roo.bootstrap.menu.Menu = function(config){
20134     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20135     
20136     this.addEvents({
20137         /**
20138          * @event beforeshow
20139          * Fires before this menu is displayed
20140          * @param {Roo.bootstrap.menu.Menu} this
20141          */
20142         beforeshow : true,
20143         /**
20144          * @event beforehide
20145          * Fires before this menu is hidden
20146          * @param {Roo.bootstrap.menu.Menu} this
20147          */
20148         beforehide : true,
20149         /**
20150          * @event show
20151          * Fires after this menu is displayed
20152          * @param {Roo.bootstrap.menu.Menu} this
20153          */
20154         show : true,
20155         /**
20156          * @event hide
20157          * Fires after this menu is hidden
20158          * @param {Roo.bootstrap.menu.Menu} this
20159          */
20160         hide : true,
20161         /**
20162          * @event click
20163          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20164          * @param {Roo.bootstrap.menu.Menu} this
20165          * @param {Roo.EventObject} e
20166          */
20167         click : true
20168     });
20169     
20170 };
20171
20172 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20173     
20174     submenu : false,
20175     html : '',
20176     weight : 'default',
20177     icon : false,
20178     pos : 'bottom',
20179     
20180     
20181     getChildContainer : function() {
20182         if(this.isSubMenu){
20183             return this.el;
20184         }
20185         
20186         return this.el.select('ul.dropdown-menu', true).first();  
20187     },
20188     
20189     getAutoCreate : function()
20190     {
20191         var text = [
20192             {
20193                 tag : 'span',
20194                 cls : 'roo-menu-text',
20195                 html : this.html
20196             }
20197         ];
20198         
20199         if(this.icon){
20200             text.unshift({
20201                 tag : 'i',
20202                 cls : 'fa ' + this.icon
20203             })
20204         }
20205         
20206         
20207         var cfg = {
20208             tag : 'div',
20209             cls : 'btn-group',
20210             cn : [
20211                 {
20212                     tag : 'button',
20213                     cls : 'dropdown-button btn btn-' + this.weight,
20214                     cn : text
20215                 },
20216                 {
20217                     tag : 'button',
20218                     cls : 'dropdown-toggle btn btn-' + this.weight,
20219                     cn : [
20220                         {
20221                             tag : 'span',
20222                             cls : 'caret'
20223                         }
20224                     ]
20225                 },
20226                 {
20227                     tag : 'ul',
20228                     cls : 'dropdown-menu'
20229                 }
20230             ]
20231             
20232         };
20233         
20234         if(this.pos == 'top'){
20235             cfg.cls += ' dropup';
20236         }
20237         
20238         if(this.isSubMenu){
20239             cfg = {
20240                 tag : 'ul',
20241                 cls : 'dropdown-menu'
20242             }
20243         }
20244         
20245         return cfg;
20246     },
20247     
20248     onRender : function(ct, position)
20249     {
20250         this.isSubMenu = ct.hasClass('dropdown-submenu');
20251         
20252         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20253     },
20254     
20255     initEvents : function() 
20256     {
20257         if(this.isSubMenu){
20258             return;
20259         }
20260         
20261         this.hidden = true;
20262         
20263         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20264         this.triggerEl.on('click', this.onTriggerPress, this);
20265         
20266         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20267         this.buttonEl.on('click', this.onClick, this);
20268         
20269     },
20270     
20271     list : function()
20272     {
20273         if(this.isSubMenu){
20274             return this.el;
20275         }
20276         
20277         return this.el.select('ul.dropdown-menu', true).first();
20278     },
20279     
20280     onClick : function(e)
20281     {
20282         this.fireEvent("click", this, e);
20283     },
20284     
20285     onTriggerPress  : function(e)
20286     {   
20287         if (this.isVisible()) {
20288             this.hide();
20289         } else {
20290             this.show();
20291         }
20292     },
20293     
20294     isVisible : function(){
20295         return !this.hidden;
20296     },
20297     
20298     show : function()
20299     {
20300         this.fireEvent("beforeshow", this);
20301         
20302         this.hidden = false;
20303         this.el.addClass('open');
20304         
20305         Roo.get(document).on("mouseup", this.onMouseUp, this);
20306         
20307         this.fireEvent("show", this);
20308         
20309         
20310     },
20311     
20312     hide : function()
20313     {
20314         this.fireEvent("beforehide", this);
20315         
20316         this.hidden = true;
20317         this.el.removeClass('open');
20318         
20319         Roo.get(document).un("mouseup", this.onMouseUp);
20320         
20321         this.fireEvent("hide", this);
20322     },
20323     
20324     onMouseUp : function()
20325     {
20326         this.hide();
20327     }
20328     
20329 });
20330
20331  
20332  /*
20333  * - LGPL
20334  *
20335  * menu item
20336  * 
20337  */
20338 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20339
20340 /**
20341  * @class Roo.bootstrap.menu.Item
20342  * @extends Roo.bootstrap.Component
20343  * Bootstrap MenuItem class
20344  * @cfg {Boolean} submenu (true | false) default false
20345  * @cfg {String} html text of the item
20346  * @cfg {String} href the link
20347  * @cfg {Boolean} disable (true | false) default false
20348  * @cfg {Boolean} preventDefault (true | false) default true
20349  * @cfg {String} icon Font awesome icon
20350  * @cfg {String} pos Submenu align to (left | right) default right 
20351  * 
20352  * 
20353  * @constructor
20354  * Create a new Item
20355  * @param {Object} config The config object
20356  */
20357
20358
20359 Roo.bootstrap.menu.Item = function(config){
20360     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20361     this.addEvents({
20362         /**
20363          * @event mouseover
20364          * Fires when the mouse is hovering over this menu
20365          * @param {Roo.bootstrap.menu.Item} this
20366          * @param {Roo.EventObject} e
20367          */
20368         mouseover : true,
20369         /**
20370          * @event mouseout
20371          * Fires when the mouse exits this menu
20372          * @param {Roo.bootstrap.menu.Item} this
20373          * @param {Roo.EventObject} e
20374          */
20375         mouseout : true,
20376         // raw events
20377         /**
20378          * @event click
20379          * The raw click event for the entire grid.
20380          * @param {Roo.EventObject} e
20381          */
20382         click : true
20383     });
20384 };
20385
20386 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20387     
20388     submenu : false,
20389     href : '',
20390     html : '',
20391     preventDefault: true,
20392     disable : false,
20393     icon : false,
20394     pos : 'right',
20395     
20396     getAutoCreate : function()
20397     {
20398         var text = [
20399             {
20400                 tag : 'span',
20401                 cls : 'roo-menu-item-text',
20402                 html : this.html
20403             }
20404         ];
20405         
20406         if(this.icon){
20407             text.unshift({
20408                 tag : 'i',
20409                 cls : 'fa ' + this.icon
20410             })
20411         }
20412         
20413         var cfg = {
20414             tag : 'li',
20415             cn : [
20416                 {
20417                     tag : 'a',
20418                     href : this.href || '#',
20419                     cn : text
20420                 }
20421             ]
20422         };
20423         
20424         if(this.disable){
20425             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20426         }
20427         
20428         if(this.submenu){
20429             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20430             
20431             if(this.pos == 'left'){
20432                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20433             }
20434         }
20435         
20436         return cfg;
20437     },
20438     
20439     initEvents : function() 
20440     {
20441         this.el.on('mouseover', this.onMouseOver, this);
20442         this.el.on('mouseout', this.onMouseOut, this);
20443         
20444         this.el.select('a', true).first().on('click', this.onClick, this);
20445         
20446     },
20447     
20448     onClick : function(e)
20449     {
20450         if(this.preventDefault){
20451             e.preventDefault();
20452         }
20453         
20454         this.fireEvent("click", this, e);
20455     },
20456     
20457     onMouseOver : function(e)
20458     {
20459         if(this.submenu && this.pos == 'left'){
20460             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20461         }
20462         
20463         this.fireEvent("mouseover", this, e);
20464     },
20465     
20466     onMouseOut : function(e)
20467     {
20468         this.fireEvent("mouseout", this, e);
20469     }
20470 });
20471
20472  
20473
20474  /*
20475  * - LGPL
20476  *
20477  * menu separator
20478  * 
20479  */
20480 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20481
20482 /**
20483  * @class Roo.bootstrap.menu.Separator
20484  * @extends Roo.bootstrap.Component
20485  * Bootstrap Separator class
20486  * 
20487  * @constructor
20488  * Create a new Separator
20489  * @param {Object} config The config object
20490  */
20491
20492
20493 Roo.bootstrap.menu.Separator = function(config){
20494     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20495 };
20496
20497 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20498     
20499     getAutoCreate : function(){
20500         var cfg = {
20501             tag : 'li',
20502             cls: 'divider'
20503         };
20504         
20505         return cfg;
20506     }
20507    
20508 });
20509
20510  
20511
20512