91c97e1233e6d0516af1b325c7505d35ff5fa42f
[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     tooltip : null,
43     /**
44      * Initialize Events for the element
45      */
46     initEvents : function() { },
47     
48     xattr : false,
49     
50     parentId : false,
51     
52     can_build_overlaid : true,
53     
54     dataId : false,
55     
56     name : false,
57     
58     parent: function() {
59         // returns the parent component..
60         return Roo.ComponentMgr.get(this.parentId)
61         
62         
63     },
64     
65     // private
66     onRender : function(ct, position)
67     {
68        // Roo.log("Call onRender: " + this.xtype);
69         
70         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
71         
72         if(this.el){
73             if (this.el.attr('xtype')) {
74                 this.el.attr('xtypex', this.el.attr('xtype'));
75                 this.el.dom.removeAttribute('xtype');
76                 
77                 this.initEvents();
78             }
79             
80             return;
81         }
82         
83          
84         
85         var cfg = Roo.apply({},  this.getAutoCreate());
86         cfg.id = Roo.id();
87         
88         // fill in the extra attributes 
89         if (this.xattr && typeof(this.xattr) =='object') {
90             for (var i in this.xattr) {
91                 cfg[i] = this.xattr[i];
92             }
93         }
94         
95         if(this.dataId){
96             cfg.dataId = this.dataId;
97         }
98         
99         if (this.cls) {
100             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
101         }
102         
103         if (this.style) { // fixme needs to support more complex style data.
104             cfg.style = this.style;
105         }
106         
107         if(this.name){
108             cfg.name = this.name;
109         }
110         
111        
112         
113         this.el = ct.createChild(cfg, position);
114         
115         if (this.tooltip) {
116             this.tooltipEl().attr('tooltip', this.tooltip);
117         }
118         
119         if(this.tabIndex !== undefined){
120             this.el.dom.setAttribute('tabIndex', this.tabIndex);
121         }
122         this.initEvents();
123         
124         
125     },
126     /**
127      * Fetch the element to add children to
128      * @return {Roo.Element} defaults to this.el
129      */
130     getChildContainer : function()
131     {
132         return this.el;
133     },
134     /**
135      * Fetch the element to display the tooltip on.
136      * @return {Roo.Element} defaults to this.el
137      */
138     tooltipEl : function()
139     {
140         return this.el;
141     },
142         
143     addxtype  : function(tree,cntr)
144     {
145         var cn = this;
146         
147         cn = Roo.factory(tree);
148            
149         cn.parentType = this.xtype; //??
150         cn.parentId = this.id;
151         
152         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
153         
154         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
155         
156         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
157         
158         var build_from_html =  Roo.XComponent.build_from_html;
159           
160         var is_body  = (tree.xtype == 'Body') ;
161           
162         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
163           
164         var self_cntr_el = Roo.get(this[cntr](false));
165         
166         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
167             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
168                 return this.addxtypeChild(tree,cntr);
169             }
170             
171             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
172                 
173             if(echild){
174                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
175             }
176             
177             Roo.log('skipping render');
178             return cn;
179             
180         }
181         
182         var ret = false;
183         
184         while (true) {
185             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
186             
187             if (!echild) {
188                 break;
189             }
190             
191             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
192                 break;
193             }
194             
195             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
196         }
197         return ret;
198     },
199     
200     addxtypeChild : function (tree, cntr)
201     {
202         Roo.log('addxtypeChild:' + cntr);
203         var cn = this;
204         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
205         
206         
207         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
208                     (typeof(tree['flexy:foreach']) != 'undefined');
209           
210         
211         
212          skip_children = false;
213         // render the element if it's not BODY.
214         if (tree.xtype != 'Body') {
215            
216             cn = Roo.factory(tree);
217            
218             cn.parentType = this.xtype; //??
219             cn.parentId = this.id;
220             
221             var build_from_html =  Roo.XComponent.build_from_html;
222             
223             
224             // does the container contain child eleemnts with 'xtype' attributes.
225             // that match this xtype..
226             // note - when we render we create these as well..
227             // so we should check to see if body has xtype set.
228             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
229                
230                 var self_cntr_el = Roo.get(this[cntr](false));
231                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
232                 
233                 
234                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
235                 // and are not displayed -this causes this to use up the wrong element when matching.
236                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
237                 
238                 
239                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
240                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
241                   
242                   
243                   
244                     cn.el = echild;
245                   //  Roo.log("GOT");
246                     //echild.dom.removeAttribute('xtype');
247                 } else {
248                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
249                     Roo.log(self_cntr_el);
250                     Roo.log(echild);
251                     Roo.log(cn);
252                 }
253             }
254            
255             
256            
257             // if object has flexy:if - then it may or may not be rendered.
258             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
259                 // skip a flexy if element.
260                 Roo.log('skipping render');
261                 Roo.log(tree);
262                 if (!cn.el) {
263                     Roo.log('skipping all children');
264                     skip_children = true;
265                 }
266                 
267              } else {
268                  
269                 // actually if flexy:foreach is found, we really want to create 
270                 // multiple copies here...
271                 //Roo.log('render');
272                 //Roo.log(this[cntr]());
273                 cn.render(this[cntr](true));
274              }
275             // then add the element..
276         }
277         
278         
279         // handle the kids..
280         
281         var nitems = [];
282         /*
283         if (typeof (tree.menu) != 'undefined') {
284             tree.menu.parentType = cn.xtype;
285             tree.menu.triggerEl = cn.el;
286             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
287             
288         }
289         */
290         if (!tree.items || !tree.items.length) {
291             cn.items = nitems;
292             return cn;
293         }
294         var items = tree.items;
295         delete tree.items;
296         
297         //Roo.log(items.length);
298             // add the items..
299         if (!skip_children) {    
300             for(var i =0;i < items.length;i++) {
301                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
302             }
303         }
304         
305         cn.items = nitems;
306         
307         return cn;
308     }
309     
310     
311     
312     
313 });
314
315  /*
316  * - LGPL
317  *
318  * Body
319  * 
320  */
321
322 /**
323  * @class Roo.bootstrap.Body
324  * @extends Roo.bootstrap.Component
325  * Bootstrap Body class
326  * 
327  * @constructor
328  * Create a new body
329  * @param {Object} config The config object
330  */
331
332 Roo.bootstrap.Body = function(config){
333     Roo.bootstrap.Body.superclass.constructor.call(this, config);
334     this.el = Roo.get(document.body);
335     if (this.cls && this.cls.length) {
336         Roo.get(document.body).addClass(this.cls);
337     }
338 };
339
340 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
341       
342         autoCreate : {
343         cls: 'container'
344     },
345     onRender : function(ct, position)
346     {
347        /* Roo.log("Roo.bootstrap.Body - onRender");
348         if (this.cls && this.cls.length) {
349             Roo.get(document.body).addClass(this.cls);
350         }
351         // style??? xttr???
352         */
353     }
354     
355     
356  
357    
358 });
359
360  /*
361  * - LGPL
362  *
363  * button group
364  * 
365  */
366
367
368 /**
369  * @class Roo.bootstrap.ButtonGroup
370  * @extends Roo.bootstrap.Component
371  * Bootstrap ButtonGroup class
372  * @cfg {String} size lg | sm | xs (default empty normal)
373  * @cfg {String} align vertical | justified  (default none)
374  * @cfg {String} direction up | down (default down)
375  * @cfg {Boolean} toolbar false | true
376  * @cfg {Boolean} btn true | false
377  * 
378  * 
379  * @constructor
380  * Create a new Input
381  * @param {Object} config The config object
382  */
383
384 Roo.bootstrap.ButtonGroup = function(config){
385     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
386 };
387
388 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
389     
390     size: '',
391     align: '',
392     direction: '',
393     toolbar: false,
394     btn: true,
395
396     getAutoCreate : function(){
397         var cfg = {
398             cls: 'btn-group',
399             html : null
400         }
401         
402         cfg.html = this.html || cfg.html;
403         
404         if (this.toolbar) {
405             cfg = {
406                 cls: 'btn-toolbar',
407                 html: null
408             }
409             
410             return cfg;
411         }
412         
413         if (['vertical','justified'].indexOf(this.align)!==-1) {
414             cfg.cls = 'btn-group-' + this.align;
415             
416             if (this.align == 'justified') {
417                 console.log(this.items);
418             }
419         }
420         
421         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
422             cfg.cls += ' btn-group-' + this.size;
423         }
424         
425         if (this.direction == 'up') {
426             cfg.cls += ' dropup' ;
427         }
428         
429         return cfg;
430     }
431    
432 });
433
434  /*
435  * - LGPL
436  *
437  * button
438  * 
439  */
440
441 /**
442  * @class Roo.bootstrap.Button
443  * @extends Roo.bootstrap.Component
444  * Bootstrap Button class
445  * @cfg {String} html The button content
446  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
447  * @cfg {String} size empty | lg | sm | xs
448  * @cfg {String} tag empty | a | input | submit
449  * @cfg {String} href empty or href
450  * @cfg {Boolean} disabled false | true
451  * @cfg {Boolean} isClose false | true
452  * @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
453  * @cfg {String} badge text for badge
454  * @cfg {String} theme default (or empty) | glow
455  * @cfg {Boolean} inverse false | true
456  * @cfg {Boolean} toggle false | true
457  * @cfg {String} ontext text for on toggle state
458  * @cfg {String} offtext text for off toggle state
459  * @cfg {Boolean} defaulton true | false
460  * @cfg {Boolean} preventDefault (true | false) default true
461  * @cfg {Boolean} removeClass true | false remove the standard class..
462  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
463  * 
464  * @constructor
465  * Create a new button
466  * @param {Object} config The config object
467  */
468
469
470 Roo.bootstrap.Button = function(config){
471     Roo.bootstrap.Button.superclass.constructor.call(this, config);
472     this.addEvents({
473         // raw events
474         /**
475          * @event click
476          * When a butotn is pressed
477          * @param {Roo.EventObject} e
478          */
479         "click" : true,
480          /**
481          * @event toggle
482          * After the button has been toggles
483          * @param {Roo.EventObject} e
484          * @param {boolean} pressed (also available as button.pressed)
485          */
486         "toggle" : true
487     });
488 };
489
490 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
491     html: false,
492     active: false,
493     weight: '',
494     size: '',
495     tag: 'button',
496     href: '',
497     disabled: false,
498     isClose: false,
499     glyphicon: '',
500     badge: '',
501     theme: 'default',
502     inverse: false,
503     
504     toggle: false,
505     ontext: 'ON',
506     offtext: 'OFF',
507     defaulton: true,
508     preventDefault: true,
509     removeClass: false,
510     name: false,
511     target: false,
512     
513     
514     pressed : null,
515      
516     
517     getAutoCreate : function(){
518         
519         var cfg = {
520             tag : 'button',
521             cls : 'roo-button',
522             html: ''
523         };
524         
525         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
526             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
527             this.tag = 'button';
528         } else {
529             cfg.tag = this.tag;
530         }
531         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
532         
533         if (this.toggle == true) {
534             cfg={
535                 tag: 'div',
536                 cls: 'slider-frame roo-button',
537                 cn: [
538                     {
539                         tag: 'span',
540                         'data-on-text':'ON',
541                         'data-off-text':'OFF',
542                         cls: 'slider-button',
543                         html: this.offtext
544                     }
545                 ]
546             };
547             
548             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
549                 cfg.cls += ' '+this.weight;
550             }
551             
552             return cfg;
553         }
554         
555         if (this.isClose) {
556             cfg.cls += ' close';
557             
558             cfg["aria-hidden"] = true;
559             
560             cfg.html = "&times;";
561             
562             return cfg;
563         }
564         
565          
566         if (this.theme==='default') {
567             cfg.cls = 'btn roo-button';
568             
569             //if (this.parentType != 'Navbar') {
570             this.weight = this.weight.length ?  this.weight : 'default';
571             //}
572             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
573                 
574                 cfg.cls += ' btn-' + this.weight;
575             }
576         } else if (this.theme==='glow') {
577             
578             cfg.tag = 'a';
579             cfg.cls = 'btn-glow roo-button';
580             
581             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
582                 
583                 cfg.cls += ' ' + this.weight;
584             }
585         }
586    
587         
588         if (this.inverse) {
589             this.cls += ' inverse';
590         }
591         
592         
593         if (this.active) {
594             cfg.cls += ' active';
595         }
596         
597         if (this.disabled) {
598             cfg.disabled = 'disabled';
599         }
600         
601         if (this.items) {
602             Roo.log('changing to ul' );
603             cfg.tag = 'ul';
604             this.glyphicon = 'caret';
605         }
606         
607         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
608          
609         //gsRoo.log(this.parentType);
610         if (this.parentType === 'Navbar' && !this.parent().bar) {
611             Roo.log('changing to li?');
612             
613             cfg.tag = 'li';
614             
615             cfg.cls = '';
616             cfg.cn =  [{
617                 tag : 'a',
618                 cls : 'roo-button',
619                 html : this.html,
620                 href : this.href || '#'
621             }];
622             if (this.menu) {
623                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
624                 cfg.cls += ' dropdown';
625             }   
626             
627             delete cfg.html;
628             
629         }
630         
631        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
632         
633         if (this.glyphicon) {
634             cfg.html = ' ' + cfg.html;
635             
636             cfg.cn = [
637                 {
638                     tag: 'span',
639                     cls: 'glyphicon glyphicon-' + this.glyphicon
640                 }
641             ];
642         }
643         
644         if (this.badge) {
645             cfg.html += ' ';
646             
647             cfg.tag = 'a';
648             
649 //            cfg.cls='btn roo-button';
650             
651             cfg.href=this.href;
652             
653             var value = cfg.html;
654             
655             if(this.glyphicon){
656                 value = {
657                             tag: 'span',
658                             cls: 'glyphicon glyphicon-' + this.glyphicon,
659                             html: this.html
660                         };
661                 
662             }
663             
664             cfg.cn = [
665                 value,
666                 {
667                     tag: 'span',
668                     cls: 'badge',
669                     html: this.badge
670                 }
671             ];
672             
673             cfg.html='';
674         }
675         
676         if (this.menu) {
677             cfg.cls += ' dropdown';
678             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
679         }
680         
681         if (cfg.tag !== 'a' && this.href !== '') {
682             throw "Tag must be a to set href.";
683         } else if (this.href.length > 0) {
684             cfg.href = this.href;
685         }
686         
687         if(this.removeClass){
688             cfg.cls = '';
689         }
690         
691         if(this.target){
692             cfg.target = this.target;
693         }
694         
695         return cfg;
696     },
697     initEvents: function() {
698        // Roo.log('init events?');
699 //        Roo.log(this.el.dom);
700         // add the menu...
701         
702         if (typeof (this.menu) != 'undefined') {
703             this.menu.parentType = this.xtype;
704             this.menu.triggerEl = this.el;
705             this.addxtype(Roo.apply({}, this.menu));
706         }
707
708
709        if (this.el.hasClass('roo-button')) {
710             this.el.on('click', this.onClick, this);
711        } else {
712             this.el.select('.roo-button').on('click', this.onClick, this);
713        }
714        
715        if(this.removeClass){
716            this.el.on('click', this.onClick, this);
717        }
718        
719        this.el.enableDisplayMode();
720         
721     },
722     onClick : function(e)
723     {
724         if (this.disabled) {
725             return;
726         }
727         
728         Roo.log('button on click ');
729         if(this.preventDefault){
730             e.preventDefault();
731         }
732         if (this.pressed === true || this.pressed === false) {
733             this.pressed = !this.pressed;
734             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
735             this.fireEvent('toggle', this, e, this.pressed);
736         }
737         
738         
739         this.fireEvent('click', this, e);
740     },
741     
742     /**
743      * Enables this button
744      */
745     enable : function()
746     {
747         this.disabled = false;
748         this.el.removeClass('disabled');
749     },
750     
751     /**
752      * Disable this button
753      */
754     disable : function()
755     {
756         this.disabled = true;
757         this.el.addClass('disabled');
758     },
759      /**
760      * sets the active state on/off, 
761      * @param {Boolean} state (optional) Force a particular state
762      */
763     setActive : function(v) {
764         
765         this.el[v ? 'addClass' : 'removeClass']('active');
766     },
767      /**
768      * toggles the current active state 
769      */
770     toggleActive : function()
771     {
772        var active = this.el.hasClass('active');
773        this.setActive(!active);
774        
775         
776     },
777     setText : function(str)
778     {
779         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
780     },
781     getText : function()
782     {
783         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
784     },
785     hide: function() {
786        
787      
788         this.el.hide();   
789     },
790     show: function() {
791        
792         this.el.show();   
793     }
794     
795     
796 });
797
798  /*
799  * - LGPL
800  *
801  * column
802  * 
803  */
804
805 /**
806  * @class Roo.bootstrap.Column
807  * @extends Roo.bootstrap.Component
808  * Bootstrap Column class
809  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
810  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
811  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
812  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
813  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
814  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
815  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
816  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
817  *
818  * 
819  * @cfg {Boolean} hidden (true|false) hide the element
820  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
821  * @cfg {String} fa (ban|check|...) font awesome icon
822  * @cfg {Number} fasize (1|2|....) font awsome size
823
824  * @cfg {String} icon (info-sign|check|...) glyphicon name
825
826  * @cfg {String} html content of column.
827  * 
828  * @constructor
829  * Create a new Column
830  * @param {Object} config The config object
831  */
832
833 Roo.bootstrap.Column = function(config){
834     Roo.bootstrap.Column.superclass.constructor.call(this, config);
835 };
836
837 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
838     
839     xs: false,
840     sm: false,
841     md: false,
842     lg: false,
843     xsoff: false,
844     smoff: false,
845     mdoff: false,
846     lgoff: false,
847     html: '',
848     offset: 0,
849     alert: false,
850     fa: false,
851     icon : false,
852     hidden : false,
853     fasize : 1,
854     
855     getAutoCreate : function(){
856         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
857         
858         cfg = {
859             tag: 'div',
860             cls: 'column'
861         };
862         
863         var settings=this;
864         ['xs','sm','md','lg'].map(function(size){
865             //Roo.log( size + ':' + settings[size]);
866             
867             if (settings[size+'off'] !== false) {
868                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
869             }
870             
871             if (settings[size] === false) {
872                 return;
873             }
874             Roo.log(settings[size]);
875             if (!settings[size]) { // 0 = hidden
876                 cfg.cls += ' hidden-' + size;
877                 return;
878             }
879             cfg.cls += ' col-' + size + '-' + settings[size];
880             
881         });
882         
883         if (this.hidden) {
884             cfg.cls += ' hidden';
885         }
886         
887         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
888             cfg.cls +=' alert alert-' + this.alert;
889         }
890         
891         
892         if (this.html.length) {
893             cfg.html = this.html;
894         }
895         if (this.fa) {
896             var fasize = '';
897             if (this.fasize > 1) {
898                 fasize = ' fa-' + this.fasize + 'x';
899             }
900             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
901             
902             
903         }
904         if (this.icon) {
905             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
906         }
907         
908         return cfg;
909     }
910    
911 });
912
913  
914
915  /*
916  * - LGPL
917  *
918  * page container.
919  * 
920  */
921
922
923 /**
924  * @class Roo.bootstrap.Container
925  * @extends Roo.bootstrap.Component
926  * Bootstrap Container class
927  * @cfg {Boolean} jumbotron is it a jumbotron element
928  * @cfg {String} html content of element
929  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
930  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
931  * @cfg {String} header content of header (for panel)
932  * @cfg {String} footer content of footer (for panel)
933  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
934  * @cfg {String} tag (header|aside|section) type of HTML tag.
935  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
936  * @cfg {String} fa (ban|check|...) font awesome icon
937  * @cfg {String} icon (info-sign|check|...) glyphicon name
938  * @cfg {Boolean} hidden (true|false) hide the element
939
940  *     
941  * @constructor
942  * Create a new Container
943  * @param {Object} config The config object
944  */
945
946 Roo.bootstrap.Container = function(config){
947     Roo.bootstrap.Container.superclass.constructor.call(this, config);
948 };
949
950 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
951     
952     jumbotron : false,
953     well: '',
954     panel : '',
955     header: '',
956     footer : '',
957     sticky: '',
958     tag : false,
959     alert : false,
960     fa: false,
961     icon : false,
962   
963      
964     getChildContainer : function() {
965         
966         if(!this.el){
967             return false;
968         }
969         
970         if (this.panel.length) {
971             return this.el.select('.panel-body',true).first();
972         }
973         
974         return this.el;
975     },
976     
977     
978     getAutoCreate : function(){
979         
980         var cfg = {
981             tag : this.tag || 'div',
982             html : '',
983             cls : ''
984         };
985         if (this.jumbotron) {
986             cfg.cls = 'jumbotron';
987         }
988         
989         
990         
991         // - this is applied by the parent..
992         //if (this.cls) {
993         //    cfg.cls = this.cls + '';
994         //}
995         
996         if (this.sticky.length) {
997             
998             var bd = Roo.get(document.body);
999             if (!bd.hasClass('bootstrap-sticky')) {
1000                 bd.addClass('bootstrap-sticky');
1001                 Roo.select('html',true).setStyle('height', '100%');
1002             }
1003              
1004             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1005         }
1006         
1007         
1008         if (this.well.length) {
1009             switch (this.well) {
1010                 case 'lg':
1011                 case 'sm':
1012                     cfg.cls +=' well well-' +this.well;
1013                     break;
1014                 default:
1015                     cfg.cls +=' well';
1016                     break;
1017             }
1018         }
1019         
1020         if (this.hidden) {
1021             cfg.cls += ' hidden';
1022         }
1023         
1024         
1025         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1026             cfg.cls +=' alert alert-' + this.alert;
1027         }
1028         
1029         var body = cfg;
1030         
1031         if (this.panel.length) {
1032             cfg.cls += ' panel panel-' + this.panel;
1033             cfg.cn = [];
1034             if (this.header.length) {
1035                 cfg.cn.push({
1036                     
1037                     cls : 'panel-heading',
1038                     cn : [{
1039                         tag: 'h3',
1040                         cls : 'panel-title',
1041                         html : this.header
1042                     }]
1043                     
1044                 });
1045             }
1046             body = false;
1047             cfg.cn.push({
1048                 cls : 'panel-body',
1049                 html : this.html
1050             });
1051             
1052             
1053             if (this.footer.length) {
1054                 cfg.cn.push({
1055                     cls : 'panel-footer',
1056                     html : this.footer
1057                     
1058                 });
1059             }
1060             
1061         }
1062         
1063         if (body) {
1064             body.html = this.html || cfg.html;
1065             // prefix with the icons..
1066             if (this.fa) {
1067                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1068             }
1069             if (this.icon) {
1070                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1071             }
1072             
1073             
1074         }
1075         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1076             cfg.cls =  'container';
1077         }
1078         
1079         return cfg;
1080     },
1081     
1082     titleEl : function()
1083     {
1084         if(!this.el || !this.panel.length || !this.header.length){
1085             return;
1086         }
1087         
1088         return this.el.select('.panel-title',true).first();
1089     },
1090     
1091     setTitle : function(v)
1092     {
1093         var titleEl = this.titleEl();
1094         
1095         if(!titleEl){
1096             return;
1097         }
1098         
1099         titleEl.dom.innerHTML = v;
1100     },
1101     
1102     getTitle : function()
1103     {
1104         
1105         var titleEl = this.titleEl();
1106         
1107         if(!titleEl){
1108             return '';
1109         }
1110         
1111         return titleEl.dom.innerHTML;
1112     }
1113    
1114 });
1115
1116  /*
1117  * - LGPL
1118  *
1119  * image
1120  * 
1121  */
1122
1123
1124 /**
1125  * @class Roo.bootstrap.Img
1126  * @extends Roo.bootstrap.Component
1127  * Bootstrap Img class
1128  * @cfg {Boolean} imgResponsive false | true
1129  * @cfg {String} border rounded | circle | thumbnail
1130  * @cfg {String} src image source
1131  * @cfg {String} alt image alternative text
1132  * @cfg {String} href a tag href
1133  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1134  * 
1135  * @constructor
1136  * Create a new Input
1137  * @param {Object} config The config object
1138  */
1139
1140 Roo.bootstrap.Img = function(config){
1141     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1142     
1143     this.addEvents({
1144         // img events
1145         /**
1146          * @event click
1147          * The img click event for the img.
1148          * @param {Roo.EventObject} e
1149          */
1150         "click" : true
1151     });
1152 };
1153
1154 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1155     
1156     imgResponsive: true,
1157     border: '',
1158     src: '',
1159     href: false,
1160     target: false,
1161
1162     getAutoCreate : function(){
1163         
1164         var cfg = {
1165             tag: 'img',
1166             cls: (this.imgResponsive) ? 'img-responsive' : '',
1167             html : null
1168         }
1169         
1170         cfg.html = this.html || cfg.html;
1171         
1172         cfg.src = this.src || cfg.src;
1173         
1174         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1175             cfg.cls += ' img-' + this.border;
1176         }
1177         
1178         if(this.alt){
1179             cfg.alt = this.alt;
1180         }
1181         
1182         if(this.href){
1183             var a = {
1184                 tag: 'a',
1185                 href: this.href,
1186                 cn: [
1187                     cfg
1188                 ]
1189             }
1190             
1191             if(this.target){
1192                 a.target = this.target;
1193             }
1194             
1195         }
1196         
1197         
1198         return (this.href) ? a : cfg;
1199     },
1200     
1201     initEvents: function() {
1202         
1203         if(!this.href){
1204             this.el.on('click', this.onClick, this);
1205         }
1206     },
1207     
1208     onClick : function(e)
1209     {
1210         Roo.log('img onclick');
1211         this.fireEvent('click', this, e);
1212     }
1213    
1214 });
1215
1216  /*
1217  * - LGPL
1218  *
1219  * image
1220  * 
1221  */
1222
1223
1224 /**
1225  * @class Roo.bootstrap.Link
1226  * @extends Roo.bootstrap.Component
1227  * Bootstrap Link Class
1228  * @cfg {String} alt image alternative text
1229  * @cfg {String} href a tag href
1230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1231  * @cfg {String} html the content of the link.
1232  * @cfg {String} anchor name for the anchor link
1233
1234  * @cfg {Boolean} preventDefault (true | false) default false
1235
1236  * 
1237  * @constructor
1238  * Create a new Input
1239  * @param {Object} config The config object
1240  */
1241
1242 Roo.bootstrap.Link = function(config){
1243     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1244     
1245     this.addEvents({
1246         // img events
1247         /**
1248          * @event click
1249          * The img click event for the img.
1250          * @param {Roo.EventObject} e
1251          */
1252         "click" : true
1253     });
1254 };
1255
1256 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1257     
1258     href: false,
1259     target: false,
1260     preventDefault: false,
1261     anchor : false,
1262     alt : false,
1263
1264     getAutoCreate : function()
1265     {
1266         
1267         var cfg = {
1268             tag: 'a'
1269         };
1270         // anchor's do not require html/href...
1271         if (this.anchor === false) {
1272             cfg.html = this.html || 'html-missing';
1273             cfg.href = this.href || '#';
1274         } else {
1275             cfg.name = this.anchor;
1276             if (this.html !== false) {
1277                 cfg.html = this.html;
1278             }
1279             if (this.href !== false) {
1280                 cfg.href = this.href;
1281             }
1282         }
1283         
1284         if(this.alt !== false){
1285             cfg.alt = this.alt;
1286         }
1287         
1288         
1289         if(this.target !== false) {
1290             cfg.target = this.target;
1291         }
1292         
1293         return cfg;
1294     },
1295     
1296     initEvents: function() {
1297         
1298         if(!this.href || this.preventDefault){
1299             this.el.on('click', this.onClick, this);
1300         }
1301     },
1302     
1303     onClick : function(e)
1304     {
1305         if(this.preventDefault){
1306             e.preventDefault();
1307         }
1308         //Roo.log('img onclick');
1309         this.fireEvent('click', this, e);
1310     }
1311    
1312 });
1313
1314  /*
1315  * - LGPL
1316  *
1317  * header
1318  * 
1319  */
1320
1321 /**
1322  * @class Roo.bootstrap.Header
1323  * @extends Roo.bootstrap.Component
1324  * Bootstrap Header class
1325  * @cfg {String} html content of header
1326  * @cfg {Number} level (1|2|3|4|5|6) default 1
1327  * 
1328  * @constructor
1329  * Create a new Header
1330  * @param {Object} config The config object
1331  */
1332
1333
1334 Roo.bootstrap.Header  = function(config){
1335     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1336 };
1337
1338 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1339     
1340     //href : false,
1341     html : false,
1342     level : 1,
1343     
1344     
1345     
1346     getAutoCreate : function(){
1347         
1348         var cfg = {
1349             tag: 'h' + (1 *this.level),
1350             html: this.html || 'fill in html'
1351         } ;
1352         
1353         return cfg;
1354     }
1355    
1356 });
1357
1358  
1359
1360  /*
1361  * Based on:
1362  * Ext JS Library 1.1.1
1363  * Copyright(c) 2006-2007, Ext JS, LLC.
1364  *
1365  * Originally Released Under LGPL - original licence link has changed is not relivant.
1366  *
1367  * Fork - LGPL
1368  * <script type="text/javascript">
1369  */
1370  
1371 /**
1372  * @class Roo.bootstrap.MenuMgr
1373  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1374  * @singleton
1375  */
1376 Roo.bootstrap.MenuMgr = function(){
1377    var menus, active, groups = {}, attached = false, lastShow = new Date();
1378
1379    // private - called when first menu is created
1380    function init(){
1381        menus = {};
1382        active = new Roo.util.MixedCollection();
1383        Roo.get(document).addKeyListener(27, function(){
1384            if(active.length > 0){
1385                hideAll();
1386            }
1387        });
1388    }
1389
1390    // private
1391    function hideAll(){
1392        if(active && active.length > 0){
1393            var c = active.clone();
1394            c.each(function(m){
1395                m.hide();
1396            });
1397        }
1398    }
1399
1400    // private
1401    function onHide(m){
1402        active.remove(m);
1403        if(active.length < 1){
1404            Roo.get(document).un("mouseup", onMouseDown);
1405             
1406            attached = false;
1407        }
1408    }
1409
1410    // private
1411    function onShow(m){
1412        var last = active.last();
1413        lastShow = new Date();
1414        active.add(m);
1415        if(!attached){
1416           Roo.get(document).on("mouseup", onMouseDown);
1417            
1418            attached = true;
1419        }
1420        if(m.parentMenu){
1421           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1422           m.parentMenu.activeChild = m;
1423        }else if(last && last.isVisible()){
1424           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1425        }
1426    }
1427
1428    // private
1429    function onBeforeHide(m){
1430        if(m.activeChild){
1431            m.activeChild.hide();
1432        }
1433        if(m.autoHideTimer){
1434            clearTimeout(m.autoHideTimer);
1435            delete m.autoHideTimer;
1436        }
1437    }
1438
1439    // private
1440    function onBeforeShow(m){
1441        var pm = m.parentMenu;
1442        if(!pm && !m.allowOtherMenus){
1443            hideAll();
1444        }else if(pm && pm.activeChild && active != m){
1445            pm.activeChild.hide();
1446        }
1447    }
1448
1449    // private
1450    function onMouseDown(e){
1451         Roo.log("on MouseDown");
1452         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1453            hideAll();
1454         }
1455         
1456         
1457    }
1458
1459    // private
1460    function onBeforeCheck(mi, state){
1461        if(state){
1462            var g = groups[mi.group];
1463            for(var i = 0, l = g.length; i < l; i++){
1464                if(g[i] != mi){
1465                    g[i].setChecked(false);
1466                }
1467            }
1468        }
1469    }
1470
1471    return {
1472
1473        /**
1474         * Hides all menus that are currently visible
1475         */
1476        hideAll : function(){
1477             hideAll();  
1478        },
1479
1480        // private
1481        register : function(menu){
1482            if(!menus){
1483                init();
1484            }
1485            menus[menu.id] = menu;
1486            menu.on("beforehide", onBeforeHide);
1487            menu.on("hide", onHide);
1488            menu.on("beforeshow", onBeforeShow);
1489            menu.on("show", onShow);
1490            var g = menu.group;
1491            if(g && menu.events["checkchange"]){
1492                if(!groups[g]){
1493                    groups[g] = [];
1494                }
1495                groups[g].push(menu);
1496                menu.on("checkchange", onCheck);
1497            }
1498        },
1499
1500         /**
1501          * Returns a {@link Roo.menu.Menu} object
1502          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1503          * be used to generate and return a new Menu instance.
1504          */
1505        get : function(menu){
1506            if(typeof menu == "string"){ // menu id
1507                return menus[menu];
1508            }else if(menu.events){  // menu instance
1509                return menu;
1510            }
1511            /*else if(typeof menu.length == 'number'){ // array of menu items?
1512                return new Roo.bootstrap.Menu({items:menu});
1513            }else{ // otherwise, must be a config
1514                return new Roo.bootstrap.Menu(menu);
1515            }
1516            */
1517            return false;
1518        },
1519
1520        // private
1521        unregister : function(menu){
1522            delete menus[menu.id];
1523            menu.un("beforehide", onBeforeHide);
1524            menu.un("hide", onHide);
1525            menu.un("beforeshow", onBeforeShow);
1526            menu.un("show", onShow);
1527            var g = menu.group;
1528            if(g && menu.events["checkchange"]){
1529                groups[g].remove(menu);
1530                menu.un("checkchange", onCheck);
1531            }
1532        },
1533
1534        // private
1535        registerCheckable : function(menuItem){
1536            var g = menuItem.group;
1537            if(g){
1538                if(!groups[g]){
1539                    groups[g] = [];
1540                }
1541                groups[g].push(menuItem);
1542                menuItem.on("beforecheckchange", onBeforeCheck);
1543            }
1544        },
1545
1546        // private
1547        unregisterCheckable : function(menuItem){
1548            var g = menuItem.group;
1549            if(g){
1550                groups[g].remove(menuItem);
1551                menuItem.un("beforecheckchange", onBeforeCheck);
1552            }
1553        }
1554    };
1555 }();/*
1556  * - LGPL
1557  *
1558  * menu
1559  * 
1560  */
1561
1562 /**
1563  * @class Roo.bootstrap.Menu
1564  * @extends Roo.bootstrap.Component
1565  * Bootstrap Menu class - container for MenuItems
1566  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1567  * 
1568  * @constructor
1569  * Create a new Menu
1570  * @param {Object} config The config object
1571  */
1572
1573
1574 Roo.bootstrap.Menu = function(config){
1575     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1576     if (this.registerMenu) {
1577         Roo.bootstrap.MenuMgr.register(this);
1578     }
1579     this.addEvents({
1580         /**
1581          * @event beforeshow
1582          * Fires before this menu is displayed
1583          * @param {Roo.menu.Menu} this
1584          */
1585         beforeshow : true,
1586         /**
1587          * @event beforehide
1588          * Fires before this menu is hidden
1589          * @param {Roo.menu.Menu} this
1590          */
1591         beforehide : true,
1592         /**
1593          * @event show
1594          * Fires after this menu is displayed
1595          * @param {Roo.menu.Menu} this
1596          */
1597         show : true,
1598         /**
1599          * @event hide
1600          * Fires after this menu is hidden
1601          * @param {Roo.menu.Menu} this
1602          */
1603         hide : true,
1604         /**
1605          * @event click
1606          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1607          * @param {Roo.menu.Menu} this
1608          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1609          * @param {Roo.EventObject} e
1610          */
1611         click : true,
1612         /**
1613          * @event mouseover
1614          * Fires when the mouse is hovering over this menu
1615          * @param {Roo.menu.Menu} this
1616          * @param {Roo.EventObject} e
1617          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1618          */
1619         mouseover : true,
1620         /**
1621          * @event mouseout
1622          * Fires when the mouse exits this menu
1623          * @param {Roo.menu.Menu} this
1624          * @param {Roo.EventObject} e
1625          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1626          */
1627         mouseout : true,
1628         /**
1629          * @event itemclick
1630          * Fires when a menu item contained in this menu is clicked
1631          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1632          * @param {Roo.EventObject} e
1633          */
1634         itemclick: true
1635     });
1636     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1637 };
1638
1639 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1640     
1641    /// html : false,
1642     //align : '',
1643     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1644     type: false,
1645     /**
1646      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1647      */
1648     registerMenu : true,
1649     
1650     menuItems :false, // stores the menu items..
1651     
1652     hidden:true,
1653     
1654     parentMenu : false,
1655     
1656     getChildContainer : function() {
1657         return this.el;  
1658     },
1659     
1660     getAutoCreate : function(){
1661          
1662         //if (['right'].indexOf(this.align)!==-1) {
1663         //    cfg.cn[1].cls += ' pull-right'
1664         //}
1665         
1666         
1667         var cfg = {
1668             tag : 'ul',
1669             cls : 'dropdown-menu' ,
1670             style : 'z-index:1000'
1671             
1672         }
1673         
1674         if (this.type === 'submenu') {
1675             cfg.cls = 'submenu active';
1676         }
1677         if (this.type === 'treeview') {
1678             cfg.cls = 'treeview-menu';
1679         }
1680         
1681         return cfg;
1682     },
1683     initEvents : function() {
1684         
1685        // Roo.log("ADD event");
1686        // Roo.log(this.triggerEl.dom);
1687         this.triggerEl.on('click', this.onTriggerPress, this);
1688         this.triggerEl.addClass('dropdown-toggle');
1689         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1690
1691         this.el.on("mouseover", this.onMouseOver, this);
1692         this.el.on("mouseout", this.onMouseOut, this);
1693         
1694         
1695     },
1696     findTargetItem : function(e){
1697         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1698         if(!t){
1699             return false;
1700         }
1701         //Roo.log(t);         Roo.log(t.id);
1702         if(t && t.id){
1703             //Roo.log(this.menuitems);
1704             return this.menuitems.get(t.id);
1705             
1706             //return this.items.get(t.menuItemId);
1707         }
1708         
1709         return false;
1710     },
1711     onClick : function(e){
1712         Roo.log("menu.onClick");
1713         var t = this.findTargetItem(e);
1714         if(!t || t.isContainer){
1715             return;
1716         }
1717         Roo.log(e);
1718         /*
1719         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1720             if(t == this.activeItem && t.shouldDeactivate(e)){
1721                 this.activeItem.deactivate();
1722                 delete this.activeItem;
1723                 return;
1724             }
1725             if(t.canActivate){
1726                 this.setActiveItem(t, true);
1727             }
1728             return;
1729             
1730             
1731         }
1732         */
1733        
1734         Roo.log('pass click event');
1735         
1736         t.onClick(e);
1737         
1738         this.fireEvent("click", this, t, e);
1739         
1740         this.hide();
1741     },
1742      onMouseOver : function(e){
1743         var t  = this.findTargetItem(e);
1744         //Roo.log(t);
1745         //if(t){
1746         //    if(t.canActivate && !t.disabled){
1747         //        this.setActiveItem(t, true);
1748         //    }
1749         //}
1750         
1751         this.fireEvent("mouseover", this, e, t);
1752     },
1753     isVisible : function(){
1754         return !this.hidden;
1755     },
1756      onMouseOut : function(e){
1757         var t  = this.findTargetItem(e);
1758         
1759         //if(t ){
1760         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1761         //        this.activeItem.deactivate();
1762         //        delete this.activeItem;
1763         //    }
1764         //}
1765         this.fireEvent("mouseout", this, e, t);
1766     },
1767     
1768     
1769     /**
1770      * Displays this menu relative to another element
1771      * @param {String/HTMLElement/Roo.Element} element The element to align to
1772      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1773      * the element (defaults to this.defaultAlign)
1774      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1775      */
1776     show : function(el, pos, parentMenu){
1777         this.parentMenu = parentMenu;
1778         if(!this.el){
1779             this.render();
1780         }
1781         this.fireEvent("beforeshow", this);
1782         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1783     },
1784      /**
1785      * Displays this menu at a specific xy position
1786      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1787      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1788      */
1789     showAt : function(xy, parentMenu, /* private: */_e){
1790         this.parentMenu = parentMenu;
1791         if(!this.el){
1792             this.render();
1793         }
1794         if(_e !== false){
1795             this.fireEvent("beforeshow", this);
1796             
1797             //xy = this.el.adjustForConstraints(xy);
1798         }
1799         //this.el.setXY(xy);
1800         //this.el.show();
1801         this.hideMenuItems();
1802         this.hidden = false;
1803         this.triggerEl.addClass('open');
1804         this.focus();
1805         this.fireEvent("show", this);
1806     },
1807     
1808     focus : function(){
1809         return;
1810         if(!this.hidden){
1811             this.doFocus.defer(50, this);
1812         }
1813     },
1814
1815     doFocus : function(){
1816         if(!this.hidden){
1817             this.focusEl.focus();
1818         }
1819     },
1820
1821     /**
1822      * Hides this menu and optionally all parent menus
1823      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1824      */
1825     hide : function(deep){
1826         
1827         this.hideMenuItems();
1828         if(this.el && this.isVisible()){
1829             this.fireEvent("beforehide", this);
1830             if(this.activeItem){
1831                 this.activeItem.deactivate();
1832                 this.activeItem = null;
1833             }
1834             this.triggerEl.removeClass('open');;
1835             this.hidden = true;
1836             this.fireEvent("hide", this);
1837         }
1838         if(deep === true && this.parentMenu){
1839             this.parentMenu.hide(true);
1840         }
1841     },
1842     
1843     onTriggerPress  : function(e)
1844     {
1845         
1846         Roo.log('trigger press');
1847         //Roo.log(e.getTarget());
1848        // Roo.log(this.triggerEl.dom);
1849         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1850             return;
1851         }
1852         if (this.isVisible()) {
1853             Roo.log('hide');
1854             this.hide();
1855         } else {
1856             this.show(this.triggerEl, false, false);
1857         }
1858         
1859         
1860     },
1861     
1862          
1863        
1864     
1865     hideMenuItems : function()
1866     {
1867         //$(backdrop).remove()
1868         Roo.select('.open',true).each(function(aa) {
1869             
1870             aa.removeClass('open');
1871           //var parent = getParent($(this))
1872           //var relatedTarget = { relatedTarget: this }
1873           
1874            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1875           //if (e.isDefaultPrevented()) return
1876            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1877         })
1878     },
1879     addxtypeChild : function (tree, cntr) {
1880         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1881           
1882         this.menuitems.add(comp);
1883         return comp;
1884
1885     },
1886     getEl : function()
1887     {
1888         Roo.log(this.el);
1889         return this.el;
1890     }
1891 });
1892
1893  
1894  /*
1895  * - LGPL
1896  *
1897  * menu item
1898  * 
1899  */
1900
1901
1902 /**
1903  * @class Roo.bootstrap.MenuItem
1904  * @extends Roo.bootstrap.Component
1905  * Bootstrap MenuItem class
1906  * @cfg {String} html the menu label
1907  * @cfg {String} href the link
1908  * @cfg {Boolean} preventDefault (true | false) default true
1909  * @cfg {Boolean} isContainer (true | false) default false
1910  * 
1911  * 
1912  * @constructor
1913  * Create a new MenuItem
1914  * @param {Object} config The config object
1915  */
1916
1917
1918 Roo.bootstrap.MenuItem = function(config){
1919     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1920     this.addEvents({
1921         // raw events
1922         /**
1923          * @event click
1924          * The raw click event for the entire grid.
1925          * @param {Roo.EventObject} e
1926          */
1927         "click" : true
1928     });
1929 };
1930
1931 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1932     
1933     href : false,
1934     html : false,
1935     preventDefault: true,
1936     isContainer : false,
1937     
1938     getAutoCreate : function(){
1939         
1940         if(this.isContainer){
1941             return {
1942                 tag: 'li',
1943                 cls: 'dropdown-menu-item'
1944             };
1945         }
1946         
1947         var cfg= {
1948             tag: 'li',
1949             cls: 'dropdown-menu-item',
1950             cn: [
1951                     {
1952                         tag : 'a',
1953                         href : '#',
1954                         html : 'Link'
1955                     }
1956                 ]
1957         };
1958         if (this.parent().type == 'treeview') {
1959             cfg.cls = 'treeview-menu';
1960         }
1961         
1962         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1963         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1964         return cfg;
1965     },
1966     
1967     initEvents: function() {
1968         
1969         //this.el.select('a').on('click', this.onClick, this);
1970         
1971     },
1972     onClick : function(e)
1973     {
1974         Roo.log('item on click ');
1975         //if(this.preventDefault){
1976         //    e.preventDefault();
1977         //}
1978         //this.parent().hideMenuItems();
1979         
1980         this.fireEvent('click', this, e);
1981     },
1982     getEl : function()
1983     {
1984         return this.el;
1985     }
1986 });
1987
1988  
1989
1990  /*
1991  * - LGPL
1992  *
1993  * menu separator
1994  * 
1995  */
1996
1997
1998 /**
1999  * @class Roo.bootstrap.MenuSeparator
2000  * @extends Roo.bootstrap.Component
2001  * Bootstrap MenuSeparator class
2002  * 
2003  * @constructor
2004  * Create a new MenuItem
2005  * @param {Object} config The config object
2006  */
2007
2008
2009 Roo.bootstrap.MenuSeparator = function(config){
2010     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2011 };
2012
2013 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2014     
2015     getAutoCreate : function(){
2016         var cfg = {
2017             cls: 'divider',
2018             tag : 'li'
2019         };
2020         
2021         return cfg;
2022     }
2023    
2024 });
2025
2026  
2027
2028  
2029 /*
2030 <div class="modal fade">
2031   <div class="modal-dialog">
2032     <div class="modal-content">
2033       <div class="modal-header">
2034         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2035         <h4 class="modal-title">Modal title</h4>
2036       </div>
2037       <div class="modal-body">
2038         <p>One fine body&hellip;</p>
2039       </div>
2040       <div class="modal-footer">
2041         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2042         <button type="button" class="btn btn-primary">Save changes</button>
2043       </div>
2044     </div><!-- /.modal-content -->
2045   </div><!-- /.modal-dialog -->
2046 </div><!-- /.modal -->
2047 */
2048 /*
2049  * - LGPL
2050  *
2051  * page contgainer.
2052  * 
2053  */
2054
2055 /**
2056  * @class Roo.bootstrap.Modal
2057  * @extends Roo.bootstrap.Component
2058  * Bootstrap Modal class
2059  * @cfg {String} title Title of dialog
2060  * @cfg {Boolean} specificTitle (true|false) default false
2061  * @cfg {Array} buttons Array of buttons or standard button set..
2062  * @cfg {String} buttonPosition (left|right|center) default right
2063  * @cfg {Boolean} animate (true | false) default true
2064  * 
2065  * @constructor
2066  * Create a new Modal Dialog
2067  * @param {Object} config The config object
2068  */
2069
2070 Roo.bootstrap.Modal = function(config){
2071     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2072     this.addEvents({
2073         // raw events
2074         /**
2075          * @event btnclick
2076          * The raw btnclick event for the button
2077          * @param {Roo.EventObject} e
2078          */
2079         "btnclick" : true
2080     });
2081     this.buttons = this.buttons || [];
2082 };
2083
2084 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2085     
2086     title : 'test dialog',
2087    
2088     buttons : false,
2089     
2090     // set on load...
2091     body:  false,
2092     
2093     specificTitle: false,
2094     
2095     buttonPosition: 'right',
2096     
2097     animate : true,
2098     
2099     onRender : function(ct, position)
2100     {
2101         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2102      
2103         if(!this.el){
2104             var cfg = Roo.apply({},  this.getAutoCreate());
2105             cfg.id = Roo.id();
2106             //if(!cfg.name){
2107             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2108             //}
2109             //if (!cfg.name.length) {
2110             //    delete cfg.name;
2111            // }
2112             if (this.cls) {
2113                 cfg.cls += ' ' + this.cls;
2114             }
2115             if (this.style) {
2116                 cfg.style = this.style;
2117             }
2118             this.el = Roo.get(document.body).createChild(cfg, position);
2119         }
2120         //var type = this.el.dom.type;
2121         
2122         if(this.tabIndex !== undefined){
2123             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2124         }
2125         
2126         
2127         
2128         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2129         this.maskEl.enableDisplayMode("block");
2130         this.maskEl.hide();
2131         //this.el.addClass("x-dlg-modal");
2132     
2133         if (this.buttons.length) {
2134             Roo.each(this.buttons, function(bb) {
2135                 b = Roo.apply({}, bb);
2136                 b.xns = b.xns || Roo.bootstrap;
2137                 b.xtype = b.xtype || 'Button';
2138                 if (typeof(b.listeners) == 'undefined') {
2139                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2140                 }
2141                 
2142                 var btn = Roo.factory(b);
2143                 
2144                 btn.onRender(this.el.select('.modal-footer div').first());
2145                 
2146             },this);
2147         }
2148         // render the children.
2149         var nitems = [];
2150         
2151         if(typeof(this.items) != 'undefined'){
2152             var items = this.items;
2153             delete this.items;
2154
2155             for(var i =0;i < items.length;i++) {
2156                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2157             }
2158         }
2159         
2160         this.items = nitems;
2161         
2162         this.body = this.el.select('.modal-body',true).first();
2163         this.close = this.el.select('.modal-header .close', true).first();
2164         this.footer = this.el.select('.modal-footer',true).first();
2165         this.initEvents();
2166         //this.el.addClass([this.fieldClass, this.cls]);
2167         
2168     },
2169     getAutoCreate : function(){
2170         
2171         
2172         var bdy = {
2173                 cls : 'modal-body',
2174                 html : this.html || ''
2175         };
2176         
2177         var title = {
2178             tag: 'h4',
2179             cls : 'modal-title',
2180             html : this.title
2181         };
2182         
2183         if(this.specificTitle){
2184             title = this.title;
2185         };
2186         
2187         var modal = {
2188             cls: "modal",
2189             style : 'display: none',
2190             cn : [
2191                 {
2192                     cls: "modal-dialog",
2193                     cn : [
2194                         {
2195                             cls : "modal-content",
2196                             cn : [
2197                                 {
2198                                     cls : 'modal-header',
2199                                     cn : [
2200                                         {
2201                                             tag: 'button',
2202                                             cls : 'close',
2203                                             html : '&times'
2204                                         },
2205                                         title
2206                                     ]
2207                                 },
2208                                 bdy,
2209                                 {
2210                                     cls : 'modal-footer',
2211                                     cn : [
2212                                         {
2213                                             tag: 'div',
2214                                             cls: 'btn-' + this.buttonPosition
2215                                         }
2216                                     ]
2217                                     
2218                                 }
2219                                 
2220                                 
2221                             ]
2222                             
2223                         }
2224                     ]
2225                         
2226                 }
2227             ]
2228         };
2229         
2230         if(this.animate){
2231             modal.cls += ' fade';
2232         }
2233         
2234         return modal;
2235           
2236     },
2237     getChildContainer : function() {
2238          
2239          return this.el.select('.modal-body',true).first();
2240         
2241     },
2242     getButtonContainer : function() {
2243          return this.el.select('.modal-footer div',true).first();
2244         
2245     },
2246     initEvents : function()
2247     {
2248         this.el.select('.modal-header .close').on('click', this.hide, this);
2249 //        
2250 //        this.addxtype(this);
2251     },
2252     show : function() {
2253         
2254         if (!this.rendered) {
2255             this.render();
2256         }
2257         
2258         this.el.setStyle('display', 'block');
2259         
2260         if(this.animate){
2261             var _this = this;
2262             (function(){ _this.el.addClass('in'); }).defer(50);
2263         }else{
2264             this.el.addClass('in');
2265         }
2266         
2267         Roo.get(document.body).addClass("x-body-masked");
2268         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2269         this.maskEl.show();
2270         this.el.setStyle('zIndex', '10001');
2271         this.fireEvent('show', this);
2272         
2273         
2274     },
2275     hide : function()
2276     {
2277         this.maskEl.hide();
2278         Roo.get(document.body).removeClass("x-body-masked");
2279         this.el.removeClass('in');
2280         
2281         if(this.animate){
2282             var _this = this;
2283             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2284         }else{
2285             this.el.setStyle('display', 'none');
2286         }
2287         
2288         this.fireEvent('hide', this);
2289     },
2290     
2291     addButton : function(str, cb)
2292     {
2293          
2294         
2295         var b = Roo.apply({}, { html : str } );
2296         b.xns = b.xns || Roo.bootstrap;
2297         b.xtype = b.xtype || 'Button';
2298         if (typeof(b.listeners) == 'undefined') {
2299             b.listeners = { click : cb.createDelegate(this)  };
2300         }
2301         
2302         var btn = Roo.factory(b);
2303            
2304         btn.onRender(this.el.select('.modal-footer div').first());
2305         
2306         return btn;   
2307        
2308     },
2309     
2310     setDefaultButton : function(btn)
2311     {
2312         //this.el.select('.modal-footer').()
2313     },
2314     resizeTo: function(w,h)
2315     {
2316         // skip..
2317     },
2318     setContentSize  : function(w, h)
2319     {
2320         
2321     },
2322     onButtonClick: function(btn,e)
2323     {
2324         //Roo.log([a,b,c]);
2325         this.fireEvent('btnclick', btn.name, e);
2326     },
2327     setTitle: function(str) {
2328         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2329         
2330     }
2331 });
2332
2333
2334 Roo.apply(Roo.bootstrap.Modal,  {
2335     /**
2336          * Button config that displays a single OK button
2337          * @type Object
2338          */
2339         OK :  [{
2340             name : 'ok',
2341             weight : 'primary',
2342             html : 'OK'
2343         }], 
2344         /**
2345          * Button config that displays Yes and No buttons
2346          * @type Object
2347          */
2348         YESNO : [
2349             {
2350                 name  : 'no',
2351                 html : 'No'
2352             },
2353             {
2354                 name  :'yes',
2355                 weight : 'primary',
2356                 html : 'Yes'
2357             }
2358         ],
2359         
2360         /**
2361          * Button config that displays OK and Cancel buttons
2362          * @type Object
2363          */
2364         OKCANCEL : [
2365             {
2366                name : 'cancel',
2367                 html : 'Cancel'
2368             },
2369             {
2370                 name : 'ok',
2371                 weight : 'primary',
2372                 html : 'OK'
2373             }
2374         ],
2375         /**
2376          * Button config that displays Yes, No and Cancel buttons
2377          * @type Object
2378          */
2379         YESNOCANCEL : [
2380             {
2381                 name : 'yes',
2382                 weight : 'primary',
2383                 html : 'Yes'
2384             },
2385             {
2386                 name : 'no',
2387                 html : 'No'
2388             },
2389             {
2390                 name : 'cancel',
2391                 html : 'Cancel'
2392             }
2393         ]
2394 });
2395  /*
2396  * - LGPL
2397  *
2398  * messagebox - can be used as a replace
2399  * 
2400  */
2401 /**
2402  * @class Roo.MessageBox
2403  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2404  * Example usage:
2405  *<pre><code>
2406 // Basic alert:
2407 Roo.Msg.alert('Status', 'Changes saved successfully.');
2408
2409 // Prompt for user data:
2410 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2411     if (btn == 'ok'){
2412         // process text value...
2413     }
2414 });
2415
2416 // Show a dialog using config options:
2417 Roo.Msg.show({
2418    title:'Save Changes?',
2419    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2420    buttons: Roo.Msg.YESNOCANCEL,
2421    fn: processResult,
2422    animEl: 'elId'
2423 });
2424 </code></pre>
2425  * @singleton
2426  */
2427 Roo.bootstrap.MessageBox = function(){
2428     var dlg, opt, mask, waitTimer;
2429     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2430     var buttons, activeTextEl, bwidth;
2431
2432     
2433     // private
2434     var handleButton = function(button){
2435         dlg.hide();
2436         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2437     };
2438
2439     // private
2440     var handleHide = function(){
2441         if(opt && opt.cls){
2442             dlg.el.removeClass(opt.cls);
2443         }
2444         //if(waitTimer){
2445         //    Roo.TaskMgr.stop(waitTimer);
2446         //    waitTimer = null;
2447         //}
2448     };
2449
2450     // private
2451     var updateButtons = function(b){
2452         var width = 0;
2453         if(!b){
2454             buttons["ok"].hide();
2455             buttons["cancel"].hide();
2456             buttons["yes"].hide();
2457             buttons["no"].hide();
2458             //dlg.footer.dom.style.display = 'none';
2459             return width;
2460         }
2461         dlg.footer.dom.style.display = '';
2462         for(var k in buttons){
2463             if(typeof buttons[k] != "function"){
2464                 if(b[k]){
2465                     buttons[k].show();
2466                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2467                     width += buttons[k].el.getWidth()+15;
2468                 }else{
2469                     buttons[k].hide();
2470                 }
2471             }
2472         }
2473         return width;
2474     };
2475
2476     // private
2477     var handleEsc = function(d, k, e){
2478         if(opt && opt.closable !== false){
2479             dlg.hide();
2480         }
2481         if(e){
2482             e.stopEvent();
2483         }
2484     };
2485
2486     return {
2487         /**
2488          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2489          * @return {Roo.BasicDialog} The BasicDialog element
2490          */
2491         getDialog : function(){
2492            if(!dlg){
2493                 dlg = new Roo.bootstrap.Modal( {
2494                     //draggable: true,
2495                     //resizable:false,
2496                     //constraintoviewport:false,
2497                     //fixedcenter:true,
2498                     //collapsible : false,
2499                     //shim:true,
2500                     //modal: true,
2501                   //  width:400,
2502                   //  height:100,
2503                     //buttonAlign:"center",
2504                     closeClick : function(){
2505                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2506                             handleButton("no");
2507                         }else{
2508                             handleButton("cancel");
2509                         }
2510                     }
2511                 });
2512                 dlg.render();
2513                 dlg.on("hide", handleHide);
2514                 mask = dlg.mask;
2515                 //dlg.addKeyListener(27, handleEsc);
2516                 buttons = {};
2517                 this.buttons = buttons;
2518                 var bt = this.buttonText;
2519                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2520                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2521                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2522                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2523                 Roo.log(buttons)
2524                 bodyEl = dlg.body.createChild({
2525
2526                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2527                         '<textarea class="roo-mb-textarea"></textarea>' +
2528                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2529                 });
2530                 msgEl = bodyEl.dom.firstChild;
2531                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2532                 textboxEl.enableDisplayMode();
2533                 textboxEl.addKeyListener([10,13], function(){
2534                     if(dlg.isVisible() && opt && opt.buttons){
2535                         if(opt.buttons.ok){
2536                             handleButton("ok");
2537                         }else if(opt.buttons.yes){
2538                             handleButton("yes");
2539                         }
2540                     }
2541                 });
2542                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2543                 textareaEl.enableDisplayMode();
2544                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2545                 progressEl.enableDisplayMode();
2546                 var pf = progressEl.dom.firstChild;
2547                 if (pf) {
2548                     pp = Roo.get(pf.firstChild);
2549                     pp.setHeight(pf.offsetHeight);
2550                 }
2551                 
2552             }
2553             return dlg;
2554         },
2555
2556         /**
2557          * Updates the message box body text
2558          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2559          * the XHTML-compliant non-breaking space character '&amp;#160;')
2560          * @return {Roo.MessageBox} This message box
2561          */
2562         updateText : function(text){
2563             if(!dlg.isVisible() && !opt.width){
2564                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2565             }
2566             msgEl.innerHTML = text || '&#160;';
2567       
2568             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2569             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2570             var w = Math.max(
2571                     Math.min(opt.width || cw , this.maxWidth), 
2572                     Math.max(opt.minWidth || this.minWidth, bwidth)
2573             );
2574             if(opt.prompt){
2575                 activeTextEl.setWidth(w);
2576             }
2577             if(dlg.isVisible()){
2578                 dlg.fixedcenter = false;
2579             }
2580             // to big, make it scroll. = But as usual stupid IE does not support
2581             // !important..
2582             
2583             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2584                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2585                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2586             } else {
2587                 bodyEl.dom.style.height = '';
2588                 bodyEl.dom.style.overflowY = '';
2589             }
2590             if (cw > w) {
2591                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2592             } else {
2593                 bodyEl.dom.style.overflowX = '';
2594             }
2595             
2596             dlg.setContentSize(w, bodyEl.getHeight());
2597             if(dlg.isVisible()){
2598                 dlg.fixedcenter = true;
2599             }
2600             return this;
2601         },
2602
2603         /**
2604          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2605          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2606          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2607          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2608          * @return {Roo.MessageBox} This message box
2609          */
2610         updateProgress : function(value, text){
2611             if(text){
2612                 this.updateText(text);
2613             }
2614             if (pp) { // weird bug on my firefox - for some reason this is not defined
2615                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2616             }
2617             return this;
2618         },        
2619
2620         /**
2621          * Returns true if the message box is currently displayed
2622          * @return {Boolean} True if the message box is visible, else false
2623          */
2624         isVisible : function(){
2625             return dlg && dlg.isVisible();  
2626         },
2627
2628         /**
2629          * Hides the message box if it is displayed
2630          */
2631         hide : function(){
2632             if(this.isVisible()){
2633                 dlg.hide();
2634             }  
2635         },
2636
2637         /**
2638          * Displays a new message box, or reinitializes an existing message box, based on the config options
2639          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2640          * The following config object properties are supported:
2641          * <pre>
2642 Property    Type             Description
2643 ----------  ---------------  ------------------------------------------------------------------------------------
2644 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2645                                    closes (defaults to undefined)
2646 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2647                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2648 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2649                                    progress and wait dialogs will ignore this property and always hide the
2650                                    close button as they can only be closed programmatically.
2651 cls               String           A custom CSS class to apply to the message box element
2652 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2653                                    displayed (defaults to 75)
2654 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2655                                    function will be btn (the name of the button that was clicked, if applicable,
2656                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2657                                    Progress and wait dialogs will ignore this option since they do not respond to
2658                                    user actions and can only be closed programmatically, so any required function
2659                                    should be called by the same code after it closes the dialog.
2660 icon              String           A CSS class that provides a background image to be used as an icon for
2661                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2662 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2663 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2664 modal             Boolean          False to allow user interaction with the page while the message box is
2665                                    displayed (defaults to true)
2666 msg               String           A string that will replace the existing message box body text (defaults
2667                                    to the XHTML-compliant non-breaking space character '&#160;')
2668 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2669 progress          Boolean          True to display a progress bar (defaults to false)
2670 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2671 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2672 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2673 title             String           The title text
2674 value             String           The string value to set into the active textbox element if displayed
2675 wait              Boolean          True to display a progress bar (defaults to false)
2676 width             Number           The width of the dialog in pixels
2677 </pre>
2678          *
2679          * Example usage:
2680          * <pre><code>
2681 Roo.Msg.show({
2682    title: 'Address',
2683    msg: 'Please enter your address:',
2684    width: 300,
2685    buttons: Roo.MessageBox.OKCANCEL,
2686    multiline: true,
2687    fn: saveAddress,
2688    animEl: 'addAddressBtn'
2689 });
2690 </code></pre>
2691          * @param {Object} config Configuration options
2692          * @return {Roo.MessageBox} This message box
2693          */
2694         show : function(options)
2695         {
2696             
2697             // this causes nightmares if you show one dialog after another
2698             // especially on callbacks..
2699              
2700             if(this.isVisible()){
2701                 
2702                 this.hide();
2703                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2704                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2705                 Roo.log("New Dialog Message:" +  options.msg )
2706                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2707                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2708                 
2709             }
2710             var d = this.getDialog();
2711             opt = options;
2712             d.setTitle(opt.title || "&#160;");
2713             d.close.setDisplayed(opt.closable !== false);
2714             activeTextEl = textboxEl;
2715             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2716             if(opt.prompt){
2717                 if(opt.multiline){
2718                     textboxEl.hide();
2719                     textareaEl.show();
2720                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2721                         opt.multiline : this.defaultTextHeight);
2722                     activeTextEl = textareaEl;
2723                 }else{
2724                     textboxEl.show();
2725                     textareaEl.hide();
2726                 }
2727             }else{
2728                 textboxEl.hide();
2729                 textareaEl.hide();
2730             }
2731             progressEl.setDisplayed(opt.progress === true);
2732             this.updateProgress(0);
2733             activeTextEl.dom.value = opt.value || "";
2734             if(opt.prompt){
2735                 dlg.setDefaultButton(activeTextEl);
2736             }else{
2737                 var bs = opt.buttons;
2738                 var db = null;
2739                 if(bs && bs.ok){
2740                     db = buttons["ok"];
2741                 }else if(bs && bs.yes){
2742                     db = buttons["yes"];
2743                 }
2744                 dlg.setDefaultButton(db);
2745             }
2746             bwidth = updateButtons(opt.buttons);
2747             this.updateText(opt.msg);
2748             if(opt.cls){
2749                 d.el.addClass(opt.cls);
2750             }
2751             d.proxyDrag = opt.proxyDrag === true;
2752             d.modal = opt.modal !== false;
2753             d.mask = opt.modal !== false ? mask : false;
2754             if(!d.isVisible()){
2755                 // force it to the end of the z-index stack so it gets a cursor in FF
2756                 document.body.appendChild(dlg.el.dom);
2757                 d.animateTarget = null;
2758                 d.show(options.animEl);
2759             }
2760             return this;
2761         },
2762
2763         /**
2764          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2765          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2766          * and closing the message box when the process is complete.
2767          * @param {String} title The title bar text
2768          * @param {String} msg The message box body text
2769          * @return {Roo.MessageBox} This message box
2770          */
2771         progress : function(title, msg){
2772             this.show({
2773                 title : title,
2774                 msg : msg,
2775                 buttons: false,
2776                 progress:true,
2777                 closable:false,
2778                 minWidth: this.minProgressWidth,
2779                 modal : true
2780             });
2781             return this;
2782         },
2783
2784         /**
2785          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2786          * If a callback function is passed it will be called after the user clicks the button, and the
2787          * id of the button that was clicked will be passed as the only parameter to the callback
2788          * (could also be the top-right close button).
2789          * @param {String} title The title bar text
2790          * @param {String} msg The message box body text
2791          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2792          * @param {Object} scope (optional) The scope of the callback function
2793          * @return {Roo.MessageBox} This message box
2794          */
2795         alert : function(title, msg, fn, scope){
2796             this.show({
2797                 title : title,
2798                 msg : msg,
2799                 buttons: this.OK,
2800                 fn: fn,
2801                 scope : scope,
2802                 modal : true
2803             });
2804             return this;
2805         },
2806
2807         /**
2808          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2809          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2810          * You are responsible for closing the message box when the process is complete.
2811          * @param {String} msg The message box body text
2812          * @param {String} title (optional) The title bar text
2813          * @return {Roo.MessageBox} This message box
2814          */
2815         wait : function(msg, title){
2816             this.show({
2817                 title : title,
2818                 msg : msg,
2819                 buttons: false,
2820                 closable:false,
2821                 progress:true,
2822                 modal:true,
2823                 width:300,
2824                 wait:true
2825             });
2826             waitTimer = Roo.TaskMgr.start({
2827                 run: function(i){
2828                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2829                 },
2830                 interval: 1000
2831             });
2832             return this;
2833         },
2834
2835         /**
2836          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2837          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2838          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2839          * @param {String} title The title bar text
2840          * @param {String} msg The message box body text
2841          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2842          * @param {Object} scope (optional) The scope of the callback function
2843          * @return {Roo.MessageBox} This message box
2844          */
2845         confirm : function(title, msg, fn, scope){
2846             this.show({
2847                 title : title,
2848                 msg : msg,
2849                 buttons: this.YESNO,
2850                 fn: fn,
2851                 scope : scope,
2852                 modal : true
2853             });
2854             return this;
2855         },
2856
2857         /**
2858          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2859          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2860          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2861          * (could also be the top-right close button) and the text that was entered will be passed as the two
2862          * parameters to the callback.
2863          * @param {String} title The title bar text
2864          * @param {String} msg The message box body text
2865          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2866          * @param {Object} scope (optional) The scope of the callback function
2867          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2868          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2869          * @return {Roo.MessageBox} This message box
2870          */
2871         prompt : function(title, msg, fn, scope, multiline){
2872             this.show({
2873                 title : title,
2874                 msg : msg,
2875                 buttons: this.OKCANCEL,
2876                 fn: fn,
2877                 minWidth:250,
2878                 scope : scope,
2879                 prompt:true,
2880                 multiline: multiline,
2881                 modal : true
2882             });
2883             return this;
2884         },
2885
2886         /**
2887          * Button config that displays a single OK button
2888          * @type Object
2889          */
2890         OK : {ok:true},
2891         /**
2892          * Button config that displays Yes and No buttons
2893          * @type Object
2894          */
2895         YESNO : {yes:true, no:true},
2896         /**
2897          * Button config that displays OK and Cancel buttons
2898          * @type Object
2899          */
2900         OKCANCEL : {ok:true, cancel:true},
2901         /**
2902          * Button config that displays Yes, No and Cancel buttons
2903          * @type Object
2904          */
2905         YESNOCANCEL : {yes:true, no:true, cancel:true},
2906
2907         /**
2908          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2909          * @type Number
2910          */
2911         defaultTextHeight : 75,
2912         /**
2913          * The maximum width in pixels of the message box (defaults to 600)
2914          * @type Number
2915          */
2916         maxWidth : 600,
2917         /**
2918          * The minimum width in pixels of the message box (defaults to 100)
2919          * @type Number
2920          */
2921         minWidth : 100,
2922         /**
2923          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2924          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2925          * @type Number
2926          */
2927         minProgressWidth : 250,
2928         /**
2929          * An object containing the default button text strings that can be overriden for localized language support.
2930          * Supported properties are: ok, cancel, yes and no.
2931          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2932          * @type Object
2933          */
2934         buttonText : {
2935             ok : "OK",
2936             cancel : "Cancel",
2937             yes : "Yes",
2938             no : "No"
2939         }
2940     };
2941 }();
2942
2943 /**
2944  * Shorthand for {@link Roo.MessageBox}
2945  */
2946 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2947 Roo.Msg = Roo.Msg || Roo.MessageBox;
2948 /*
2949  * - LGPL
2950  *
2951  * navbar
2952  * 
2953  */
2954
2955 /**
2956  * @class Roo.bootstrap.Navbar
2957  * @extends Roo.bootstrap.Component
2958  * Bootstrap Navbar class
2959
2960  * @constructor
2961  * Create a new Navbar
2962  * @param {Object} config The config object
2963  */
2964
2965
2966 Roo.bootstrap.Navbar = function(config){
2967     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2968     
2969 };
2970
2971 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2972     
2973     
2974    
2975     // private
2976     navItems : false,
2977     loadMask : false,
2978     
2979     
2980     getAutoCreate : function(){
2981         
2982         
2983         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2984         
2985     },
2986     
2987     initEvents :function ()
2988     {
2989         //Roo.log(this.el.select('.navbar-toggle',true));
2990         this.el.select('.navbar-toggle',true).on('click', function() {
2991            // Roo.log('click');
2992             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2993         }, this);
2994         
2995         var mark = {
2996             tag: "div",
2997             cls:"x-dlg-mask"
2998         }
2999         
3000         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3001         
3002         var size = this.el.getSize();
3003         this.maskEl.setSize(size.width, size.height);
3004         this.maskEl.enableDisplayMode("block");
3005         this.maskEl.hide();
3006         
3007         if(this.loadMask){
3008             this.maskEl.show();
3009         }
3010     },
3011     
3012     
3013     getChildContainer : function()
3014     {
3015         if (this.el.select('.collapse').getCount()) {
3016             return this.el.select('.collapse',true).first();
3017         }
3018         
3019         return this.el;
3020     },
3021     
3022     mask : function()
3023     {
3024         this.maskEl.show();
3025     },
3026     
3027     unmask : function()
3028     {
3029         this.maskEl.hide();
3030     } 
3031     
3032     
3033     
3034     
3035 });
3036
3037
3038
3039  
3040
3041  /*
3042  * - LGPL
3043  *
3044  * navbar
3045  * 
3046  */
3047
3048 /**
3049  * @class Roo.bootstrap.NavSimplebar
3050  * @extends Roo.bootstrap.Navbar
3051  * Bootstrap Sidebar class
3052  *
3053  * @cfg {Boolean} inverse is inverted color
3054  * 
3055  * @cfg {String} type (nav | pills | tabs)
3056  * @cfg {Boolean} arrangement stacked | justified
3057  * @cfg {String} align (left | right) alignment
3058  * 
3059  * @cfg {Boolean} main (true|false) main nav bar? default false
3060  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3061  * 
3062  * @cfg {String} tag (header|footer|nav|div) default is nav 
3063
3064  * 
3065  * 
3066  * 
3067  * @constructor
3068  * Create a new Sidebar
3069  * @param {Object} config The config object
3070  */
3071
3072
3073 Roo.bootstrap.NavSimplebar = function(config){
3074     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3075 };
3076
3077 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3078     
3079     inverse: false,
3080     
3081     type: false,
3082     arrangement: '',
3083     align : false,
3084     
3085     
3086     
3087     main : false,
3088     
3089     
3090     tag : false,
3091     
3092     
3093     getAutoCreate : function(){
3094         
3095         
3096         var cfg = {
3097             tag : this.tag || 'div',
3098             cls : 'navbar'
3099         };
3100           
3101         
3102         cfg.cn = [
3103             {
3104                 cls: 'nav',
3105                 tag : 'ul'
3106             }
3107         ];
3108         
3109          
3110         this.type = this.type || 'nav';
3111         if (['tabs','pills'].indexOf(this.type)!==-1) {
3112             cfg.cn[0].cls += ' nav-' + this.type
3113         
3114         
3115         } else {
3116             if (this.type!=='nav') {
3117                 Roo.log('nav type must be nav/tabs/pills')
3118             }
3119             cfg.cn[0].cls += ' navbar-nav'
3120         }
3121         
3122         
3123         
3124         
3125         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3126             cfg.cn[0].cls += ' nav-' + this.arrangement;
3127         }
3128         
3129         
3130         if (this.align === 'right') {
3131             cfg.cn[0].cls += ' navbar-right';
3132         }
3133         
3134         if (this.inverse) {
3135             cfg.cls += ' navbar-inverse';
3136             
3137         }
3138         
3139         
3140         return cfg;
3141     
3142         
3143     }
3144     
3145     
3146     
3147 });
3148
3149
3150
3151  
3152
3153  
3154        /*
3155  * - LGPL
3156  *
3157  * navbar
3158  * 
3159  */
3160
3161 /**
3162  * @class Roo.bootstrap.NavHeaderbar
3163  * @extends Roo.bootstrap.NavSimplebar
3164  * Bootstrap Sidebar class
3165  *
3166  * @cfg {String} brand what is brand
3167  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3168  * @cfg {String} brand_href href of the brand
3169  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3170  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3171  * 
3172  * @constructor
3173  * Create a new Sidebar
3174  * @param {Object} config The config object
3175  */
3176
3177
3178 Roo.bootstrap.NavHeaderbar = function(config){
3179     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3180 };
3181
3182 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3183     
3184     position: '',
3185     brand: '',
3186     brand_href: false,
3187     srButton : true,
3188     autohide : false,
3189     
3190     getAutoCreate : function(){
3191         
3192         var   cfg = {
3193             tag: this.nav || 'nav',
3194             cls: 'navbar',
3195             role: 'navigation',
3196             cn: []
3197         };
3198         
3199         if(this.srButton){
3200             cfg.cn.push({
3201                 tag: 'div',
3202                 cls: 'navbar-header',
3203                 cn: [
3204                     {
3205                         tag: 'button',
3206                         type: 'button',
3207                         cls: 'navbar-toggle',
3208                         'data-toggle': 'collapse',
3209                         cn: [
3210                             {
3211                                 tag: 'span',
3212                                 cls: 'sr-only',
3213                                 html: 'Toggle navigation'
3214                             },
3215                             {
3216                                 tag: 'span',
3217                                 cls: 'icon-bar'
3218                             },
3219                             {
3220                                 tag: 'span',
3221                                 cls: 'icon-bar'
3222                             },
3223                             {
3224                                 tag: 'span',
3225                                 cls: 'icon-bar'
3226                             }
3227                         ]
3228                     }
3229                 ]
3230             });
3231         }
3232         
3233         cfg.cn.push({
3234             tag: 'div',
3235             cls: 'collapse navbar-collapse',
3236             cn : []
3237         });
3238         
3239         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3240         
3241         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3242             cfg.cls += ' navbar-' + this.position;
3243             
3244             // tag can override this..
3245             
3246             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3247         }
3248         
3249         if (this.brand !== '') {
3250             cfg.cn[0].cn.push({
3251                 tag: 'a',
3252                 href: this.brand_href ? this.brand_href : '#',
3253                 cls: 'navbar-brand',
3254                 cn: [
3255                 this.brand
3256                 ]
3257             });
3258         }
3259         
3260         if(this.main){
3261             cfg.cls += ' main-nav';
3262         }
3263         
3264         
3265         return cfg;
3266
3267         
3268     },
3269     initEvents : function()
3270     {
3271         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3272         
3273         if (this.autohide) {
3274             
3275             var prevScroll = 0;
3276             var ft = this.el;
3277             
3278             Roo.get(document).on('scroll',function(e) {
3279                 var ns = Roo.get(document).getScroll().top;
3280                 var os = prevScroll;
3281                 prevScroll = ns;
3282                 
3283                 if(ns > os){
3284                     ft.removeClass('slideDown');
3285                     ft.addClass('slideUp');
3286                     return;
3287                 }
3288                 ft.removeClass('slideUp');
3289                 ft.addClass('slideDown');
3290                  
3291               
3292           },this);
3293         }
3294     }    
3295           
3296       
3297     
3298     
3299 });
3300
3301
3302
3303  
3304
3305  /*
3306  * - LGPL
3307  *
3308  * navbar
3309  * 
3310  */
3311
3312 /**
3313  * @class Roo.bootstrap.NavSidebar
3314  * @extends Roo.bootstrap.Navbar
3315  * Bootstrap Sidebar class
3316  * 
3317  * @constructor
3318  * Create a new Sidebar
3319  * @param {Object} config The config object
3320  */
3321
3322
3323 Roo.bootstrap.NavSidebar = function(config){
3324     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3325 };
3326
3327 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3328     
3329     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3330     
3331     getAutoCreate : function(){
3332         
3333         
3334         return  {
3335             tag: 'div',
3336             cls: 'sidebar sidebar-nav'
3337         };
3338     
3339         
3340     }
3341     
3342     
3343     
3344 });
3345
3346
3347
3348  
3349
3350  /*
3351  * - LGPL
3352  *
3353  * nav group
3354  * 
3355  */
3356
3357 /**
3358  * @class Roo.bootstrap.NavGroup
3359  * @extends Roo.bootstrap.Component
3360  * Bootstrap NavGroup class
3361  * @cfg {String} align left | right
3362  * @cfg {Boolean} inverse false | true
3363  * @cfg {String} type (nav|pills|tab) default nav
3364  * @cfg {String} navId - reference Id for navbar.
3365
3366  * 
3367  * @constructor
3368  * Create a new nav group
3369  * @param {Object} config The config object
3370  */
3371
3372 Roo.bootstrap.NavGroup = function(config){
3373     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3374     this.navItems = [];
3375    
3376     Roo.bootstrap.NavGroup.register(this);
3377      this.addEvents({
3378         /**
3379              * @event changed
3380              * Fires when the active item changes
3381              * @param {Roo.bootstrap.NavGroup} this
3382              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3383              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3384          */
3385         'changed': true
3386      });
3387     
3388 };
3389
3390 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3391     
3392     align: '',
3393     inverse: false,
3394     form: false,
3395     type: 'nav',
3396     navId : '',
3397     // private
3398     
3399     navItems : false, 
3400     
3401     getAutoCreate : function()
3402     {
3403         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3404         
3405         cfg = {
3406             tag : 'ul',
3407             cls: 'nav' 
3408         }
3409         
3410         if (['tabs','pills'].indexOf(this.type)!==-1) {
3411             cfg.cls += ' nav-' + this.type
3412         } else {
3413             if (this.type!=='nav') {
3414                 Roo.log('nav type must be nav/tabs/pills')
3415             }
3416             cfg.cls += ' navbar-nav'
3417         }
3418         
3419         if (this.parent().sidebar) {
3420             cfg = {
3421                 tag: 'ul',
3422                 cls: 'dashboard-menu sidebar-menu'
3423             }
3424             
3425             return cfg;
3426         }
3427         
3428         if (this.form === true) {
3429             cfg = {
3430                 tag: 'form',
3431                 cls: 'navbar-form'
3432             }
3433             
3434             if (this.align === 'right') {
3435                 cfg.cls += ' navbar-right';
3436             } else {
3437                 cfg.cls += ' navbar-left';
3438             }
3439         }
3440         
3441         if (this.align === 'right') {
3442             cfg.cls += ' navbar-right';
3443         }
3444         
3445         if (this.inverse) {
3446             cfg.cls += ' navbar-inverse';
3447             
3448         }
3449         
3450         
3451         return cfg;
3452     },
3453     /**
3454     * sets the active Navigation item
3455     * @param {Roo.bootstrap.NavItem} the new current navitem
3456     */
3457     setActiveItem : function(item)
3458     {
3459         var prev = false;
3460         Roo.each(this.navItems, function(v){
3461             if (v == item) {
3462                 return ;
3463             }
3464             if (v.isActive()) {
3465                 v.setActive(false, true);
3466                 prev = v;
3467                 
3468             }
3469             
3470         });
3471
3472         item.setActive(true, true);
3473         this.fireEvent('changed', this, item, prev);
3474         
3475         
3476     },
3477     /**
3478     * gets the active Navigation item
3479     * @return {Roo.bootstrap.NavItem} the current navitem
3480     */
3481     getActive : function()
3482     {
3483         
3484         var prev = false;
3485         Roo.each(this.navItems, function(v){
3486             
3487             if (v.isActive()) {
3488                 prev = v;
3489                 
3490             }
3491             
3492         });
3493         return prev;
3494     },
3495     
3496     indexOfNav : function()
3497     {
3498         
3499         var prev = false;
3500         Roo.each(this.navItems, function(v,i){
3501             
3502             if (v.isActive()) {
3503                 prev = i;
3504                 
3505             }
3506             
3507         });
3508         return prev;
3509     },
3510     /**
3511     * adds a Navigation item
3512     * @param {Roo.bootstrap.NavItem} the navitem to add
3513     */
3514     addItem : function(cfg)
3515     {
3516         var cn = new Roo.bootstrap.NavItem(cfg);
3517         this.register(cn);
3518         cn.parentId = this.id;
3519         cn.onRender(this.el, null);
3520         return cn;
3521     },
3522     /**
3523     * register a Navigation item
3524     * @param {Roo.bootstrap.NavItem} the navitem to add
3525     */
3526     register : function(item)
3527     {
3528         this.navItems.push( item);
3529         item.navId = this.navId;
3530     
3531     },
3532     
3533     /**
3534     * clear all the Navigation item
3535     */
3536    
3537     clearAll : function()
3538     {
3539         this.navItems = [];
3540         this.el.dom.innerHTML = '';
3541     },
3542     
3543     getNavItem: function(tabId)
3544     {
3545         var ret = false;
3546         Roo.each(this.navItems, function(e) {
3547             if (e.tabId == tabId) {
3548                ret =  e;
3549                return false;
3550             }
3551             return true;
3552             
3553         });
3554         return ret;
3555     },
3556     
3557     setActiveNext : function()
3558     {
3559         var i = this.indexOfNav(this.getActive());
3560         if (i > this.navItems.length) {
3561             return;
3562         }
3563         this.setActiveItem(this.navItems[i+1]);
3564     },
3565     setActivePrev : function()
3566     {
3567         var i = this.indexOfNav(this.getActive());
3568         if (i  < 1) {
3569             return;
3570         }
3571         this.setActiveItem(this.navItems[i-1]);
3572     },
3573     clearWasActive : function(except) {
3574         Roo.each(this.navItems, function(e) {
3575             if (e.tabId != except.tabId && e.was_active) {
3576                e.was_active = false;
3577                return false;
3578             }
3579             return true;
3580             
3581         });
3582     },
3583     getWasActive : function ()
3584     {
3585         var r = false;
3586         Roo.each(this.navItems, function(e) {
3587             if (e.was_active) {
3588                r = e;
3589                return false;
3590             }
3591             return true;
3592             
3593         });
3594         return r;
3595     }
3596     
3597     
3598 });
3599
3600  
3601 Roo.apply(Roo.bootstrap.NavGroup, {
3602     
3603     groups: {},
3604      /**
3605     * register a Navigation Group
3606     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3607     */
3608     register : function(navgrp)
3609     {
3610         this.groups[navgrp.navId] = navgrp;
3611         
3612     },
3613     /**
3614     * fetch a Navigation Group based on the navigation ID
3615     * @param {string} the navgroup to add
3616     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3617     */
3618     get: function(navId) {
3619         if (typeof(this.groups[navId]) == 'undefined') {
3620             return false;
3621             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3622         }
3623         return this.groups[navId] ;
3624     }
3625     
3626     
3627     
3628 });
3629
3630  /*
3631  * - LGPL
3632  *
3633  * row
3634  * 
3635  */
3636
3637 /**
3638  * @class Roo.bootstrap.NavItem
3639  * @extends Roo.bootstrap.Component
3640  * Bootstrap Navbar.NavItem class
3641  * @cfg {String} href  link to
3642  * @cfg {String} html content of button
3643  * @cfg {String} badge text inside badge
3644  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3645  * @cfg {String} glyphicon name of glyphicon
3646  * @cfg {String} icon name of font awesome icon
3647  * @cfg {Boolean} active Is item active
3648  * @cfg {Boolean} disabled Is item disabled
3649  
3650  * @cfg {Boolean} preventDefault (true | false) default false
3651  * @cfg {String} tabId the tab that this item activates.
3652  * @cfg {String} tagtype (a|span) render as a href or span?
3653   
3654  * @constructor
3655  * Create a new Navbar Item
3656  * @param {Object} config The config object
3657  */
3658 Roo.bootstrap.NavItem = function(config){
3659     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3660     this.addEvents({
3661         // raw events
3662         /**
3663          * @event click
3664          * The raw click event for the entire grid.
3665          * @param {Roo.EventObject} e
3666          */
3667         "click" : true,
3668          /**
3669             * @event changed
3670             * Fires when the active item active state changes
3671             * @param {Roo.bootstrap.NavItem} this
3672             * @param {boolean} state the new state
3673              
3674          */
3675         'changed': true
3676     });
3677    
3678 };
3679
3680 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3681     
3682     href: false,
3683     html: '',
3684     badge: '',
3685     icon: false,
3686     glyphicon: false,
3687     active: false,
3688     preventDefault : false,
3689     tabId : false,
3690     tagtype : 'a',
3691     disabled : false,
3692     
3693     was_active : false,
3694     
3695     getAutoCreate : function(){
3696          
3697         var cfg = {
3698             tag: 'li',
3699             cls: 'nav-item'
3700             
3701         }
3702         if (this.active) {
3703             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3704         }
3705         if (this.disabled) {
3706             cfg.cls += ' disabled';
3707         }
3708         
3709         if (this.href || this.html || this.glyphicon || this.icon) {
3710             cfg.cn = [
3711                 {
3712                     tag: this.tagtype,
3713                     href : this.href || "#",
3714                     html: this.html || ''
3715                 }
3716             ];
3717             
3718             if (this.icon) {
3719                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3720             }
3721
3722             if(this.glyphicon) {
3723                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3724             }
3725             
3726             if (this.menu) {
3727                 
3728                 cfg.cn[0].html += " <span class='caret'></span>";
3729              
3730             }
3731             
3732             if (this.badge !== '') {
3733                  
3734                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3735             }
3736         }
3737         
3738         
3739         
3740         return cfg;
3741     },
3742     initEvents: function() 
3743     {
3744         if (typeof (this.menu) != 'undefined') {
3745             this.menu.parentType = this.xtype;
3746             this.menu.triggerEl = this.el;
3747             this.addxtype(Roo.apply({}, this.menu));
3748         }
3749         
3750         this.el.select('a',true).on('click', this.onClick, this);
3751         
3752         if(this.tagtype == 'span'){
3753             this.el.select('span',true).on('click', this.onClick, this);
3754         }
3755        
3756         // at this point parent should be available..
3757         this.parent().register(this);
3758     },
3759     
3760     onClick : function(e)
3761     {
3762          
3763         if(this.preventDefault){
3764             e.preventDefault();
3765         }
3766         if (this.disabled) {
3767             return;
3768         }
3769         
3770         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3771         if (tg && tg.transition) {
3772             Roo.log("waiting for the transitionend");
3773             return;
3774         }
3775         
3776         Roo.log("fire event clicked");
3777         if(this.fireEvent('click', this, e) === false){
3778             return;
3779         };
3780         
3781         if(this.tagtype == 'span'){
3782             return;
3783         }
3784         
3785         var p = this.parent();
3786         if (['tabs','pills'].indexOf(p.type)!==-1) {
3787             if (typeof(p.setActiveItem) !== 'undefined') {
3788                 p.setActiveItem(this);
3789             }
3790         }
3791         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3792         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3793             // remove the collapsed menu expand...
3794             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3795         }
3796         
3797     },
3798     
3799     isActive: function () {
3800         return this.active
3801     },
3802     setActive : function(state, fire, is_was_active)
3803     {
3804         if (this.active && !state & this.navId) {
3805             this.was_active = true;
3806             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3807             if (nv) {
3808                 nv.clearWasActive(this);
3809             }
3810             
3811         }
3812         this.active = state;
3813         
3814         if (!state ) {
3815             this.el.removeClass('active');
3816         } else if (!this.el.hasClass('active')) {
3817             this.el.addClass('active');
3818         }
3819         if (fire) {
3820             this.fireEvent('changed', this, state);
3821         }
3822         
3823         // show a panel if it's registered and related..
3824         
3825         if (!this.navId || !this.tabId || !state || is_was_active) {
3826             return;
3827         }
3828         
3829         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3830         if (!tg) {
3831             return;
3832         }
3833         var pan = tg.getPanelByName(this.tabId);
3834         if (!pan) {
3835             return;
3836         }
3837         // if we can not flip to new panel - go back to old nav highlight..
3838         if (false == tg.showPanel(pan)) {
3839             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3840             if (nv) {
3841                 var onav = nv.getWasActive();
3842                 if (onav) {
3843                     onav.setActive(true, false, true);
3844                 }
3845             }
3846             
3847         }
3848         
3849         
3850         
3851     },
3852      // this should not be here...
3853     setDisabled : function(state)
3854     {
3855         this.disabled = state;
3856         if (!state ) {
3857             this.el.removeClass('disabled');
3858         } else if (!this.el.hasClass('disabled')) {
3859             this.el.addClass('disabled');
3860         }
3861         
3862     }
3863 });
3864  
3865
3866  /*
3867  * - LGPL
3868  *
3869  * sidebar item
3870  *
3871  *  li
3872  *    <span> icon </span>
3873  *    <span> text </span>
3874  *    <span>badge </span>
3875  */
3876
3877 /**
3878  * @class Roo.bootstrap.NavSidebarItem
3879  * @extends Roo.bootstrap.NavItem
3880  * Bootstrap Navbar.NavSidebarItem class
3881  * @constructor
3882  * Create a new Navbar Button
3883  * @param {Object} config The config object
3884  */
3885 Roo.bootstrap.NavSidebarItem = function(config){
3886     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3887     this.addEvents({
3888         // raw events
3889         /**
3890          * @event click
3891          * The raw click event for the entire grid.
3892          * @param {Roo.EventObject} e
3893          */
3894         "click" : true,
3895          /**
3896             * @event changed
3897             * Fires when the active item active state changes
3898             * @param {Roo.bootstrap.NavSidebarItem} this
3899             * @param {boolean} state the new state
3900              
3901          */
3902         'changed': true
3903     });
3904    
3905 };
3906
3907 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3908     
3909     
3910     getAutoCreate : function(){
3911         
3912         
3913         var a = {
3914                 tag: 'a',
3915                 href : this.href || '#',
3916                 cls: '',
3917                 html : '',
3918                 cn : []
3919         };
3920         var cfg = {
3921             tag: 'li',
3922             cls: '',
3923             cn: [ a ]
3924         }
3925         var span = {
3926             tag: 'span',
3927             html : this.html || ''
3928         }
3929         
3930         
3931         if (this.active) {
3932             cfg.cls += ' active';
3933         }
3934         
3935         // left icon..
3936         if (this.glyphicon || this.icon) {
3937             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3938             a.cn.push({ tag : 'i', cls : c }) ;
3939         }
3940         // html..
3941         a.cn.push(span);
3942         // then badge..
3943         if (this.badge !== '') {
3944             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3945         }
3946         // fi
3947         if (this.menu) {
3948             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3949             a.cls += 'dropdown-toggle treeview' ;
3950             
3951         }
3952         
3953         
3954         
3955         return cfg;
3956          
3957            
3958     }
3959    
3960      
3961  
3962 });
3963  
3964
3965  /*
3966  * - LGPL
3967  *
3968  * row
3969  * 
3970  */
3971
3972 /**
3973  * @class Roo.bootstrap.Row
3974  * @extends Roo.bootstrap.Component
3975  * Bootstrap Row class (contains columns...)
3976  * 
3977  * @constructor
3978  * Create a new Row
3979  * @param {Object} config The config object
3980  */
3981
3982 Roo.bootstrap.Row = function(config){
3983     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3984 };
3985
3986 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3987     
3988     getAutoCreate : function(){
3989        return {
3990             cls: 'row clearfix'
3991        };
3992     }
3993     
3994     
3995 });
3996
3997  
3998
3999  /*
4000  * - LGPL
4001  *
4002  * element
4003  * 
4004  */
4005
4006 /**
4007  * @class Roo.bootstrap.Element
4008  * @extends Roo.bootstrap.Component
4009  * Bootstrap Element class
4010  * @cfg {String} html contents of the element
4011  * @cfg {String} tag tag of the element
4012  * @cfg {String} cls class of the element
4013  * 
4014  * @constructor
4015  * Create a new Element
4016  * @param {Object} config The config object
4017  */
4018
4019 Roo.bootstrap.Element = function(config){
4020     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4021 };
4022
4023 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4024     
4025     tag: 'div',
4026     cls: '',
4027     html: '',
4028      
4029     
4030     getAutoCreate : function(){
4031         
4032         var cfg = {
4033             tag: this.tag,
4034             cls: this.cls,
4035             html: this.html
4036         }
4037         
4038         
4039         
4040         return cfg;
4041     }
4042    
4043 });
4044
4045  
4046
4047  /*
4048  * - LGPL
4049  *
4050  * pagination
4051  * 
4052  */
4053
4054 /**
4055  * @class Roo.bootstrap.Pagination
4056  * @extends Roo.bootstrap.Component
4057  * Bootstrap Pagination class
4058  * @cfg {String} size xs | sm | md | lg
4059  * @cfg {Boolean} inverse false | true
4060  * 
4061  * @constructor
4062  * Create a new Pagination
4063  * @param {Object} config The config object
4064  */
4065
4066 Roo.bootstrap.Pagination = function(config){
4067     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4068 };
4069
4070 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4071     
4072     cls: false,
4073     size: false,
4074     inverse: false,
4075     
4076     getAutoCreate : function(){
4077         var cfg = {
4078             tag: 'ul',
4079                 cls: 'pagination'
4080         };
4081         if (this.inverse) {
4082             cfg.cls += ' inverse';
4083         }
4084         if (this.html) {
4085             cfg.html=this.html;
4086         }
4087         if (this.cls) {
4088             cfg.cls += " " + this.cls;
4089         }
4090         return cfg;
4091     }
4092    
4093 });
4094
4095  
4096
4097  /*
4098  * - LGPL
4099  *
4100  * Pagination item
4101  * 
4102  */
4103
4104
4105 /**
4106  * @class Roo.bootstrap.PaginationItem
4107  * @extends Roo.bootstrap.Component
4108  * Bootstrap PaginationItem class
4109  * @cfg {String} html text
4110  * @cfg {String} href the link
4111  * @cfg {Boolean} preventDefault (true | false) default true
4112  * @cfg {Boolean} active (true | false) default false
4113  * 
4114  * 
4115  * @constructor
4116  * Create a new PaginationItem
4117  * @param {Object} config The config object
4118  */
4119
4120
4121 Roo.bootstrap.PaginationItem = function(config){
4122     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4123     this.addEvents({
4124         // raw events
4125         /**
4126          * @event click
4127          * The raw click event for the entire grid.
4128          * @param {Roo.EventObject} e
4129          */
4130         "click" : true
4131     });
4132 };
4133
4134 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4135     
4136     href : false,
4137     html : false,
4138     preventDefault: true,
4139     active : false,
4140     cls : false,
4141     
4142     getAutoCreate : function(){
4143         var cfg= {
4144             tag: 'li',
4145             cn: [
4146                 {
4147                     tag : 'a',
4148                     href : this.href ? this.href : '#',
4149                     html : this.html ? this.html : ''
4150                 }
4151             ]
4152         };
4153         
4154         if(this.cls){
4155             cfg.cls = this.cls;
4156         }
4157         
4158         if(this.active){
4159             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4160         }
4161         
4162         return cfg;
4163     },
4164     
4165     initEvents: function() {
4166         
4167         this.el.on('click', this.onClick, this);
4168         
4169     },
4170     onClick : function(e)
4171     {
4172         Roo.log('PaginationItem on click ');
4173         if(this.preventDefault){
4174             e.preventDefault();
4175         }
4176         
4177         this.fireEvent('click', this, e);
4178     }
4179    
4180 });
4181
4182  
4183
4184  /*
4185  * - LGPL
4186  *
4187  * slider
4188  * 
4189  */
4190
4191
4192 /**
4193  * @class Roo.bootstrap.Slider
4194  * @extends Roo.bootstrap.Component
4195  * Bootstrap Slider class
4196  *    
4197  * @constructor
4198  * Create a new Slider
4199  * @param {Object} config The config object
4200  */
4201
4202 Roo.bootstrap.Slider = function(config){
4203     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4204 };
4205
4206 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4207     
4208     getAutoCreate : function(){
4209         
4210         var cfg = {
4211             tag: 'div',
4212             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4213             cn: [
4214                 {
4215                     tag: 'a',
4216                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4217                 }
4218             ]
4219         }
4220         
4221         return cfg;
4222     }
4223    
4224 });
4225
4226  /*
4227  * Based on:
4228  * Ext JS Library 1.1.1
4229  * Copyright(c) 2006-2007, Ext JS, LLC.
4230  *
4231  * Originally Released Under LGPL - original licence link has changed is not relivant.
4232  *
4233  * Fork - LGPL
4234  * <script type="text/javascript">
4235  */
4236  
4237
4238 /**
4239  * @class Roo.grid.ColumnModel
4240  * @extends Roo.util.Observable
4241  * This is the default implementation of a ColumnModel used by the Grid. It defines
4242  * the columns in the grid.
4243  * <br>Usage:<br>
4244  <pre><code>
4245  var colModel = new Roo.grid.ColumnModel([
4246         {header: "Ticker", width: 60, sortable: true, locked: true},
4247         {header: "Company Name", width: 150, sortable: true},
4248         {header: "Market Cap.", width: 100, sortable: true},
4249         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4250         {header: "Employees", width: 100, sortable: true, resizable: false}
4251  ]);
4252  </code></pre>
4253  * <p>
4254  
4255  * The config options listed for this class are options which may appear in each
4256  * individual column definition.
4257  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4258  * @constructor
4259  * @param {Object} config An Array of column config objects. See this class's
4260  * config objects for details.
4261 */
4262 Roo.grid.ColumnModel = function(config){
4263         /**
4264      * The config passed into the constructor
4265      */
4266     this.config = config;
4267     this.lookup = {};
4268
4269     // if no id, create one
4270     // if the column does not have a dataIndex mapping,
4271     // map it to the order it is in the config
4272     for(var i = 0, len = config.length; i < len; i++){
4273         var c = config[i];
4274         if(typeof c.dataIndex == "undefined"){
4275             c.dataIndex = i;
4276         }
4277         if(typeof c.renderer == "string"){
4278             c.renderer = Roo.util.Format[c.renderer];
4279         }
4280         if(typeof c.id == "undefined"){
4281             c.id = Roo.id();
4282         }
4283         if(c.editor && c.editor.xtype){
4284             c.editor  = Roo.factory(c.editor, Roo.grid);
4285         }
4286         if(c.editor && c.editor.isFormField){
4287             c.editor = new Roo.grid.GridEditor(c.editor);
4288         }
4289         this.lookup[c.id] = c;
4290     }
4291
4292     /**
4293      * The width of columns which have no width specified (defaults to 100)
4294      * @type Number
4295      */
4296     this.defaultWidth = 100;
4297
4298     /**
4299      * Default sortable of columns which have no sortable specified (defaults to false)
4300      * @type Boolean
4301      */
4302     this.defaultSortable = false;
4303
4304     this.addEvents({
4305         /**
4306              * @event widthchange
4307              * Fires when the width of a column changes.
4308              * @param {ColumnModel} this
4309              * @param {Number} columnIndex The column index
4310              * @param {Number} newWidth The new width
4311              */
4312             "widthchange": true,
4313         /**
4314              * @event headerchange
4315              * Fires when the text of a header changes.
4316              * @param {ColumnModel} this
4317              * @param {Number} columnIndex The column index
4318              * @param {Number} newText The new header text
4319              */
4320             "headerchange": true,
4321         /**
4322              * @event hiddenchange
4323              * Fires when a column is hidden or "unhidden".
4324              * @param {ColumnModel} this
4325              * @param {Number} columnIndex The column index
4326              * @param {Boolean} hidden true if hidden, false otherwise
4327              */
4328             "hiddenchange": true,
4329             /**
4330          * @event columnmoved
4331          * Fires when a column is moved.
4332          * @param {ColumnModel} this
4333          * @param {Number} oldIndex
4334          * @param {Number} newIndex
4335          */
4336         "columnmoved" : true,
4337         /**
4338          * @event columlockchange
4339          * Fires when a column's locked state is changed
4340          * @param {ColumnModel} this
4341          * @param {Number} colIndex
4342          * @param {Boolean} locked true if locked
4343          */
4344         "columnlockchange" : true
4345     });
4346     Roo.grid.ColumnModel.superclass.constructor.call(this);
4347 };
4348 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4349     /**
4350      * @cfg {String} header The header text to display in the Grid view.
4351      */
4352     /**
4353      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4354      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4355      * specified, the column's index is used as an index into the Record's data Array.
4356      */
4357     /**
4358      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4359      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4360      */
4361     /**
4362      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4363      * Defaults to the value of the {@link #defaultSortable} property.
4364      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4365      */
4366     /**
4367      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4368      */
4369     /**
4370      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4371      */
4372     /**
4373      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4374      */
4375     /**
4376      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4377      */
4378     /**
4379      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4380      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4381      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4382      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4383      */
4384        /**
4385      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4386      */
4387     /**
4388      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4389      */
4390
4391     /**
4392      * Returns the id of the column at the specified index.
4393      * @param {Number} index The column index
4394      * @return {String} the id
4395      */
4396     getColumnId : function(index){
4397         return this.config[index].id;
4398     },
4399
4400     /**
4401      * Returns the column for a specified id.
4402      * @param {String} id The column id
4403      * @return {Object} the column
4404      */
4405     getColumnById : function(id){
4406         return this.lookup[id];
4407     },
4408
4409     
4410     /**
4411      * Returns the column for a specified dataIndex.
4412      * @param {String} dataIndex The column dataIndex
4413      * @return {Object|Boolean} the column or false if not found
4414      */
4415     getColumnByDataIndex: function(dataIndex){
4416         var index = this.findColumnIndex(dataIndex);
4417         return index > -1 ? this.config[index] : false;
4418     },
4419     
4420     /**
4421      * Returns the index for a specified column id.
4422      * @param {String} id The column id
4423      * @return {Number} the index, or -1 if not found
4424      */
4425     getIndexById : function(id){
4426         for(var i = 0, len = this.config.length; i < len; i++){
4427             if(this.config[i].id == id){
4428                 return i;
4429             }
4430         }
4431         return -1;
4432     },
4433     
4434     /**
4435      * Returns the index for a specified column dataIndex.
4436      * @param {String} dataIndex The column dataIndex
4437      * @return {Number} the index, or -1 if not found
4438      */
4439     
4440     findColumnIndex : function(dataIndex){
4441         for(var i = 0, len = this.config.length; i < len; i++){
4442             if(this.config[i].dataIndex == dataIndex){
4443                 return i;
4444             }
4445         }
4446         return -1;
4447     },
4448     
4449     
4450     moveColumn : function(oldIndex, newIndex){
4451         var c = this.config[oldIndex];
4452         this.config.splice(oldIndex, 1);
4453         this.config.splice(newIndex, 0, c);
4454         this.dataMap = null;
4455         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4456     },
4457
4458     isLocked : function(colIndex){
4459         return this.config[colIndex].locked === true;
4460     },
4461
4462     setLocked : function(colIndex, value, suppressEvent){
4463         if(this.isLocked(colIndex) == value){
4464             return;
4465         }
4466         this.config[colIndex].locked = value;
4467         if(!suppressEvent){
4468             this.fireEvent("columnlockchange", this, colIndex, value);
4469         }
4470     },
4471
4472     getTotalLockedWidth : function(){
4473         var totalWidth = 0;
4474         for(var i = 0; i < this.config.length; i++){
4475             if(this.isLocked(i) && !this.isHidden(i)){
4476                 this.totalWidth += this.getColumnWidth(i);
4477             }
4478         }
4479         return totalWidth;
4480     },
4481
4482     getLockedCount : function(){
4483         for(var i = 0, len = this.config.length; i < len; i++){
4484             if(!this.isLocked(i)){
4485                 return i;
4486             }
4487         }
4488     },
4489
4490     /**
4491      * Returns the number of columns.
4492      * @return {Number}
4493      */
4494     getColumnCount : function(visibleOnly){
4495         if(visibleOnly === true){
4496             var c = 0;
4497             for(var i = 0, len = this.config.length; i < len; i++){
4498                 if(!this.isHidden(i)){
4499                     c++;
4500                 }
4501             }
4502             return c;
4503         }
4504         return this.config.length;
4505     },
4506
4507     /**
4508      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4509      * @param {Function} fn
4510      * @param {Object} scope (optional)
4511      * @return {Array} result
4512      */
4513     getColumnsBy : function(fn, scope){
4514         var r = [];
4515         for(var i = 0, len = this.config.length; i < len; i++){
4516             var c = this.config[i];
4517             if(fn.call(scope||this, c, i) === true){
4518                 r[r.length] = c;
4519             }
4520         }
4521         return r;
4522     },
4523
4524     /**
4525      * Returns true if the specified column is sortable.
4526      * @param {Number} col The column index
4527      * @return {Boolean}
4528      */
4529     isSortable : function(col){
4530         if(typeof this.config[col].sortable == "undefined"){
4531             return this.defaultSortable;
4532         }
4533         return this.config[col].sortable;
4534     },
4535
4536     /**
4537      * Returns the rendering (formatting) function defined for the column.
4538      * @param {Number} col The column index.
4539      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4540      */
4541     getRenderer : function(col){
4542         if(!this.config[col].renderer){
4543             return Roo.grid.ColumnModel.defaultRenderer;
4544         }
4545         return this.config[col].renderer;
4546     },
4547
4548     /**
4549      * Sets the rendering (formatting) function for a column.
4550      * @param {Number} col The column index
4551      * @param {Function} fn The function to use to process the cell's raw data
4552      * to return HTML markup for the grid view. The render function is called with
4553      * the following parameters:<ul>
4554      * <li>Data value.</li>
4555      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4556      * <li>css A CSS style string to apply to the table cell.</li>
4557      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4558      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4559      * <li>Row index</li>
4560      * <li>Column index</li>
4561      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4562      */
4563     setRenderer : function(col, fn){
4564         this.config[col].renderer = fn;
4565     },
4566
4567     /**
4568      * Returns the width for the specified column.
4569      * @param {Number} col The column index
4570      * @return {Number}
4571      */
4572     getColumnWidth : function(col){
4573         return this.config[col].width * 1 || this.defaultWidth;
4574     },
4575
4576     /**
4577      * Sets the width for a column.
4578      * @param {Number} col The column index
4579      * @param {Number} width The new width
4580      */
4581     setColumnWidth : function(col, width, suppressEvent){
4582         this.config[col].width = width;
4583         this.totalWidth = null;
4584         if(!suppressEvent){
4585              this.fireEvent("widthchange", this, col, width);
4586         }
4587     },
4588
4589     /**
4590      * Returns the total width of all columns.
4591      * @param {Boolean} includeHidden True to include hidden column widths
4592      * @return {Number}
4593      */
4594     getTotalWidth : function(includeHidden){
4595         if(!this.totalWidth){
4596             this.totalWidth = 0;
4597             for(var i = 0, len = this.config.length; i < len; i++){
4598                 if(includeHidden || !this.isHidden(i)){
4599                     this.totalWidth += this.getColumnWidth(i);
4600                 }
4601             }
4602         }
4603         return this.totalWidth;
4604     },
4605
4606     /**
4607      * Returns the header for the specified column.
4608      * @param {Number} col The column index
4609      * @return {String}
4610      */
4611     getColumnHeader : function(col){
4612         return this.config[col].header;
4613     },
4614
4615     /**
4616      * Sets the header for a column.
4617      * @param {Number} col The column index
4618      * @param {String} header The new header
4619      */
4620     setColumnHeader : function(col, header){
4621         this.config[col].header = header;
4622         this.fireEvent("headerchange", this, col, header);
4623     },
4624
4625     /**
4626      * Returns the tooltip for the specified column.
4627      * @param {Number} col The column index
4628      * @return {String}
4629      */
4630     getColumnTooltip : function(col){
4631             return this.config[col].tooltip;
4632     },
4633     /**
4634      * Sets the tooltip for a column.
4635      * @param {Number} col The column index
4636      * @param {String} tooltip The new tooltip
4637      */
4638     setColumnTooltip : function(col, tooltip){
4639             this.config[col].tooltip = tooltip;
4640     },
4641
4642     /**
4643      * Returns the dataIndex for the specified column.
4644      * @param {Number} col The column index
4645      * @return {Number}
4646      */
4647     getDataIndex : function(col){
4648         return this.config[col].dataIndex;
4649     },
4650
4651     /**
4652      * Sets the dataIndex for a column.
4653      * @param {Number} col The column index
4654      * @param {Number} dataIndex The new dataIndex
4655      */
4656     setDataIndex : function(col, dataIndex){
4657         this.config[col].dataIndex = dataIndex;
4658     },
4659
4660     
4661     
4662     /**
4663      * Returns true if the cell is editable.
4664      * @param {Number} colIndex The column index
4665      * @param {Number} rowIndex The row index
4666      * @return {Boolean}
4667      */
4668     isCellEditable : function(colIndex, rowIndex){
4669         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4670     },
4671
4672     /**
4673      * Returns the editor defined for the cell/column.
4674      * return false or null to disable editing.
4675      * @param {Number} colIndex The column index
4676      * @param {Number} rowIndex The row index
4677      * @return {Object}
4678      */
4679     getCellEditor : function(colIndex, rowIndex){
4680         return this.config[colIndex].editor;
4681     },
4682
4683     /**
4684      * Sets if a column is editable.
4685      * @param {Number} col The column index
4686      * @param {Boolean} editable True if the column is editable
4687      */
4688     setEditable : function(col, editable){
4689         this.config[col].editable = editable;
4690     },
4691
4692
4693     /**
4694      * Returns true if the column is hidden.
4695      * @param {Number} colIndex The column index
4696      * @return {Boolean}
4697      */
4698     isHidden : function(colIndex){
4699         return this.config[colIndex].hidden;
4700     },
4701
4702
4703     /**
4704      * Returns true if the column width cannot be changed
4705      */
4706     isFixed : function(colIndex){
4707         return this.config[colIndex].fixed;
4708     },
4709
4710     /**
4711      * Returns true if the column can be resized
4712      * @return {Boolean}
4713      */
4714     isResizable : function(colIndex){
4715         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4716     },
4717     /**
4718      * Sets if a column is hidden.
4719      * @param {Number} colIndex The column index
4720      * @param {Boolean} hidden True if the column is hidden
4721      */
4722     setHidden : function(colIndex, hidden){
4723         this.config[colIndex].hidden = hidden;
4724         this.totalWidth = null;
4725         this.fireEvent("hiddenchange", this, colIndex, hidden);
4726     },
4727
4728     /**
4729      * Sets the editor for a column.
4730      * @param {Number} col The column index
4731      * @param {Object} editor The editor object
4732      */
4733     setEditor : function(col, editor){
4734         this.config[col].editor = editor;
4735     }
4736 });
4737
4738 Roo.grid.ColumnModel.defaultRenderer = function(value){
4739         if(typeof value == "string" && value.length < 1){
4740             return "&#160;";
4741         }
4742         return value;
4743 };
4744
4745 // Alias for backwards compatibility
4746 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4747 /*
4748  * Based on:
4749  * Ext JS Library 1.1.1
4750  * Copyright(c) 2006-2007, Ext JS, LLC.
4751  *
4752  * Originally Released Under LGPL - original licence link has changed is not relivant.
4753  *
4754  * Fork - LGPL
4755  * <script type="text/javascript">
4756  */
4757  
4758 /**
4759  * @class Roo.LoadMask
4760  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4761  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4762  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4763  * element's UpdateManager load indicator and will be destroyed after the initial load.
4764  * @constructor
4765  * Create a new LoadMask
4766  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4767  * @param {Object} config The config object
4768  */
4769 Roo.LoadMask = function(el, config){
4770     this.el = Roo.get(el);
4771     Roo.apply(this, config);
4772     if(this.store){
4773         this.store.on('beforeload', this.onBeforeLoad, this);
4774         this.store.on('load', this.onLoad, this);
4775         this.store.on('loadexception', this.onLoadException, this);
4776         this.removeMask = false;
4777     }else{
4778         var um = this.el.getUpdateManager();
4779         um.showLoadIndicator = false; // disable the default indicator
4780         um.on('beforeupdate', this.onBeforeLoad, this);
4781         um.on('update', this.onLoad, this);
4782         um.on('failure', this.onLoad, this);
4783         this.removeMask = true;
4784     }
4785 };
4786
4787 Roo.LoadMask.prototype = {
4788     /**
4789      * @cfg {Boolean} removeMask
4790      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4791      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4792      */
4793     /**
4794      * @cfg {String} msg
4795      * The text to display in a centered loading message box (defaults to 'Loading...')
4796      */
4797     msg : 'Loading...',
4798     /**
4799      * @cfg {String} msgCls
4800      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4801      */
4802     msgCls : 'x-mask-loading',
4803
4804     /**
4805      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4806      * @type Boolean
4807      */
4808     disabled: false,
4809
4810     /**
4811      * Disables the mask to prevent it from being displayed
4812      */
4813     disable : function(){
4814        this.disabled = true;
4815     },
4816
4817     /**
4818      * Enables the mask so that it can be displayed
4819      */
4820     enable : function(){
4821         this.disabled = false;
4822     },
4823     
4824     onLoadException : function()
4825     {
4826         Roo.log(arguments);
4827         
4828         if (typeof(arguments[3]) != 'undefined') {
4829             Roo.MessageBox.alert("Error loading",arguments[3]);
4830         } 
4831         /*
4832         try {
4833             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4834                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4835             }   
4836         } catch(e) {
4837             
4838         }
4839         */
4840     
4841         
4842         
4843         this.el.unmask(this.removeMask);
4844     },
4845     // private
4846     onLoad : function()
4847     {
4848         this.el.unmask(this.removeMask);
4849     },
4850
4851     // private
4852     onBeforeLoad : function(){
4853         if(!this.disabled){
4854             this.el.mask(this.msg, this.msgCls);
4855         }
4856     },
4857
4858     // private
4859     destroy : function(){
4860         if(this.store){
4861             this.store.un('beforeload', this.onBeforeLoad, this);
4862             this.store.un('load', this.onLoad, this);
4863             this.store.un('loadexception', this.onLoadException, this);
4864         }else{
4865             var um = this.el.getUpdateManager();
4866             um.un('beforeupdate', this.onBeforeLoad, this);
4867             um.un('update', this.onLoad, this);
4868             um.un('failure', this.onLoad, this);
4869         }
4870     }
4871 };/*
4872  * - LGPL
4873  *
4874  * table
4875  * 
4876  */
4877
4878 /**
4879  * @class Roo.bootstrap.Table
4880  * @extends Roo.bootstrap.Component
4881  * Bootstrap Table class
4882  * @cfg {String} cls table class
4883  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4884  * @cfg {String} bgcolor Specifies the background color for a table
4885  * @cfg {Number} border Specifies whether the table cells should have borders or not
4886  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4887  * @cfg {Number} cellspacing Specifies the space between cells
4888  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4889  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4890  * @cfg {String} sortable Specifies that the table should be sortable
4891  * @cfg {String} summary Specifies a summary of the content of a table
4892  * @cfg {Number} width Specifies the width of a table
4893  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4894  * 
4895  * @cfg {boolean} striped Should the rows be alternative striped
4896  * @cfg {boolean} bordered Add borders to the table
4897  * @cfg {boolean} hover Add hover highlighting
4898  * @cfg {boolean} condensed Format condensed
4899  * @cfg {boolean} responsive Format condensed
4900  * @cfg {Boolean} loadMask (true|false) default false
4901  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4902  * @cfg {Boolean} thead (true|false) generate thead, default true
4903  * @cfg {Boolean} RowSelection (true|false) default false
4904  * @cfg {Boolean} CellSelection (true|false) default false
4905  *
4906  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4907  
4908  * 
4909  * @constructor
4910  * Create a new Table
4911  * @param {Object} config The config object
4912  */
4913
4914 Roo.bootstrap.Table = function(config){
4915     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4916     
4917     if (this.sm) {
4918         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4919         this.sm = this.selModel;
4920         this.sm.xmodule = this.xmodule || false;
4921     }
4922     if (this.cm && typeof(this.cm.config) == 'undefined') {
4923         this.colModel = new Roo.grid.ColumnModel(this.cm);
4924         this.cm = this.colModel;
4925         this.cm.xmodule = this.xmodule || false;
4926     }
4927     if (this.store) {
4928         this.store= Roo.factory(this.store, Roo.data);
4929         this.ds = this.store;
4930         this.ds.xmodule = this.xmodule || false;
4931          
4932     }
4933     if (this.footer && this.store) {
4934         this.footer.dataSource = this.ds;
4935         this.footer = Roo.factory(this.footer);
4936     }
4937     
4938     /** @private */
4939     this.addEvents({
4940         /**
4941          * @event cellclick
4942          * Fires when a cell is clicked
4943          * @param {Roo.bootstrap.Table} this
4944          * @param {Roo.Element} el
4945          * @param {Number} rowIndex
4946          * @param {Number} columnIndex
4947          * @param {Roo.EventObject} e
4948          */
4949         "cellclick" : true,
4950         /**
4951          * @event celldblclick
4952          * Fires when a cell is double clicked
4953          * @param {Roo.bootstrap.Table} this
4954          * @param {Roo.Element} el
4955          * @param {Number} rowIndex
4956          * @param {Number} columnIndex
4957          * @param {Roo.EventObject} e
4958          */
4959         "celldblclick" : true,
4960         /**
4961          * @event rowclick
4962          * Fires when a row is clicked
4963          * @param {Roo.bootstrap.Table} this
4964          * @param {Roo.Element} el
4965          * @param {Number} rowIndex
4966          * @param {Roo.EventObject} e
4967          */
4968         "rowclick" : true,
4969         /**
4970          * @event rowdblclick
4971          * Fires when a row is double clicked
4972          * @param {Roo.bootstrap.Table} this
4973          * @param {Roo.Element} el
4974          * @param {Number} rowIndex
4975          * @param {Roo.EventObject} e
4976          */
4977         "rowdblclick" : true,
4978         /**
4979          * @event mouseover
4980          * Fires when a mouseover occur
4981          * @param {Roo.bootstrap.Table} this
4982          * @param {Roo.Element} el
4983          * @param {Number} rowIndex
4984          * @param {Number} columnIndex
4985          * @param {Roo.EventObject} e
4986          */
4987         "mouseover" : true,
4988         /**
4989          * @event mouseout
4990          * Fires when a mouseout occur
4991          * @param {Roo.bootstrap.Table} this
4992          * @param {Roo.Element} el
4993          * @param {Number} rowIndex
4994          * @param {Number} columnIndex
4995          * @param {Roo.EventObject} e
4996          */
4997         "mouseout" : true,
4998         /**
4999          * @event rowclass
5000          * Fires when a row is rendered, so you can change add a style to it.
5001          * @param {Roo.bootstrap.Table} this
5002          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5003          */
5004         'rowclass' : true
5005         
5006     });
5007 };
5008
5009 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5010     
5011     cls: false,
5012     align: false,
5013     bgcolor: false,
5014     border: false,
5015     cellpadding: false,
5016     cellspacing: false,
5017     frame: false,
5018     rules: false,
5019     sortable: false,
5020     summary: false,
5021     width: false,
5022     striped : false,
5023     bordered: false,
5024     hover:  false,
5025     condensed : false,
5026     responsive : false,
5027     sm : false,
5028     cm : false,
5029     store : false,
5030     loadMask : false,
5031     tfoot : true,
5032     thead : true,
5033     RowSelection : false,
5034     CellSelection : false,
5035     layout : false,
5036     
5037     // Roo.Element - the tbody
5038     mainBody: false, 
5039     
5040     getAutoCreate : function(){
5041         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5042         
5043         cfg = {
5044             tag: 'table',
5045             cls : 'table',
5046             cn : []
5047         }
5048             
5049         if (this.striped) {
5050             cfg.cls += ' table-striped';
5051         }
5052         
5053         if (this.hover) {
5054             cfg.cls += ' table-hover';
5055         }
5056         if (this.bordered) {
5057             cfg.cls += ' table-bordered';
5058         }
5059         if (this.condensed) {
5060             cfg.cls += ' table-condensed';
5061         }
5062         if (this.responsive) {
5063             cfg.cls += ' table-responsive';
5064         }
5065         
5066         if (this.cls) {
5067             cfg.cls+=  ' ' +this.cls;
5068         }
5069         
5070         // this lot should be simplifed...
5071         
5072         if (this.align) {
5073             cfg.align=this.align;
5074         }
5075         if (this.bgcolor) {
5076             cfg.bgcolor=this.bgcolor;
5077         }
5078         if (this.border) {
5079             cfg.border=this.border;
5080         }
5081         if (this.cellpadding) {
5082             cfg.cellpadding=this.cellpadding;
5083         }
5084         if (this.cellspacing) {
5085             cfg.cellspacing=this.cellspacing;
5086         }
5087         if (this.frame) {
5088             cfg.frame=this.frame;
5089         }
5090         if (this.rules) {
5091             cfg.rules=this.rules;
5092         }
5093         if (this.sortable) {
5094             cfg.sortable=this.sortable;
5095         }
5096         if (this.summary) {
5097             cfg.summary=this.summary;
5098         }
5099         if (this.width) {
5100             cfg.width=this.width;
5101         }
5102         if (this.layout) {
5103             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5104         }
5105         
5106         if(this.store || this.cm){
5107             if(this.thead){
5108                 cfg.cn.push(this.renderHeader());
5109             }
5110             
5111             cfg.cn.push(this.renderBody());
5112             
5113             if(this.tfoot){
5114                 cfg.cn.push(this.renderFooter());
5115             }
5116             
5117             cfg.cls+=  ' TableGrid';
5118         }
5119         
5120         return { cn : [ cfg ] };
5121     },
5122     
5123     initEvents : function()
5124     {   
5125         if(!this.store || !this.cm){
5126             return;
5127         }
5128         
5129         //Roo.log('initEvents with ds!!!!');
5130         
5131         this.mainBody = this.el.select('tbody', true).first();
5132         
5133         
5134         var _this = this;
5135         
5136         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5137             e.on('click', _this.sort, _this);
5138         });
5139         
5140         this.el.on("click", this.onClick, this);
5141         this.el.on("dblclick", this.onDblClick, this);
5142         
5143         this.parent().el.setStyle('position', 'relative');
5144         if (this.footer) {
5145             this.footer.parentId = this.id;
5146             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5147         }
5148         
5149         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5150         
5151         this.store.on('load', this.onLoad, this);
5152         this.store.on('beforeload', this.onBeforeLoad, this);
5153         this.store.on('update', this.onUpdate, this);
5154         
5155     },
5156     
5157     onMouseover : function(e, el)
5158     {
5159         var cell = Roo.get(el);
5160         
5161         if(!cell){
5162             return;
5163         }
5164         
5165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5166             cell = cell.findParent('td', false, true);
5167         }
5168         
5169         var row = cell.findParent('tr', false, true);
5170         var cellIndex = cell.dom.cellIndex;
5171         var rowIndex = row.dom.rowIndex - 1; // start from 0
5172         
5173         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5174         
5175     },
5176     
5177     onMouseout : function(e, el)
5178     {
5179         var cell = Roo.get(el);
5180         
5181         if(!cell){
5182             return;
5183         }
5184         
5185         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5186             cell = cell.findParent('td', false, true);
5187         }
5188         
5189         var row = cell.findParent('tr', false, true);
5190         var cellIndex = cell.dom.cellIndex;
5191         var rowIndex = row.dom.rowIndex - 1; // start from 0
5192         
5193         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5194         
5195     },
5196     
5197     onClick : function(e, el)
5198     {
5199         var cell = Roo.get(el);
5200         
5201         if(!cell || (!this.CellSelection && !this.RowSelection)){
5202             return;
5203         }
5204         
5205         
5206         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5207             cell = cell.findParent('td', false, true);
5208         }
5209         
5210         var row = cell.findParent('tr', false, true);
5211         var cellIndex = cell.dom.cellIndex;
5212         var rowIndex = row.dom.rowIndex - 1;
5213         
5214         if(this.CellSelection){
5215             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5216         }
5217         
5218         if(this.RowSelection){
5219             this.fireEvent('rowclick', this, row, rowIndex, e);
5220         }
5221         
5222         
5223     },
5224     
5225     onDblClick : function(e,el)
5226     {
5227         var cell = Roo.get(el);
5228         
5229         if(!cell || (!this.CellSelection && !this.RowSelection)){
5230             return;
5231         }
5232         
5233         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5234             cell = cell.findParent('td', false, true);
5235         }
5236         
5237         var row = cell.findParent('tr', false, true);
5238         var cellIndex = cell.dom.cellIndex;
5239         var rowIndex = row.dom.rowIndex - 1;
5240         
5241         if(this.CellSelection){
5242             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5243         }
5244         
5245         if(this.RowSelection){
5246             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5247         }
5248     },
5249     
5250     sort : function(e,el)
5251     {
5252         var col = Roo.get(el)
5253         
5254         if(!col.hasClass('sortable')){
5255             return;
5256         }
5257         
5258         var sort = col.attr('sort');
5259         var dir = 'ASC';
5260         
5261         if(col.hasClass('glyphicon-arrow-up')){
5262             dir = 'DESC';
5263         }
5264         
5265         this.store.sortInfo = {field : sort, direction : dir};
5266         
5267         if (this.footer) {
5268             Roo.log("calling footer first");
5269             this.footer.onClick('first');
5270         } else {
5271         
5272             this.store.load({ params : { start : 0 } });
5273         }
5274     },
5275     
5276     renderHeader : function()
5277     {
5278         var header = {
5279             tag: 'thead',
5280             cn : []
5281         };
5282         
5283         var cm = this.cm;
5284         
5285         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5286             
5287             var config = cm.config[i];
5288                     
5289             var c = {
5290                 tag: 'th',
5291                 style : '',
5292                 html: cm.getColumnHeader(i)
5293             };
5294             
5295             if(typeof(config.hidden) != 'undefined' && config.hidden){
5296                 c.style += ' display:none;';
5297             }
5298             
5299             if(typeof(config.dataIndex) != 'undefined'){
5300                 c.sort = config.dataIndex;
5301             }
5302             
5303             if(typeof(config.sortable) != 'undefined' && config.sortable){
5304                 c.cls = 'sortable';
5305             }
5306             
5307             if(typeof(config.align) != 'undefined' && config.align.length){
5308                 c.style += ' text-align:' + config.align + ';';
5309             }
5310             
5311             if(typeof(config.width) != 'undefined'){
5312                 c.style += ' width:' + config.width + 'px;';
5313             }
5314             
5315             header.cn.push(c)
5316         }
5317         
5318         return header;
5319     },
5320     
5321     renderBody : function()
5322     {
5323         var body = {
5324             tag: 'tbody',
5325             cn : [
5326                 {
5327                     tag: 'tr',
5328                     cn : [
5329                         {
5330                             tag : 'td',
5331                             colspan :  this.cm.getColumnCount()
5332                         }
5333                     ]
5334                 }
5335             ]
5336         };
5337         
5338         return body;
5339     },
5340     
5341     renderFooter : function()
5342     {
5343         var footer = {
5344             tag: 'tfoot',
5345             cn : [
5346                 {
5347                     tag: 'tr',
5348                     cn : [
5349                         {
5350                             tag : 'td',
5351                             colspan :  this.cm.getColumnCount()
5352                         }
5353                     ]
5354                 }
5355             ]
5356         };
5357         
5358         return footer;
5359     },
5360     
5361     
5362     
5363     onLoad : function()
5364     {
5365         Roo.log('ds onload');
5366         this.clear();
5367         
5368         var _this = this;
5369         var cm = this.cm;
5370         var ds = this.store;
5371         
5372         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5373             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5374             
5375             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5376                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5377             }
5378             
5379             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5380                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5381             }
5382         });
5383         
5384         var tbody =  this.mainBody;
5385               
5386         if(ds.getCount() > 0){
5387             ds.data.each(function(d,rowIndex){
5388                 var row =  this.renderRow(cm, ds, rowIndex);
5389                 
5390                 tbody.createChild(row);
5391                 
5392                 var _this = this;
5393                 
5394                 if(row.cellObjects.length){
5395                     Roo.each(row.cellObjects, function(r){
5396                         _this.renderCellObject(r);
5397                     })
5398                 }
5399                 
5400             }, this);
5401         }
5402         
5403         Roo.each(this.el.select('tbody td', true).elements, function(e){
5404             e.on('mouseover', _this.onMouseover, _this);
5405         });
5406         
5407         Roo.each(this.el.select('tbody td', true).elements, function(e){
5408             e.on('mouseout', _this.onMouseout, _this);
5409         });
5410
5411         //if(this.loadMask){
5412         //    this.maskEl.hide();
5413         //}
5414     },
5415     
5416     
5417     onUpdate : function(ds,record)
5418     {
5419         this.refreshRow(record);
5420     },
5421     onRemove : function(ds, record, index, isUpdate){
5422         if(isUpdate !== true){
5423             this.fireEvent("beforerowremoved", this, index, record);
5424         }
5425         var bt = this.mainBody.dom;
5426         if(bt.rows[index]){
5427             bt.removeChild(bt.rows[index]);
5428         }
5429         
5430         if(isUpdate !== true){
5431             //this.stripeRows(index);
5432             //this.syncRowHeights(index, index);
5433             //this.layout();
5434             this.fireEvent("rowremoved", this, index, record);
5435         }
5436     },
5437     
5438     
5439     refreshRow : function(record){
5440         var ds = this.store, index;
5441         if(typeof record == 'number'){
5442             index = record;
5443             record = ds.getAt(index);
5444         }else{
5445             index = ds.indexOf(record);
5446         }
5447         this.insertRow(ds, index, true);
5448         this.onRemove(ds, record, index+1, true);
5449         //this.syncRowHeights(index, index);
5450         //this.layout();
5451         this.fireEvent("rowupdated", this, index, record);
5452     },
5453     
5454     insertRow : function(dm, rowIndex, isUpdate){
5455         
5456         if(!isUpdate){
5457             this.fireEvent("beforerowsinserted", this, rowIndex);
5458         }
5459             //var s = this.getScrollState();
5460         var row = this.renderRow(this.cm, this.store, rowIndex);
5461         // insert before rowIndex..
5462         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5463         
5464         var _this = this;
5465                 
5466         if(row.cellObjects.length){
5467             Roo.each(row.cellObjects, function(r){
5468                 _this.renderCellObject(r);
5469             })
5470         }
5471             
5472         if(!isUpdate){
5473             this.fireEvent("rowsinserted", this, rowIndex);
5474             //this.syncRowHeights(firstRow, lastRow);
5475             //this.stripeRows(firstRow);
5476             //this.layout();
5477         }
5478         
5479     },
5480     
5481     
5482     getRowDom : function(rowIndex)
5483     {
5484         // not sure if I need to check this.. but let's do it anyway..
5485         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5486                 this.mainBody.dom.rows[rowIndex] : false
5487     },
5488     // returns the object tree for a tr..
5489   
5490     
5491     renderRow : function(cm, ds, rowIndex) {
5492         
5493         var d = ds.getAt(rowIndex);
5494         
5495         var row = {
5496             tag : 'tr',
5497             cn : []
5498         };
5499             
5500         var cellObjects = [];
5501         
5502         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5503             var config = cm.config[i];
5504             
5505             var renderer = cm.getRenderer(i);
5506             var value = '';
5507             var id = false;
5508             
5509             if(typeof(renderer) !== 'undefined'){
5510                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5511             }
5512             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5513             // and are rendered into the cells after the row is rendered - using the id for the element.
5514             
5515             if(typeof(value) === 'object'){
5516                 id = Roo.id();
5517                 cellObjects.push({
5518                     container : id,
5519                     cfg : value 
5520                 })
5521             }
5522             
5523             var rowcfg = {
5524                 record: d,
5525                 rowIndex : rowIndex,
5526                 colIndex : i,
5527                 rowClass : ''
5528             }
5529
5530             this.fireEvent('rowclass', this, rowcfg);
5531             
5532             var td = {
5533                 tag: 'td',
5534                 cls : rowcfg.rowClass,
5535                 style: '',
5536                 html: (typeof(value) === 'object') ? '' : value
5537             };
5538             
5539             if (id) {
5540                 td.id = id;
5541             }
5542             
5543             if(typeof(config.hidden) != 'undefined' && config.hidden){
5544                 td.style += ' display:none;';
5545             }
5546             
5547             if(typeof(config.align) != 'undefined' && config.align.length){
5548                 td.style += ' text-align:' + config.align + ';';
5549             }
5550             
5551             if(typeof(config.width) != 'undefined'){
5552                 td.style += ' width:' +  config.width + 'px;';
5553             }
5554              
5555             row.cn.push(td);
5556            
5557         }
5558         
5559         row.cellObjects = cellObjects;
5560         
5561         return row;
5562           
5563     },
5564     
5565     
5566     
5567     onBeforeLoad : function()
5568     {
5569         //Roo.log('ds onBeforeLoad');
5570         
5571         //this.clear();
5572         
5573         //if(this.loadMask){
5574         //    this.maskEl.show();
5575         //}
5576     },
5577     
5578     clear : function()
5579     {
5580         this.el.select('tbody', true).first().dom.innerHTML = '';
5581     },
5582     
5583     getSelectionModel : function(){
5584         if(!this.selModel){
5585             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5586         }
5587         return this.selModel;
5588     },
5589     /*
5590      * Render the Roo.bootstrap object from renderder
5591      */
5592     renderCellObject : function(r)
5593     {
5594         var _this = this;
5595         
5596         var t = r.cfg.render(r.container);
5597         
5598         if(r.cfg.cn){
5599             Roo.each(r.cfg.cn, function(c){
5600                 var child = {
5601                     container: t.getChildContainer(),
5602                     cfg: c
5603                 }
5604                 _this.renderCellObject(child);
5605             })
5606         }
5607     }
5608    
5609 });
5610
5611  
5612
5613  /*
5614  * - LGPL
5615  *
5616  * table cell
5617  * 
5618  */
5619
5620 /**
5621  * @class Roo.bootstrap.TableCell
5622  * @extends Roo.bootstrap.Component
5623  * Bootstrap TableCell class
5624  * @cfg {String} html cell contain text
5625  * @cfg {String} cls cell class
5626  * @cfg {String} tag cell tag (td|th) default td
5627  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5628  * @cfg {String} align Aligns the content in a cell
5629  * @cfg {String} axis Categorizes cells
5630  * @cfg {String} bgcolor Specifies the background color of a cell
5631  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5632  * @cfg {Number} colspan Specifies the number of columns a cell should span
5633  * @cfg {String} headers Specifies one or more header cells a cell is related to
5634  * @cfg {Number} height Sets the height of a cell
5635  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5636  * @cfg {Number} rowspan Sets the number of rows a cell should span
5637  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5638  * @cfg {String} valign Vertical aligns the content in a cell
5639  * @cfg {Number} width Specifies the width of a cell
5640  * 
5641  * @constructor
5642  * Create a new TableCell
5643  * @param {Object} config The config object
5644  */
5645
5646 Roo.bootstrap.TableCell = function(config){
5647     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5648 };
5649
5650 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5651     
5652     html: false,
5653     cls: false,
5654     tag: false,
5655     abbr: false,
5656     align: false,
5657     axis: false,
5658     bgcolor: false,
5659     charoff: false,
5660     colspan: false,
5661     headers: false,
5662     height: false,
5663     nowrap: false,
5664     rowspan: false,
5665     scope: false,
5666     valign: false,
5667     width: false,
5668     
5669     
5670     getAutoCreate : function(){
5671         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5672         
5673         cfg = {
5674             tag: 'td'
5675         }
5676         
5677         if(this.tag){
5678             cfg.tag = this.tag;
5679         }
5680         
5681         if (this.html) {
5682             cfg.html=this.html
5683         }
5684         if (this.cls) {
5685             cfg.cls=this.cls
5686         }
5687         if (this.abbr) {
5688             cfg.abbr=this.abbr
5689         }
5690         if (this.align) {
5691             cfg.align=this.align
5692         }
5693         if (this.axis) {
5694             cfg.axis=this.axis
5695         }
5696         if (this.bgcolor) {
5697             cfg.bgcolor=this.bgcolor
5698         }
5699         if (this.charoff) {
5700             cfg.charoff=this.charoff
5701         }
5702         if (this.colspan) {
5703             cfg.colspan=this.colspan
5704         }
5705         if (this.headers) {
5706             cfg.headers=this.headers
5707         }
5708         if (this.height) {
5709             cfg.height=this.height
5710         }
5711         if (this.nowrap) {
5712             cfg.nowrap=this.nowrap
5713         }
5714         if (this.rowspan) {
5715             cfg.rowspan=this.rowspan
5716         }
5717         if (this.scope) {
5718             cfg.scope=this.scope
5719         }
5720         if (this.valign) {
5721             cfg.valign=this.valign
5722         }
5723         if (this.width) {
5724             cfg.width=this.width
5725         }
5726         
5727         
5728         return cfg;
5729     }
5730    
5731 });
5732
5733  
5734
5735  /*
5736  * - LGPL
5737  *
5738  * table row
5739  * 
5740  */
5741
5742 /**
5743  * @class Roo.bootstrap.TableRow
5744  * @extends Roo.bootstrap.Component
5745  * Bootstrap TableRow class
5746  * @cfg {String} cls row class
5747  * @cfg {String} align Aligns the content in a table row
5748  * @cfg {String} bgcolor Specifies a background color for a table row
5749  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5750  * @cfg {String} valign Vertical aligns the content in a table row
5751  * 
5752  * @constructor
5753  * Create a new TableRow
5754  * @param {Object} config The config object
5755  */
5756
5757 Roo.bootstrap.TableRow = function(config){
5758     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5759 };
5760
5761 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5762     
5763     cls: false,
5764     align: false,
5765     bgcolor: false,
5766     charoff: false,
5767     valign: false,
5768     
5769     getAutoCreate : function(){
5770         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5771         
5772         cfg = {
5773             tag: 'tr'
5774         }
5775             
5776         if(this.cls){
5777             cfg.cls = this.cls;
5778         }
5779         if(this.align){
5780             cfg.align = this.align;
5781         }
5782         if(this.bgcolor){
5783             cfg.bgcolor = this.bgcolor;
5784         }
5785         if(this.charoff){
5786             cfg.charoff = this.charoff;
5787         }
5788         if(this.valign){
5789             cfg.valign = this.valign;
5790         }
5791         
5792         return cfg;
5793     }
5794    
5795 });
5796
5797  
5798
5799  /*
5800  * - LGPL
5801  *
5802  * table body
5803  * 
5804  */
5805
5806 /**
5807  * @class Roo.bootstrap.TableBody
5808  * @extends Roo.bootstrap.Component
5809  * Bootstrap TableBody class
5810  * @cfg {String} cls element class
5811  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5812  * @cfg {String} align Aligns the content inside the element
5813  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5814  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5815  * 
5816  * @constructor
5817  * Create a new TableBody
5818  * @param {Object} config The config object
5819  */
5820
5821 Roo.bootstrap.TableBody = function(config){
5822     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5823 };
5824
5825 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5826     
5827     cls: false,
5828     tag: false,
5829     align: false,
5830     charoff: false,
5831     valign: false,
5832     
5833     getAutoCreate : function(){
5834         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5835         
5836         cfg = {
5837             tag: 'tbody'
5838         }
5839             
5840         if (this.cls) {
5841             cfg.cls=this.cls
5842         }
5843         if(this.tag){
5844             cfg.tag = this.tag;
5845         }
5846         
5847         if(this.align){
5848             cfg.align = this.align;
5849         }
5850         if(this.charoff){
5851             cfg.charoff = this.charoff;
5852         }
5853         if(this.valign){
5854             cfg.valign = this.valign;
5855         }
5856         
5857         return cfg;
5858     }
5859     
5860     
5861 //    initEvents : function()
5862 //    {
5863 //        
5864 //        if(!this.store){
5865 //            return;
5866 //        }
5867 //        
5868 //        this.store = Roo.factory(this.store, Roo.data);
5869 //        this.store.on('load', this.onLoad, this);
5870 //        
5871 //        this.store.load();
5872 //        
5873 //    },
5874 //    
5875 //    onLoad: function () 
5876 //    {   
5877 //        this.fireEvent('load', this);
5878 //    }
5879 //    
5880 //   
5881 });
5882
5883  
5884
5885  /*
5886  * Based on:
5887  * Ext JS Library 1.1.1
5888  * Copyright(c) 2006-2007, Ext JS, LLC.
5889  *
5890  * Originally Released Under LGPL - original licence link has changed is not relivant.
5891  *
5892  * Fork - LGPL
5893  * <script type="text/javascript">
5894  */
5895
5896 // as we use this in bootstrap.
5897 Roo.namespace('Roo.form');
5898  /**
5899  * @class Roo.form.Action
5900  * Internal Class used to handle form actions
5901  * @constructor
5902  * @param {Roo.form.BasicForm} el The form element or its id
5903  * @param {Object} config Configuration options
5904  */
5905
5906  
5907  
5908 // define the action interface
5909 Roo.form.Action = function(form, options){
5910     this.form = form;
5911     this.options = options || {};
5912 };
5913 /**
5914  * Client Validation Failed
5915  * @const 
5916  */
5917 Roo.form.Action.CLIENT_INVALID = 'client';
5918 /**
5919  * Server Validation Failed
5920  * @const 
5921  */
5922 Roo.form.Action.SERVER_INVALID = 'server';
5923  /**
5924  * Connect to Server Failed
5925  * @const 
5926  */
5927 Roo.form.Action.CONNECT_FAILURE = 'connect';
5928 /**
5929  * Reading Data from Server Failed
5930  * @const 
5931  */
5932 Roo.form.Action.LOAD_FAILURE = 'load';
5933
5934 Roo.form.Action.prototype = {
5935     type : 'default',
5936     failureType : undefined,
5937     response : undefined,
5938     result : undefined,
5939
5940     // interface method
5941     run : function(options){
5942
5943     },
5944
5945     // interface method
5946     success : function(response){
5947
5948     },
5949
5950     // interface method
5951     handleResponse : function(response){
5952
5953     },
5954
5955     // default connection failure
5956     failure : function(response){
5957         
5958         this.response = response;
5959         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5960         this.form.afterAction(this, false);
5961     },
5962
5963     processResponse : function(response){
5964         this.response = response;
5965         if(!response.responseText){
5966             return true;
5967         }
5968         this.result = this.handleResponse(response);
5969         return this.result;
5970     },
5971
5972     // utility functions used internally
5973     getUrl : function(appendParams){
5974         var url = this.options.url || this.form.url || this.form.el.dom.action;
5975         if(appendParams){
5976             var p = this.getParams();
5977             if(p){
5978                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5979             }
5980         }
5981         return url;
5982     },
5983
5984     getMethod : function(){
5985         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5986     },
5987
5988     getParams : function(){
5989         var bp = this.form.baseParams;
5990         var p = this.options.params;
5991         if(p){
5992             if(typeof p == "object"){
5993                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5994             }else if(typeof p == 'string' && bp){
5995                 p += '&' + Roo.urlEncode(bp);
5996             }
5997         }else if(bp){
5998             p = Roo.urlEncode(bp);
5999         }
6000         return p;
6001     },
6002
6003     createCallback : function(){
6004         return {
6005             success: this.success,
6006             failure: this.failure,
6007             scope: this,
6008             timeout: (this.form.timeout*1000),
6009             upload: this.form.fileUpload ? this.success : undefined
6010         };
6011     }
6012 };
6013
6014 Roo.form.Action.Submit = function(form, options){
6015     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6016 };
6017
6018 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6019     type : 'submit',
6020
6021     haveProgress : false,
6022     uploadComplete : false,
6023     
6024     // uploadProgress indicator.
6025     uploadProgress : function()
6026     {
6027         if (!this.form.progressUrl) {
6028             return;
6029         }
6030         
6031         if (!this.haveProgress) {
6032             Roo.MessageBox.progress("Uploading", "Uploading");
6033         }
6034         if (this.uploadComplete) {
6035            Roo.MessageBox.hide();
6036            return;
6037         }
6038         
6039         this.haveProgress = true;
6040    
6041         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6042         
6043         var c = new Roo.data.Connection();
6044         c.request({
6045             url : this.form.progressUrl,
6046             params: {
6047                 id : uid
6048             },
6049             method: 'GET',
6050             success : function(req){
6051                //console.log(data);
6052                 var rdata = false;
6053                 var edata;
6054                 try  {
6055                    rdata = Roo.decode(req.responseText)
6056                 } catch (e) {
6057                     Roo.log("Invalid data from server..");
6058                     Roo.log(edata);
6059                     return;
6060                 }
6061                 if (!rdata || !rdata.success) {
6062                     Roo.log(rdata);
6063                     Roo.MessageBox.alert(Roo.encode(rdata));
6064                     return;
6065                 }
6066                 var data = rdata.data;
6067                 
6068                 if (this.uploadComplete) {
6069                    Roo.MessageBox.hide();
6070                    return;
6071                 }
6072                    
6073                 if (data){
6074                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6075                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6076                     );
6077                 }
6078                 this.uploadProgress.defer(2000,this);
6079             },
6080        
6081             failure: function(data) {
6082                 Roo.log('progress url failed ');
6083                 Roo.log(data);
6084             },
6085             scope : this
6086         });
6087            
6088     },
6089     
6090     
6091     run : function()
6092     {
6093         // run get Values on the form, so it syncs any secondary forms.
6094         this.form.getValues();
6095         
6096         var o = this.options;
6097         var method = this.getMethod();
6098         var isPost = method == 'POST';
6099         if(o.clientValidation === false || this.form.isValid()){
6100             
6101             if (this.form.progressUrl) {
6102                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6103                     (new Date() * 1) + '' + Math.random());
6104                     
6105             } 
6106             
6107             
6108             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6109                 form:this.form.el.dom,
6110                 url:this.getUrl(!isPost),
6111                 method: method,
6112                 params:isPost ? this.getParams() : null,
6113                 isUpload: this.form.fileUpload
6114             }));
6115             
6116             this.uploadProgress();
6117
6118         }else if (o.clientValidation !== false){ // client validation failed
6119             this.failureType = Roo.form.Action.CLIENT_INVALID;
6120             this.form.afterAction(this, false);
6121         }
6122     },
6123
6124     success : function(response)
6125     {
6126         this.uploadComplete= true;
6127         if (this.haveProgress) {
6128             Roo.MessageBox.hide();
6129         }
6130         
6131         
6132         var result = this.processResponse(response);
6133         if(result === true || result.success){
6134             this.form.afterAction(this, true);
6135             return;
6136         }
6137         if(result.errors){
6138             this.form.markInvalid(result.errors);
6139             this.failureType = Roo.form.Action.SERVER_INVALID;
6140         }
6141         this.form.afterAction(this, false);
6142     },
6143     failure : function(response)
6144     {
6145         this.uploadComplete= true;
6146         if (this.haveProgress) {
6147             Roo.MessageBox.hide();
6148         }
6149         
6150         this.response = response;
6151         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6152         this.form.afterAction(this, false);
6153     },
6154     
6155     handleResponse : function(response){
6156         if(this.form.errorReader){
6157             var rs = this.form.errorReader.read(response);
6158             var errors = [];
6159             if(rs.records){
6160                 for(var i = 0, len = rs.records.length; i < len; i++) {
6161                     var r = rs.records[i];
6162                     errors[i] = r.data;
6163                 }
6164             }
6165             if(errors.length < 1){
6166                 errors = null;
6167             }
6168             return {
6169                 success : rs.success,
6170                 errors : errors
6171             };
6172         }
6173         var ret = false;
6174         try {
6175             ret = Roo.decode(response.responseText);
6176         } catch (e) {
6177             ret = {
6178                 success: false,
6179                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6180                 errors : []
6181             };
6182         }
6183         return ret;
6184         
6185     }
6186 });
6187
6188
6189 Roo.form.Action.Load = function(form, options){
6190     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6191     this.reader = this.form.reader;
6192 };
6193
6194 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6195     type : 'load',
6196
6197     run : function(){
6198         
6199         Roo.Ajax.request(Roo.apply(
6200                 this.createCallback(), {
6201                     method:this.getMethod(),
6202                     url:this.getUrl(false),
6203                     params:this.getParams()
6204         }));
6205     },
6206
6207     success : function(response){
6208         
6209         var result = this.processResponse(response);
6210         if(result === true || !result.success || !result.data){
6211             this.failureType = Roo.form.Action.LOAD_FAILURE;
6212             this.form.afterAction(this, false);
6213             return;
6214         }
6215         this.form.clearInvalid();
6216         this.form.setValues(result.data);
6217         this.form.afterAction(this, true);
6218     },
6219
6220     handleResponse : function(response){
6221         if(this.form.reader){
6222             var rs = this.form.reader.read(response);
6223             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6224             return {
6225                 success : rs.success,
6226                 data : data
6227             };
6228         }
6229         return Roo.decode(response.responseText);
6230     }
6231 });
6232
6233 Roo.form.Action.ACTION_TYPES = {
6234     'load' : Roo.form.Action.Load,
6235     'submit' : Roo.form.Action.Submit
6236 };/*
6237  * - LGPL
6238  *
6239  * form
6240  * 
6241  */
6242
6243 /**
6244  * @class Roo.bootstrap.Form
6245  * @extends Roo.bootstrap.Component
6246  * Bootstrap Form class
6247  * @cfg {String} method  GET | POST (default POST)
6248  * @cfg {String} labelAlign top | left (default top)
6249  * @cfg {String} align left  | right - for navbars
6250  * @cfg {Boolean} loadMask load mask when submit (default true)
6251
6252  * 
6253  * @constructor
6254  * Create a new Form
6255  * @param {Object} config The config object
6256  */
6257
6258
6259 Roo.bootstrap.Form = function(config){
6260     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6261     this.addEvents({
6262         /**
6263          * @event clientvalidation
6264          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6265          * @param {Form} this
6266          * @param {Boolean} valid true if the form has passed client-side validation
6267          */
6268         clientvalidation: true,
6269         /**
6270          * @event beforeaction
6271          * Fires before any action is performed. Return false to cancel the action.
6272          * @param {Form} this
6273          * @param {Action} action The action to be performed
6274          */
6275         beforeaction: true,
6276         /**
6277          * @event actionfailed
6278          * Fires when an action fails.
6279          * @param {Form} this
6280          * @param {Action} action The action that failed
6281          */
6282         actionfailed : true,
6283         /**
6284          * @event actioncomplete
6285          * Fires when an action is completed.
6286          * @param {Form} this
6287          * @param {Action} action The action that completed
6288          */
6289         actioncomplete : true
6290     });
6291     
6292 };
6293
6294 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6295       
6296      /**
6297      * @cfg {String} method
6298      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6299      */
6300     method : 'POST',
6301     /**
6302      * @cfg {String} url
6303      * The URL to use for form actions if one isn't supplied in the action options.
6304      */
6305     /**
6306      * @cfg {Boolean} fileUpload
6307      * Set to true if this form is a file upload.
6308      */
6309      
6310     /**
6311      * @cfg {Object} baseParams
6312      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6313      */
6314       
6315     /**
6316      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6317      */
6318     timeout: 30,
6319     /**
6320      * @cfg {Sting} align (left|right) for navbar forms
6321      */
6322     align : 'left',
6323
6324     // private
6325     activeAction : null,
6326  
6327     /**
6328      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6329      * element by passing it or its id or mask the form itself by passing in true.
6330      * @type Mixed
6331      */
6332     waitMsgTarget : false,
6333     
6334     loadMask : true,
6335     
6336     getAutoCreate : function(){
6337         
6338         var cfg = {
6339             tag: 'form',
6340             method : this.method || 'POST',
6341             id : this.id || Roo.id(),
6342             cls : ''
6343         }
6344         if (this.parent().xtype.match(/^Nav/)) {
6345             cfg.cls = 'navbar-form navbar-' + this.align;
6346             
6347         }
6348         
6349         if (this.labelAlign == 'left' ) {
6350             cfg.cls += ' form-horizontal';
6351         }
6352         
6353         
6354         return cfg;
6355     },
6356     initEvents : function()
6357     {
6358         this.el.on('submit', this.onSubmit, this);
6359         // this was added as random key presses on the form where triggering form submit.
6360         this.el.on('keypress', function(e) {
6361             if (e.getCharCode() != 13) {
6362                 return true;
6363             }
6364             // we might need to allow it for textareas.. and some other items.
6365             // check e.getTarget().
6366             
6367             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6368                 return true;
6369             }
6370         
6371             Roo.log("keypress blocked");
6372             
6373             e.preventDefault();
6374             return false;
6375         });
6376         
6377     },
6378     // private
6379     onSubmit : function(e){
6380         e.stopEvent();
6381     },
6382     
6383      /**
6384      * Returns true if client-side validation on the form is successful.
6385      * @return Boolean
6386      */
6387     isValid : function(){
6388         var items = this.getItems();
6389         var valid = true;
6390         items.each(function(f){
6391            if(!f.validate()){
6392                valid = false;
6393                
6394            }
6395         });
6396         return valid;
6397     },
6398     /**
6399      * Returns true if any fields in this form have changed since their original load.
6400      * @return Boolean
6401      */
6402     isDirty : function(){
6403         var dirty = false;
6404         var items = this.getItems();
6405         items.each(function(f){
6406            if(f.isDirty()){
6407                dirty = true;
6408                return false;
6409            }
6410            return true;
6411         });
6412         return dirty;
6413     },
6414      /**
6415      * Performs a predefined action (submit or load) or custom actions you define on this form.
6416      * @param {String} actionName The name of the action type
6417      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6418      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6419      * accept other config options):
6420      * <pre>
6421 Property          Type             Description
6422 ----------------  ---------------  ----------------------------------------------------------------------------------
6423 url               String           The url for the action (defaults to the form's url)
6424 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6425 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6426 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6427                                    validate the form on the client (defaults to false)
6428      * </pre>
6429      * @return {BasicForm} this
6430      */
6431     doAction : function(action, options){
6432         if(typeof action == 'string'){
6433             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6434         }
6435         if(this.fireEvent('beforeaction', this, action) !== false){
6436             this.beforeAction(action);
6437             action.run.defer(100, action);
6438         }
6439         return this;
6440     },
6441     
6442     // private
6443     beforeAction : function(action){
6444         var o = action.options;
6445         
6446         if(this.loadMask){
6447             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6448         }
6449         // not really supported yet.. ??
6450         
6451         //if(this.waitMsgTarget === true){
6452         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6453         //}else if(this.waitMsgTarget){
6454         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6455         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6456         //}else {
6457         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6458        // }
6459          
6460     },
6461
6462     // private
6463     afterAction : function(action, success){
6464         this.activeAction = null;
6465         var o = action.options;
6466         
6467         //if(this.waitMsgTarget === true){
6468             this.el.unmask();
6469         //}else if(this.waitMsgTarget){
6470         //    this.waitMsgTarget.unmask();
6471         //}else{
6472         //    Roo.MessageBox.updateProgress(1);
6473         //    Roo.MessageBox.hide();
6474        // }
6475         // 
6476         if(success){
6477             if(o.reset){
6478                 this.reset();
6479             }
6480             Roo.callback(o.success, o.scope, [this, action]);
6481             this.fireEvent('actioncomplete', this, action);
6482             
6483         }else{
6484             
6485             // failure condition..
6486             // we have a scenario where updates need confirming.
6487             // eg. if a locking scenario exists..
6488             // we look for { errors : { needs_confirm : true }} in the response.
6489             if (
6490                 (typeof(action.result) != 'undefined')  &&
6491                 (typeof(action.result.errors) != 'undefined')  &&
6492                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6493            ){
6494                 var _t = this;
6495                 Roo.log("not supported yet");
6496                  /*
6497                 
6498                 Roo.MessageBox.confirm(
6499                     "Change requires confirmation",
6500                     action.result.errorMsg,
6501                     function(r) {
6502                         if (r != 'yes') {
6503                             return;
6504                         }
6505                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6506                     }
6507                     
6508                 );
6509                 */
6510                 
6511                 
6512                 return;
6513             }
6514             
6515             Roo.callback(o.failure, o.scope, [this, action]);
6516             // show an error message if no failed handler is set..
6517             if (!this.hasListener('actionfailed')) {
6518                 Roo.log("need to add dialog support");
6519                 /*
6520                 Roo.MessageBox.alert("Error",
6521                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6522                         action.result.errorMsg :
6523                         "Saving Failed, please check your entries or try again"
6524                 );
6525                 */
6526             }
6527             
6528             this.fireEvent('actionfailed', this, action);
6529         }
6530         
6531     },
6532     /**
6533      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6534      * @param {String} id The value to search for
6535      * @return Field
6536      */
6537     findField : function(id){
6538         var items = this.getItems();
6539         var field = items.get(id);
6540         if(!field){
6541              items.each(function(f){
6542                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6543                     field = f;
6544                     return false;
6545                 }
6546                 return true;
6547             });
6548         }
6549         return field || null;
6550     },
6551      /**
6552      * Mark fields in this form invalid in bulk.
6553      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6554      * @return {BasicForm} this
6555      */
6556     markInvalid : function(errors){
6557         if(errors instanceof Array){
6558             for(var i = 0, len = errors.length; i < len; i++){
6559                 var fieldError = errors[i];
6560                 var f = this.findField(fieldError.id);
6561                 if(f){
6562                     f.markInvalid(fieldError.msg);
6563                 }
6564             }
6565         }else{
6566             var field, id;
6567             for(id in errors){
6568                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6569                     field.markInvalid(errors[id]);
6570                 }
6571             }
6572         }
6573         //Roo.each(this.childForms || [], function (f) {
6574         //    f.markInvalid(errors);
6575         //});
6576         
6577         return this;
6578     },
6579
6580     /**
6581      * Set values for fields in this form in bulk.
6582      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6583      * @return {BasicForm} this
6584      */
6585     setValues : function(values){
6586         if(values instanceof Array){ // array of objects
6587             for(var i = 0, len = values.length; i < len; i++){
6588                 var v = values[i];
6589                 var f = this.findField(v.id);
6590                 if(f){
6591                     f.setValue(v.value);
6592                     if(this.trackResetOnLoad){
6593                         f.originalValue = f.getValue();
6594                     }
6595                 }
6596             }
6597         }else{ // object hash
6598             var field, id;
6599             for(id in values){
6600                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6601                     
6602                     if (field.setFromData && 
6603                         field.valueField && 
6604                         field.displayField &&
6605                         // combos' with local stores can 
6606                         // be queried via setValue()
6607                         // to set their value..
6608                         (field.store && !field.store.isLocal)
6609                         ) {
6610                         // it's a combo
6611                         var sd = { };
6612                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6613                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6614                         field.setFromData(sd);
6615                         
6616                     } else {
6617                         field.setValue(values[id]);
6618                     }
6619                     
6620                     
6621                     if(this.trackResetOnLoad){
6622                         field.originalValue = field.getValue();
6623                     }
6624                 }
6625             }
6626         }
6627          
6628         //Roo.each(this.childForms || [], function (f) {
6629         //    f.setValues(values);
6630         //});
6631                 
6632         return this;
6633     },
6634
6635     /**
6636      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6637      * they are returned as an array.
6638      * @param {Boolean} asString
6639      * @return {Object}
6640      */
6641     getValues : function(asString){
6642         //if (this.childForms) {
6643             // copy values from the child forms
6644         //    Roo.each(this.childForms, function (f) {
6645         //        this.setValues(f.getValues());
6646         //    }, this);
6647         //}
6648         
6649         
6650         
6651         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6652         if(asString === true){
6653             return fs;
6654         }
6655         return Roo.urlDecode(fs);
6656     },
6657     
6658     /**
6659      * Returns the fields in this form as an object with key/value pairs. 
6660      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6661      * @return {Object}
6662      */
6663     getFieldValues : function(with_hidden)
6664     {
6665         var items = this.getItems();
6666         var ret = {};
6667         items.each(function(f){
6668             if (!f.getName()) {
6669                 return;
6670             }
6671             var v = f.getValue();
6672             if (f.inputType =='radio') {
6673                 if (typeof(ret[f.getName()]) == 'undefined') {
6674                     ret[f.getName()] = ''; // empty..
6675                 }
6676                 
6677                 if (!f.el.dom.checked) {
6678                     return;
6679                     
6680                 }
6681                 v = f.el.dom.value;
6682                 
6683             }
6684             
6685             // not sure if this supported any more..
6686             if ((typeof(v) == 'object') && f.getRawValue) {
6687                 v = f.getRawValue() ; // dates..
6688             }
6689             // combo boxes where name != hiddenName...
6690             if (f.name != f.getName()) {
6691                 ret[f.name] = f.getRawValue();
6692             }
6693             ret[f.getName()] = v;
6694         });
6695         
6696         return ret;
6697     },
6698
6699     /**
6700      * Clears all invalid messages in this form.
6701      * @return {BasicForm} this
6702      */
6703     clearInvalid : function(){
6704         var items = this.getItems();
6705         
6706         items.each(function(f){
6707            f.clearInvalid();
6708         });
6709         
6710         
6711         
6712         return this;
6713     },
6714
6715     /**
6716      * Resets this form.
6717      * @return {BasicForm} this
6718      */
6719     reset : function(){
6720         var items = this.getItems();
6721         items.each(function(f){
6722             f.reset();
6723         });
6724         
6725         Roo.each(this.childForms || [], function (f) {
6726             f.reset();
6727         });
6728        
6729         
6730         return this;
6731     },
6732     getItems : function()
6733     {
6734         var r=new Roo.util.MixedCollection(false, function(o){
6735             return o.id || (o.id = Roo.id());
6736         });
6737         var iter = function(el) {
6738             if (el.inputEl) {
6739                 r.add(el);
6740             }
6741             if (!el.items) {
6742                 return;
6743             }
6744             Roo.each(el.items,function(e) {
6745                 iter(e);
6746             });
6747             
6748             
6749         };
6750         iter(this);
6751         return r;
6752         
6753         
6754         
6755         
6756     }
6757     
6758 });
6759
6760  
6761 /*
6762  * Based on:
6763  * Ext JS Library 1.1.1
6764  * Copyright(c) 2006-2007, Ext JS, LLC.
6765  *
6766  * Originally Released Under LGPL - original licence link has changed is not relivant.
6767  *
6768  * Fork - LGPL
6769  * <script type="text/javascript">
6770  */
6771 /**
6772  * @class Roo.form.VTypes
6773  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6774  * @singleton
6775  */
6776 Roo.form.VTypes = function(){
6777     // closure these in so they are only created once.
6778     var alpha = /^[a-zA-Z_]+$/;
6779     var alphanum = /^[a-zA-Z0-9_]+$/;
6780     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6781     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6782
6783     // All these messages and functions are configurable
6784     return {
6785         /**
6786          * The function used to validate email addresses
6787          * @param {String} value The email address
6788          */
6789         'email' : function(v){
6790             return email.test(v);
6791         },
6792         /**
6793          * The error text to display when the email validation function returns false
6794          * @type String
6795          */
6796         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6797         /**
6798          * The keystroke filter mask to be applied on email input
6799          * @type RegExp
6800          */
6801         'emailMask' : /[a-z0-9_\.\-@]/i,
6802
6803         /**
6804          * The function used to validate URLs
6805          * @param {String} value The URL
6806          */
6807         'url' : function(v){
6808             return url.test(v);
6809         },
6810         /**
6811          * The error text to display when the url validation function returns false
6812          * @type String
6813          */
6814         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6815         
6816         /**
6817          * The function used to validate alpha values
6818          * @param {String} value The value
6819          */
6820         'alpha' : function(v){
6821             return alpha.test(v);
6822         },
6823         /**
6824          * The error text to display when the alpha validation function returns false
6825          * @type String
6826          */
6827         'alphaText' : 'This field should only contain letters and _',
6828         /**
6829          * The keystroke filter mask to be applied on alpha input
6830          * @type RegExp
6831          */
6832         'alphaMask' : /[a-z_]/i,
6833
6834         /**
6835          * The function used to validate alphanumeric values
6836          * @param {String} value The value
6837          */
6838         'alphanum' : function(v){
6839             return alphanum.test(v);
6840         },
6841         /**
6842          * The error text to display when the alphanumeric validation function returns false
6843          * @type String
6844          */
6845         'alphanumText' : 'This field should only contain letters, numbers and _',
6846         /**
6847          * The keystroke filter mask to be applied on alphanumeric input
6848          * @type RegExp
6849          */
6850         'alphanumMask' : /[a-z0-9_]/i
6851     };
6852 }();/*
6853  * - LGPL
6854  *
6855  * Input
6856  * 
6857  */
6858
6859 /**
6860  * @class Roo.bootstrap.Input
6861  * @extends Roo.bootstrap.Component
6862  * Bootstrap Input class
6863  * @cfg {Boolean} disabled is it disabled
6864  * @cfg {String} fieldLabel - the label associated
6865  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6866  * @cfg {String} name name of the input
6867  * @cfg {string} fieldLabel - the label associated
6868  * @cfg {string}  inputType - input / file submit ...
6869  * @cfg {string} placeholder - placeholder to put in text.
6870  * @cfg {string}  before - input group add on before
6871  * @cfg {string} after - input group add on after
6872  * @cfg {string} size - (lg|sm) or leave empty..
6873  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6874  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6875  * @cfg {Number} md colspan out of 12 for computer-sized screens
6876  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6877  * @cfg {string} value default value of the input
6878  * @cfg {Number} labelWidth set the width of label (0-12)
6879  * @cfg {String} labelAlign (top|left)
6880  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6881  * @cfg {String} align (left|center|right) Default left
6882  * 
6883  * 
6884  * @constructor
6885  * Create a new Input
6886  * @param {Object} config The config object
6887  */
6888
6889 Roo.bootstrap.Input = function(config){
6890     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6891    
6892         this.addEvents({
6893             /**
6894              * @event focus
6895              * Fires when this field receives input focus.
6896              * @param {Roo.form.Field} this
6897              */
6898             focus : true,
6899             /**
6900              * @event blur
6901              * Fires when this field loses input focus.
6902              * @param {Roo.form.Field} this
6903              */
6904             blur : true,
6905             /**
6906              * @event specialkey
6907              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6908              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6909              * @param {Roo.form.Field} this
6910              * @param {Roo.EventObject} e The event object
6911              */
6912             specialkey : true,
6913             /**
6914              * @event change
6915              * Fires just before the field blurs if the field value has changed.
6916              * @param {Roo.form.Field} this
6917              * @param {Mixed} newValue The new value
6918              * @param {Mixed} oldValue The original value
6919              */
6920             change : true,
6921             /**
6922              * @event invalid
6923              * Fires after the field has been marked as invalid.
6924              * @param {Roo.form.Field} this
6925              * @param {String} msg The validation message
6926              */
6927             invalid : true,
6928             /**
6929              * @event valid
6930              * Fires after the field has been validated with no errors.
6931              * @param {Roo.form.Field} this
6932              */
6933             valid : true,
6934              /**
6935              * @event keyup
6936              * Fires after the key up
6937              * @param {Roo.form.Field} this
6938              * @param {Roo.EventObject}  e The event Object
6939              */
6940             keyup : true
6941         });
6942 };
6943
6944 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6945      /**
6946      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6947       automatic validation (defaults to "keyup").
6948      */
6949     validationEvent : "keyup",
6950      /**
6951      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6952      */
6953     validateOnBlur : true,
6954     /**
6955      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6956      */
6957     validationDelay : 250,
6958      /**
6959      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6960      */
6961     focusClass : "x-form-focus",  // not needed???
6962     
6963        
6964     /**
6965      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6966      */
6967     invalidClass : "has-error",
6968     
6969     /**
6970      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6971      */
6972     selectOnFocus : false,
6973     
6974      /**
6975      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6976      */
6977     maskRe : null,
6978        /**
6979      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6980      */
6981     vtype : null,
6982     
6983       /**
6984      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6985      */
6986     disableKeyFilter : false,
6987     
6988        /**
6989      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6990      */
6991     disabled : false,
6992      /**
6993      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6994      */
6995     allowBlank : true,
6996     /**
6997      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6998      */
6999     blankText : "This field is required",
7000     
7001      /**
7002      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7003      */
7004     minLength : 0,
7005     /**
7006      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7007      */
7008     maxLength : Number.MAX_VALUE,
7009     /**
7010      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7011      */
7012     minLengthText : "The minimum length for this field is {0}",
7013     /**
7014      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7015      */
7016     maxLengthText : "The maximum length for this field is {0}",
7017   
7018     
7019     /**
7020      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7021      * If available, this function will be called only after the basic validators all return true, and will be passed the
7022      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7023      */
7024     validator : null,
7025     /**
7026      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7027      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7028      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7029      */
7030     regex : null,
7031     /**
7032      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7033      */
7034     regexText : "",
7035     
7036     
7037     
7038     fieldLabel : '',
7039     inputType : 'text',
7040     
7041     name : false,
7042     placeholder: false,
7043     before : false,
7044     after : false,
7045     size : false,
7046     // private
7047     hasFocus : false,
7048     preventMark: false,
7049     isFormField : true,
7050     value : '',
7051     labelWidth : 2,
7052     labelAlign : false,
7053     readOnly : false,
7054     align : false,
7055     formatedValue : false,
7056     
7057     parentLabelAlign : function()
7058     {
7059         var parent = this;
7060         while (parent.parent()) {
7061             parent = parent.parent();
7062             if (typeof(parent.labelAlign) !='undefined') {
7063                 return parent.labelAlign;
7064             }
7065         }
7066         return 'left';
7067         
7068     },
7069     
7070     getAutoCreate : function(){
7071         
7072         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7073         
7074         var id = Roo.id();
7075         
7076         var cfg = {};
7077         
7078         if(this.inputType != 'hidden'){
7079             cfg.cls = 'form-group' //input-group
7080         }
7081         
7082         var input =  {
7083             tag: 'input',
7084             id : id,
7085             type : this.inputType,
7086             value : this.value,
7087             cls : 'form-control',
7088             placeholder : this.placeholder || ''
7089             
7090         };
7091         
7092         if(this.align){
7093             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7094         }
7095         
7096         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7097             input.maxLength = this.maxLength;
7098         }
7099         
7100         if (this.disabled) {
7101             input.disabled=true;
7102         }
7103         
7104         if (this.readOnly) {
7105             input.readonly=true;
7106         }
7107         
7108         if (this.name) {
7109             input.name = this.name;
7110         }
7111         if (this.size) {
7112             input.cls += ' input-' + this.size;
7113         }
7114         var settings=this;
7115         ['xs','sm','md','lg'].map(function(size){
7116             if (settings[size]) {
7117                 cfg.cls += ' col-' + size + '-' + settings[size];
7118             }
7119         });
7120         
7121         var inputblock = input;
7122         
7123         if (this.before || this.after) {
7124             
7125             inputblock = {
7126                 cls : 'input-group',
7127                 cn :  [] 
7128             };
7129             if (this.before && typeof(this.before) == 'string') {
7130                 
7131                 inputblock.cn.push({
7132                     tag :'span',
7133                     cls : 'roo-input-before input-group-addon',
7134                     html : this.before
7135                 });
7136             }
7137             if (this.before && typeof(this.before) == 'object') {
7138                 this.before = Roo.factory(this.before);
7139                 Roo.log(this.before);
7140                 inputblock.cn.push({
7141                     tag :'span',
7142                     cls : 'roo-input-before input-group-' +
7143                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7144                 });
7145             }
7146             
7147             inputblock.cn.push(input);
7148             
7149             if (this.after && typeof(this.after) == 'string') {
7150                 inputblock.cn.push({
7151                     tag :'span',
7152                     cls : 'roo-input-after input-group-addon',
7153                     html : this.after
7154                 });
7155             }
7156             if (this.after && typeof(this.after) == 'object') {
7157                 this.after = Roo.factory(this.after);
7158                 Roo.log(this.after);
7159                 inputblock.cn.push({
7160                     tag :'span',
7161                     cls : 'roo-input-after input-group-' +
7162                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7163                 });
7164             }
7165         };
7166         
7167         if (align ==='left' && this.fieldLabel.length) {
7168                 Roo.log("left and has label");
7169                 cfg.cn = [
7170                     
7171                     {
7172                         tag: 'label',
7173                         'for' :  id,
7174                         cls : 'control-label col-sm-' + this.labelWidth,
7175                         html : this.fieldLabel
7176                         
7177                     },
7178                     {
7179                         cls : "col-sm-" + (12 - this.labelWidth), 
7180                         cn: [
7181                             inputblock
7182                         ]
7183                     }
7184                     
7185                 ];
7186         } else if ( this.fieldLabel.length) {
7187                 Roo.log(" label");
7188                  cfg.cn = [
7189                    
7190                     {
7191                         tag: 'label',
7192                         //cls : 'input-group-addon',
7193                         html : this.fieldLabel
7194                         
7195                     },
7196                     
7197                     inputblock
7198                     
7199                 ];
7200
7201         } else {
7202             
7203                 Roo.log(" no label && no align");
7204                 cfg.cn = [
7205                     
7206                         inputblock
7207                     
7208                 ];
7209                 
7210                 
7211         };
7212         Roo.log('input-parentType: ' + this.parentType);
7213         
7214         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7215            cfg.cls += ' navbar-form';
7216            Roo.log(cfg);
7217         }
7218         
7219         return cfg;
7220         
7221     },
7222     /**
7223      * return the real input element.
7224      */
7225     inputEl: function ()
7226     {
7227         return this.el.select('input.form-control',true).first();
7228     },
7229     
7230     tooltipEl : function()
7231     {
7232         return this.inputEl();
7233     },
7234     
7235     setDisabled : function(v)
7236     {
7237         var i  = this.inputEl().dom;
7238         if (!v) {
7239             i.removeAttribute('disabled');
7240             return;
7241             
7242         }
7243         i.setAttribute('disabled','true');
7244     },
7245     initEvents : function()
7246     {
7247           
7248         this.inputEl().on("keydown" , this.fireKey,  this);
7249         this.inputEl().on("focus", this.onFocus,  this);
7250         this.inputEl().on("blur", this.onBlur,  this);
7251         
7252         this.inputEl().relayEvent('keyup', this);
7253
7254         // reference to original value for reset
7255         this.originalValue = this.getValue();
7256         //Roo.form.TextField.superclass.initEvents.call(this);
7257         if(this.validationEvent == 'keyup'){
7258             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7259             this.inputEl().on('keyup', this.filterValidation, this);
7260         }
7261         else if(this.validationEvent !== false){
7262             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7263         }
7264         
7265         if(this.selectOnFocus){
7266             this.on("focus", this.preFocus, this);
7267             
7268         }
7269         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7270             this.inputEl().on("keypress", this.filterKeys, this);
7271         }
7272        /* if(this.grow){
7273             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7274             this.el.on("click", this.autoSize,  this);
7275         }
7276         */
7277         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7278             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7279         }
7280         
7281         if (typeof(this.before) == 'object') {
7282             this.before.render(this.el.select('.roo-input-before',true).first());
7283         }
7284         if (typeof(this.after) == 'object') {
7285             this.after.render(this.el.select('.roo-input-after',true).first());
7286         }
7287         
7288         
7289     },
7290     filterValidation : function(e){
7291         if(!e.isNavKeyPress()){
7292             this.validationTask.delay(this.validationDelay);
7293         }
7294     },
7295      /**
7296      * Validates the field value
7297      * @return {Boolean} True if the value is valid, else false
7298      */
7299     validate : function(){
7300         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7301         if(this.disabled || this.validateValue(this.getRawValue())){
7302             this.clearInvalid();
7303             return true;
7304         }
7305         return false;
7306     },
7307     
7308     
7309     /**
7310      * Validates a value according to the field's validation rules and marks the field as invalid
7311      * if the validation fails
7312      * @param {Mixed} value The value to validate
7313      * @return {Boolean} True if the value is valid, else false
7314      */
7315     validateValue : function(value){
7316         if(value.length < 1)  { // if it's blank
7317              if(this.allowBlank){
7318                 this.clearInvalid();
7319                 return true;
7320              }else{
7321                 this.markInvalid(this.blankText);
7322                 return false;
7323              }
7324         }
7325         if(value.length < this.minLength){
7326             this.markInvalid(String.format(this.minLengthText, this.minLength));
7327             return false;
7328         }
7329         if(value.length > this.maxLength){
7330             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7331             return false;
7332         }
7333         if(this.vtype){
7334             var vt = Roo.form.VTypes;
7335             if(!vt[this.vtype](value, this)){
7336                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7337                 return false;
7338             }
7339         }
7340         if(typeof this.validator == "function"){
7341             var msg = this.validator(value);
7342             if(msg !== true){
7343                 this.markInvalid(msg);
7344                 return false;
7345             }
7346         }
7347         if(this.regex && !this.regex.test(value)){
7348             this.markInvalid(this.regexText);
7349             return false;
7350         }
7351         return true;
7352     },
7353
7354     
7355     
7356      // private
7357     fireKey : function(e){
7358         //Roo.log('field ' + e.getKey());
7359         if(e.isNavKeyPress()){
7360             this.fireEvent("specialkey", this, e);
7361         }
7362     },
7363     focus : function (selectText){
7364         if(this.rendered){
7365             this.inputEl().focus();
7366             if(selectText === true){
7367                 this.inputEl().dom.select();
7368             }
7369         }
7370         return this;
7371     } ,
7372     
7373     onFocus : function(){
7374         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7375            // this.el.addClass(this.focusClass);
7376         }
7377         if(!this.hasFocus){
7378             this.hasFocus = true;
7379             this.startValue = this.getValue();
7380             this.fireEvent("focus", this);
7381         }
7382     },
7383     
7384     beforeBlur : Roo.emptyFn,
7385
7386     
7387     // private
7388     onBlur : function(){
7389         this.beforeBlur();
7390         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7391             //this.el.removeClass(this.focusClass);
7392         }
7393         this.hasFocus = false;
7394         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7395             this.validate();
7396         }
7397         var v = this.getValue();
7398         if(String(v) !== String(this.startValue)){
7399             this.fireEvent('change', this, v, this.startValue);
7400         }
7401         this.fireEvent("blur", this);
7402     },
7403     
7404     /**
7405      * Resets the current field value to the originally loaded value and clears any validation messages
7406      */
7407     reset : function(){
7408         this.setValue(this.originalValue);
7409         this.clearInvalid();
7410     },
7411      /**
7412      * Returns the name of the field
7413      * @return {Mixed} name The name field
7414      */
7415     getName: function(){
7416         return this.name;
7417     },
7418      /**
7419      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7420      * @return {Mixed} value The field value
7421      */
7422     getValue : function(){
7423         
7424         var v = this.inputEl().getValue();
7425         
7426         return v;
7427     },
7428     /**
7429      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7430      * @return {Mixed} value The field value
7431      */
7432     getRawValue : function(){
7433         var v = this.inputEl().getValue();
7434         
7435         return v;
7436     },
7437     
7438     /**
7439      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7440      * @param {Mixed} value The value to set
7441      */
7442     setRawValue : function(v){
7443         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7444     },
7445     
7446     selectText : function(start, end){
7447         var v = this.getRawValue();
7448         if(v.length > 0){
7449             start = start === undefined ? 0 : start;
7450             end = end === undefined ? v.length : end;
7451             var d = this.inputEl().dom;
7452             if(d.setSelectionRange){
7453                 d.setSelectionRange(start, end);
7454             }else if(d.createTextRange){
7455                 var range = d.createTextRange();
7456                 range.moveStart("character", start);
7457                 range.moveEnd("character", v.length-end);
7458                 range.select();
7459             }
7460         }
7461     },
7462     
7463     /**
7464      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7465      * @param {Mixed} value The value to set
7466      */
7467     setValue : function(v){
7468         this.value = v;
7469         if(this.rendered){
7470             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7471             this.validate();
7472         }
7473     },
7474     
7475     /*
7476     processValue : function(value){
7477         if(this.stripCharsRe){
7478             var newValue = value.replace(this.stripCharsRe, '');
7479             if(newValue !== value){
7480                 this.setRawValue(newValue);
7481                 return newValue;
7482             }
7483         }
7484         return value;
7485     },
7486   */
7487     preFocus : function(){
7488         
7489         if(this.selectOnFocus){
7490             this.inputEl().dom.select();
7491         }
7492     },
7493     filterKeys : function(e){
7494         var k = e.getKey();
7495         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7496             return;
7497         }
7498         var c = e.getCharCode(), cc = String.fromCharCode(c);
7499         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7500             return;
7501         }
7502         if(!this.maskRe.test(cc)){
7503             e.stopEvent();
7504         }
7505     },
7506      /**
7507      * Clear any invalid styles/messages for this field
7508      */
7509     clearInvalid : function(){
7510         
7511         if(!this.el || this.preventMark){ // not rendered
7512             return;
7513         }
7514         this.el.removeClass(this.invalidClass);
7515         /*
7516         switch(this.msgTarget){
7517             case 'qtip':
7518                 this.el.dom.qtip = '';
7519                 break;
7520             case 'title':
7521                 this.el.dom.title = '';
7522                 break;
7523             case 'under':
7524                 if(this.errorEl){
7525                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7526                 }
7527                 break;
7528             case 'side':
7529                 if(this.errorIcon){
7530                     this.errorIcon.dom.qtip = '';
7531                     this.errorIcon.hide();
7532                     this.un('resize', this.alignErrorIcon, this);
7533                 }
7534                 break;
7535             default:
7536                 var t = Roo.getDom(this.msgTarget);
7537                 t.innerHTML = '';
7538                 t.style.display = 'none';
7539                 break;
7540         }
7541         */
7542         this.fireEvent('valid', this);
7543     },
7544      /**
7545      * Mark this field as invalid
7546      * @param {String} msg The validation message
7547      */
7548     markInvalid : function(msg){
7549         if(!this.el  || this.preventMark){ // not rendered
7550             return;
7551         }
7552         this.el.addClass(this.invalidClass);
7553         /*
7554         msg = msg || this.invalidText;
7555         switch(this.msgTarget){
7556             case 'qtip':
7557                 this.el.dom.qtip = msg;
7558                 this.el.dom.qclass = 'x-form-invalid-tip';
7559                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7560                     Roo.QuickTips.enable();
7561                 }
7562                 break;
7563             case 'title':
7564                 this.el.dom.title = msg;
7565                 break;
7566             case 'under':
7567                 if(!this.errorEl){
7568                     var elp = this.el.findParent('.x-form-element', 5, true);
7569                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7570                     this.errorEl.setWidth(elp.getWidth(true)-20);
7571                 }
7572                 this.errorEl.update(msg);
7573                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7574                 break;
7575             case 'side':
7576                 if(!this.errorIcon){
7577                     var elp = this.el.findParent('.x-form-element', 5, true);
7578                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7579                 }
7580                 this.alignErrorIcon();
7581                 this.errorIcon.dom.qtip = msg;
7582                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7583                 this.errorIcon.show();
7584                 this.on('resize', this.alignErrorIcon, this);
7585                 break;
7586             default:
7587                 var t = Roo.getDom(this.msgTarget);
7588                 t.innerHTML = msg;
7589                 t.style.display = this.msgDisplay;
7590                 break;
7591         }
7592         */
7593         this.fireEvent('invalid', this, msg);
7594     },
7595     // private
7596     SafariOnKeyDown : function(event)
7597     {
7598         // this is a workaround for a password hang bug on chrome/ webkit.
7599         
7600         var isSelectAll = false;
7601         
7602         if(this.inputEl().dom.selectionEnd > 0){
7603             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7604         }
7605         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7606             event.preventDefault();
7607             this.setValue('');
7608             return;
7609         }
7610         
7611         if(isSelectAll){ // backspace and delete key
7612             
7613             event.preventDefault();
7614             // this is very hacky as keydown always get's upper case.
7615             //
7616             var cc = String.fromCharCode(event.getCharCode());
7617             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7618             
7619         }
7620     },
7621     adjustWidth : function(tag, w){
7622         tag = tag.toLowerCase();
7623         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7624             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7625                 if(tag == 'input'){
7626                     return w + 2;
7627                 }
7628                 if(tag == 'textarea'){
7629                     return w-2;
7630                 }
7631             }else if(Roo.isOpera){
7632                 if(tag == 'input'){
7633                     return w + 2;
7634                 }
7635                 if(tag == 'textarea'){
7636                     return w-2;
7637                 }
7638             }
7639         }
7640         return w;
7641     }
7642     
7643 });
7644
7645  
7646 /*
7647  * - LGPL
7648  *
7649  * Input
7650  * 
7651  */
7652
7653 /**
7654  * @class Roo.bootstrap.TextArea
7655  * @extends Roo.bootstrap.Input
7656  * Bootstrap TextArea class
7657  * @cfg {Number} cols Specifies the visible width of a text area
7658  * @cfg {Number} rows Specifies the visible number of lines in a text area
7659  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7660  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7661  * @cfg {string} html text
7662  * 
7663  * @constructor
7664  * Create a new TextArea
7665  * @param {Object} config The config object
7666  */
7667
7668 Roo.bootstrap.TextArea = function(config){
7669     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7670    
7671 };
7672
7673 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7674      
7675     cols : false,
7676     rows : 5,
7677     readOnly : false,
7678     warp : 'soft',
7679     resize : false,
7680     value: false,
7681     html: false,
7682     
7683     getAutoCreate : function(){
7684         
7685         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7686         
7687         var id = Roo.id();
7688         
7689         var cfg = {};
7690         
7691         var input =  {
7692             tag: 'textarea',
7693             id : id,
7694             warp : this.warp,
7695             rows : this.rows,
7696             value : this.value || '',
7697             html: this.html || '',
7698             cls : 'form-control',
7699             placeholder : this.placeholder || '' 
7700             
7701         };
7702         
7703         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7704             input.maxLength = this.maxLength;
7705         }
7706         
7707         if(this.resize){
7708             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7709         }
7710         
7711         if(this.cols){
7712             input.cols = this.cols;
7713         }
7714         
7715         if (this.readOnly) {
7716             input.readonly = true;
7717         }
7718         
7719         if (this.name) {
7720             input.name = this.name;
7721         }
7722         
7723         if (this.size) {
7724             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7725         }
7726         
7727         var settings=this;
7728         ['xs','sm','md','lg'].map(function(size){
7729             if (settings[size]) {
7730                 cfg.cls += ' col-' + size + '-' + settings[size];
7731             }
7732         });
7733         
7734         var inputblock = input;
7735         
7736         if (this.before || this.after) {
7737             
7738             inputblock = {
7739                 cls : 'input-group',
7740                 cn :  [] 
7741             };
7742             if (this.before) {
7743                 inputblock.cn.push({
7744                     tag :'span',
7745                     cls : 'input-group-addon',
7746                     html : this.before
7747                 });
7748             }
7749             inputblock.cn.push(input);
7750             if (this.after) {
7751                 inputblock.cn.push({
7752                     tag :'span',
7753                     cls : 'input-group-addon',
7754                     html : this.after
7755                 });
7756             }
7757             
7758         }
7759         
7760         if (align ==='left' && this.fieldLabel.length) {
7761                 Roo.log("left and has label");
7762                 cfg.cn = [
7763                     
7764                     {
7765                         tag: 'label',
7766                         'for' :  id,
7767                         cls : 'control-label col-sm-' + this.labelWidth,
7768                         html : this.fieldLabel
7769                         
7770                     },
7771                     {
7772                         cls : "col-sm-" + (12 - this.labelWidth), 
7773                         cn: [
7774                             inputblock
7775                         ]
7776                     }
7777                     
7778                 ];
7779         } else if ( this.fieldLabel.length) {
7780                 Roo.log(" label");
7781                  cfg.cn = [
7782                    
7783                     {
7784                         tag: 'label',
7785                         //cls : 'input-group-addon',
7786                         html : this.fieldLabel
7787                         
7788                     },
7789                     
7790                     inputblock
7791                     
7792                 ];
7793
7794         } else {
7795             
7796                    Roo.log(" no label && no align");
7797                 cfg.cn = [
7798                     
7799                         inputblock
7800                     
7801                 ];
7802                 
7803                 
7804         }
7805         
7806         if (this.disabled) {
7807             input.disabled=true;
7808         }
7809         
7810         return cfg;
7811         
7812     },
7813     /**
7814      * return the real textarea element.
7815      */
7816     inputEl: function ()
7817     {
7818         return this.el.select('textarea.form-control',true).first();
7819     }
7820 });
7821
7822  
7823 /*
7824  * - LGPL
7825  *
7826  * trigger field - base class for combo..
7827  * 
7828  */
7829  
7830 /**
7831  * @class Roo.bootstrap.TriggerField
7832  * @extends Roo.bootstrap.Input
7833  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7834  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7835  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7836  * for which you can provide a custom implementation.  For example:
7837  * <pre><code>
7838 var trigger = new Roo.bootstrap.TriggerField();
7839 trigger.onTriggerClick = myTriggerFn;
7840 trigger.applyTo('my-field');
7841 </code></pre>
7842  *
7843  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7844  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7845  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7846  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7847  * @constructor
7848  * Create a new TriggerField.
7849  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7850  * to the base TextField)
7851  */
7852 Roo.bootstrap.TriggerField = function(config){
7853     this.mimicing = false;
7854     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7855 };
7856
7857 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7858     /**
7859      * @cfg {String} triggerClass A CSS class to apply to the trigger
7860      */
7861      /**
7862      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7863      */
7864     hideTrigger:false,
7865
7866     /** @cfg {Boolean} grow @hide */
7867     /** @cfg {Number} growMin @hide */
7868     /** @cfg {Number} growMax @hide */
7869
7870     /**
7871      * @hide 
7872      * @method
7873      */
7874     autoSize: Roo.emptyFn,
7875     // private
7876     monitorTab : true,
7877     // private
7878     deferHeight : true,
7879
7880     
7881     actionMode : 'wrap',
7882     
7883     
7884     
7885     getAutoCreate : function(){
7886        
7887         var align = this.labelAlign || this.parentLabelAlign();
7888         
7889         var id = Roo.id();
7890         
7891         var cfg = {
7892             cls: 'form-group' //input-group
7893         };
7894         
7895         
7896         var input =  {
7897             tag: 'input',
7898             id : id,
7899             type : this.inputType,
7900             cls : 'form-control',
7901             autocomplete: 'off',
7902             placeholder : this.placeholder || '' 
7903             
7904         };
7905         if (this.name) {
7906             input.name = this.name;
7907         }
7908         if (this.size) {
7909             input.cls += ' input-' + this.size;
7910         }
7911         
7912         if (this.disabled) {
7913             input.disabled=true;
7914         }
7915         
7916         var inputblock = input;
7917         
7918         if (this.before || this.after) {
7919             
7920             inputblock = {
7921                 cls : 'input-group',
7922                 cn :  [] 
7923             };
7924             if (this.before) {
7925                 inputblock.cn.push({
7926                     tag :'span',
7927                     cls : 'input-group-addon',
7928                     html : this.before
7929                 });
7930             }
7931             inputblock.cn.push(input);
7932             if (this.after) {
7933                 inputblock.cn.push({
7934                     tag :'span',
7935                     cls : 'input-group-addon',
7936                     html : this.after
7937                 });
7938             }
7939             
7940         };
7941         
7942         var box = {
7943             tag: 'div',
7944             cn: [
7945                 {
7946                     tag: 'input',
7947                     type : 'hidden',
7948                     cls: 'form-hidden-field'
7949                 },
7950                 inputblock
7951             ]
7952             
7953         };
7954         
7955         if(this.multiple){
7956             Roo.log('multiple');
7957             
7958             box = {
7959                 tag: 'div',
7960                 cn: [
7961                     {
7962                         tag: 'input',
7963                         type : 'hidden',
7964                         cls: 'form-hidden-field'
7965                     },
7966                     {
7967                         tag: 'ul',
7968                         cls: 'select2-choices',
7969                         cn:[
7970                             {
7971                                 tag: 'li',
7972                                 cls: 'select2-search-field',
7973                                 cn: [
7974
7975                                     inputblock
7976                                 ]
7977                             }
7978                         ]
7979                     }
7980                 ]
7981             }
7982         };
7983         
7984         var combobox = {
7985             cls: 'select2-container input-group',
7986             cn: [
7987                 box
7988 //                {
7989 //                    tag: 'ul',
7990 //                    cls: 'typeahead typeahead-long dropdown-menu',
7991 //                    style: 'display:none'
7992 //                }
7993             ]
7994         };
7995         
7996         if(!this.multiple && this.showToggleBtn){
7997             combobox.cn.push({
7998                 tag :'span',
7999                 cls : 'input-group-addon btn dropdown-toggle',
8000                 cn : [
8001                     {
8002                         tag: 'span',
8003                         cls: 'caret'
8004                     },
8005                     {
8006                         tag: 'span',
8007                         cls: 'combobox-clear',
8008                         cn  : [
8009                             {
8010                                 tag : 'i',
8011                                 cls: 'icon-remove'
8012                             }
8013                         ]
8014                     }
8015                 ]
8016
8017             })
8018         }
8019         
8020         if(this.multiple){
8021             combobox.cls += ' select2-container-multi';
8022         }
8023         
8024         if (align ==='left' && this.fieldLabel.length) {
8025             
8026                 Roo.log("left and has label");
8027                 cfg.cn = [
8028                     
8029                     {
8030                         tag: 'label',
8031                         'for' :  id,
8032                         cls : 'control-label col-sm-' + this.labelWidth,
8033                         html : this.fieldLabel
8034                         
8035                     },
8036                     {
8037                         cls : "col-sm-" + (12 - this.labelWidth), 
8038                         cn: [
8039                             combobox
8040                         ]
8041                     }
8042                     
8043                 ];
8044         } else if ( this.fieldLabel.length) {
8045                 Roo.log(" label");
8046                  cfg.cn = [
8047                    
8048                     {
8049                         tag: 'label',
8050                         //cls : 'input-group-addon',
8051                         html : this.fieldLabel
8052                         
8053                     },
8054                     
8055                     combobox
8056                     
8057                 ];
8058
8059         } else {
8060             
8061                 Roo.log(" no label && no align");
8062                 cfg = combobox
8063                      
8064                 
8065         }
8066          
8067         var settings=this;
8068         ['xs','sm','md','lg'].map(function(size){
8069             if (settings[size]) {
8070                 cfg.cls += ' col-' + size + '-' + settings[size];
8071             }
8072         });
8073         
8074         return cfg;
8075         
8076     },
8077     
8078     
8079     
8080     // private
8081     onResize : function(w, h){
8082 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8083 //        if(typeof w == 'number'){
8084 //            var x = w - this.trigger.getWidth();
8085 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8086 //            this.trigger.setStyle('left', x+'px');
8087 //        }
8088     },
8089
8090     // private
8091     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8092
8093     // private
8094     getResizeEl : function(){
8095         return this.inputEl();
8096     },
8097
8098     // private
8099     getPositionEl : function(){
8100         return this.inputEl();
8101     },
8102
8103     // private
8104     alignErrorIcon : function(){
8105         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8106     },
8107
8108     // private
8109     initEvents : function(){
8110         
8111         this.createList();
8112         
8113         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8114         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8115         if(!this.multiple && this.showToggleBtn){
8116             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8117             if(this.hideTrigger){
8118                 this.trigger.setDisplayed(false);
8119             }
8120             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8121         }
8122         
8123         if(this.multiple){
8124             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8125         }
8126         
8127         //this.trigger.addClassOnOver('x-form-trigger-over');
8128         //this.trigger.addClassOnClick('x-form-trigger-click');
8129         
8130         //if(!this.width){
8131         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8132         //}
8133     },
8134     
8135     createList : function()
8136     {
8137         this.list = Roo.get(document.body).createChild({
8138             tag: 'ul',
8139             cls: 'typeahead typeahead-long dropdown-menu',
8140             style: 'display:none'
8141         });
8142         
8143         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8144         
8145     },
8146
8147     // private
8148     initTrigger : function(){
8149        
8150     },
8151
8152     // private
8153     onDestroy : function(){
8154         if(this.trigger){
8155             this.trigger.removeAllListeners();
8156           //  this.trigger.remove();
8157         }
8158         //if(this.wrap){
8159         //    this.wrap.remove();
8160         //}
8161         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8162     },
8163
8164     // private
8165     onFocus : function(){
8166         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8167         /*
8168         if(!this.mimicing){
8169             this.wrap.addClass('x-trigger-wrap-focus');
8170             this.mimicing = true;
8171             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8172             if(this.monitorTab){
8173                 this.el.on("keydown", this.checkTab, this);
8174             }
8175         }
8176         */
8177     },
8178
8179     // private
8180     checkTab : function(e){
8181         if(e.getKey() == e.TAB){
8182             this.triggerBlur();
8183         }
8184     },
8185
8186     // private
8187     onBlur : function(){
8188         // do nothing
8189     },
8190
8191     // private
8192     mimicBlur : function(e, t){
8193         /*
8194         if(!this.wrap.contains(t) && this.validateBlur()){
8195             this.triggerBlur();
8196         }
8197         */
8198     },
8199
8200     // private
8201     triggerBlur : function(){
8202         this.mimicing = false;
8203         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8204         if(this.monitorTab){
8205             this.el.un("keydown", this.checkTab, this);
8206         }
8207         //this.wrap.removeClass('x-trigger-wrap-focus');
8208         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8209     },
8210
8211     // private
8212     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8213     validateBlur : function(e, t){
8214         return true;
8215     },
8216
8217     // private
8218     onDisable : function(){
8219         this.inputEl().dom.disabled = true;
8220         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8221         //if(this.wrap){
8222         //    this.wrap.addClass('x-item-disabled');
8223         //}
8224     },
8225
8226     // private
8227     onEnable : function(){
8228         this.inputEl().dom.disabled = false;
8229         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8230         //if(this.wrap){
8231         //    this.el.removeClass('x-item-disabled');
8232         //}
8233     },
8234
8235     // private
8236     onShow : function(){
8237         var ae = this.getActionEl();
8238         
8239         if(ae){
8240             ae.dom.style.display = '';
8241             ae.dom.style.visibility = 'visible';
8242         }
8243     },
8244
8245     // private
8246     
8247     onHide : function(){
8248         var ae = this.getActionEl();
8249         ae.dom.style.display = 'none';
8250     },
8251
8252     /**
8253      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8254      * by an implementing function.
8255      * @method
8256      * @param {EventObject} e
8257      */
8258     onTriggerClick : Roo.emptyFn
8259 });
8260  /*
8261  * Based on:
8262  * Ext JS Library 1.1.1
8263  * Copyright(c) 2006-2007, Ext JS, LLC.
8264  *
8265  * Originally Released Under LGPL - original licence link has changed is not relivant.
8266  *
8267  * Fork - LGPL
8268  * <script type="text/javascript">
8269  */
8270
8271
8272 /**
8273  * @class Roo.data.SortTypes
8274  * @singleton
8275  * Defines the default sorting (casting?) comparison functions used when sorting data.
8276  */
8277 Roo.data.SortTypes = {
8278     /**
8279      * Default sort that does nothing
8280      * @param {Mixed} s The value being converted
8281      * @return {Mixed} The comparison value
8282      */
8283     none : function(s){
8284         return s;
8285     },
8286     
8287     /**
8288      * The regular expression used to strip tags
8289      * @type {RegExp}
8290      * @property
8291      */
8292     stripTagsRE : /<\/?[^>]+>/gi,
8293     
8294     /**
8295      * Strips all HTML tags to sort on text only
8296      * @param {Mixed} s The value being converted
8297      * @return {String} The comparison value
8298      */
8299     asText : function(s){
8300         return String(s).replace(this.stripTagsRE, "");
8301     },
8302     
8303     /**
8304      * Strips all HTML tags to sort on text only - Case insensitive
8305      * @param {Mixed} s The value being converted
8306      * @return {String} The comparison value
8307      */
8308     asUCText : function(s){
8309         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8310     },
8311     
8312     /**
8313      * Case insensitive string
8314      * @param {Mixed} s The value being converted
8315      * @return {String} The comparison value
8316      */
8317     asUCString : function(s) {
8318         return String(s).toUpperCase();
8319     },
8320     
8321     /**
8322      * Date sorting
8323      * @param {Mixed} s The value being converted
8324      * @return {Number} The comparison value
8325      */
8326     asDate : function(s) {
8327         if(!s){
8328             return 0;
8329         }
8330         if(s instanceof Date){
8331             return s.getTime();
8332         }
8333         return Date.parse(String(s));
8334     },
8335     
8336     /**
8337      * Float sorting
8338      * @param {Mixed} s The value being converted
8339      * @return {Float} The comparison value
8340      */
8341     asFloat : function(s) {
8342         var val = parseFloat(String(s).replace(/,/g, ""));
8343         if(isNaN(val)) val = 0;
8344         return val;
8345     },
8346     
8347     /**
8348      * Integer sorting
8349      * @param {Mixed} s The value being converted
8350      * @return {Number} The comparison value
8351      */
8352     asInt : function(s) {
8353         var val = parseInt(String(s).replace(/,/g, ""));
8354         if(isNaN(val)) val = 0;
8355         return val;
8356     }
8357 };/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367
8368 /**
8369 * @class Roo.data.Record
8370  * Instances of this class encapsulate both record <em>definition</em> information, and record
8371  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8372  * to access Records cached in an {@link Roo.data.Store} object.<br>
8373  * <p>
8374  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8375  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8376  * objects.<br>
8377  * <p>
8378  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8379  * @constructor
8380  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8381  * {@link #create}. The parameters are the same.
8382  * @param {Array} data An associative Array of data values keyed by the field name.
8383  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8384  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8385  * not specified an integer id is generated.
8386  */
8387 Roo.data.Record = function(data, id){
8388     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8389     this.data = data;
8390 };
8391
8392 /**
8393  * Generate a constructor for a specific record layout.
8394  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8395  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8396  * Each field definition object may contain the following properties: <ul>
8397  * <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,
8398  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8399  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8400  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8401  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8402  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8403  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8404  * this may be omitted.</p></li>
8405  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8406  * <ul><li>auto (Default, implies no conversion)</li>
8407  * <li>string</li>
8408  * <li>int</li>
8409  * <li>float</li>
8410  * <li>boolean</li>
8411  * <li>date</li></ul></p></li>
8412  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8413  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8414  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8415  * by the Reader into an object that will be stored in the Record. It is passed the
8416  * following parameters:<ul>
8417  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8418  * </ul></p></li>
8419  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8420  * </ul>
8421  * <br>usage:<br><pre><code>
8422 var TopicRecord = Roo.data.Record.create(
8423     {name: 'title', mapping: 'topic_title'},
8424     {name: 'author', mapping: 'username'},
8425     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8426     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8427     {name: 'lastPoster', mapping: 'user2'},
8428     {name: 'excerpt', mapping: 'post_text'}
8429 );
8430
8431 var myNewRecord = new TopicRecord({
8432     title: 'Do my job please',
8433     author: 'noobie',
8434     totalPosts: 1,
8435     lastPost: new Date(),
8436     lastPoster: 'Animal',
8437     excerpt: 'No way dude!'
8438 });
8439 myStore.add(myNewRecord);
8440 </code></pre>
8441  * @method create
8442  * @static
8443  */
8444 Roo.data.Record.create = function(o){
8445     var f = function(){
8446         f.superclass.constructor.apply(this, arguments);
8447     };
8448     Roo.extend(f, Roo.data.Record);
8449     var p = f.prototype;
8450     p.fields = new Roo.util.MixedCollection(false, function(field){
8451         return field.name;
8452     });
8453     for(var i = 0, len = o.length; i < len; i++){
8454         p.fields.add(new Roo.data.Field(o[i]));
8455     }
8456     f.getField = function(name){
8457         return p.fields.get(name);  
8458     };
8459     return f;
8460 };
8461
8462 Roo.data.Record.AUTO_ID = 1000;
8463 Roo.data.Record.EDIT = 'edit';
8464 Roo.data.Record.REJECT = 'reject';
8465 Roo.data.Record.COMMIT = 'commit';
8466
8467 Roo.data.Record.prototype = {
8468     /**
8469      * Readonly flag - true if this record has been modified.
8470      * @type Boolean
8471      */
8472     dirty : false,
8473     editing : false,
8474     error: null,
8475     modified: null,
8476
8477     // private
8478     join : function(store){
8479         this.store = store;
8480     },
8481
8482     /**
8483      * Set the named field to the specified value.
8484      * @param {String} name The name of the field to set.
8485      * @param {Object} value The value to set the field to.
8486      */
8487     set : function(name, value){
8488         if(this.data[name] == value){
8489             return;
8490         }
8491         this.dirty = true;
8492         if(!this.modified){
8493             this.modified = {};
8494         }
8495         if(typeof this.modified[name] == 'undefined'){
8496             this.modified[name] = this.data[name];
8497         }
8498         this.data[name] = value;
8499         if(!this.editing && this.store){
8500             this.store.afterEdit(this);
8501         }       
8502     },
8503
8504     /**
8505      * Get the value of the named field.
8506      * @param {String} name The name of the field to get the value of.
8507      * @return {Object} The value of the field.
8508      */
8509     get : function(name){
8510         return this.data[name]; 
8511     },
8512
8513     // private
8514     beginEdit : function(){
8515         this.editing = true;
8516         this.modified = {}; 
8517     },
8518
8519     // private
8520     cancelEdit : function(){
8521         this.editing = false;
8522         delete this.modified;
8523     },
8524
8525     // private
8526     endEdit : function(){
8527         this.editing = false;
8528         if(this.dirty && this.store){
8529             this.store.afterEdit(this);
8530         }
8531     },
8532
8533     /**
8534      * Usually called by the {@link Roo.data.Store} which owns the Record.
8535      * Rejects all changes made to the Record since either creation, or the last commit operation.
8536      * Modified fields are reverted to their original values.
8537      * <p>
8538      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8539      * of reject operations.
8540      */
8541     reject : function(){
8542         var m = this.modified;
8543         for(var n in m){
8544             if(typeof m[n] != "function"){
8545                 this.data[n] = m[n];
8546             }
8547         }
8548         this.dirty = false;
8549         delete this.modified;
8550         this.editing = false;
8551         if(this.store){
8552             this.store.afterReject(this);
8553         }
8554     },
8555
8556     /**
8557      * Usually called by the {@link Roo.data.Store} which owns the Record.
8558      * Commits all changes made to the Record since either creation, or the last commit operation.
8559      * <p>
8560      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8561      * of commit operations.
8562      */
8563     commit : function(){
8564         this.dirty = false;
8565         delete this.modified;
8566         this.editing = false;
8567         if(this.store){
8568             this.store.afterCommit(this);
8569         }
8570     },
8571
8572     // private
8573     hasError : function(){
8574         return this.error != null;
8575     },
8576
8577     // private
8578     clearError : function(){
8579         this.error = null;
8580     },
8581
8582     /**
8583      * Creates a copy of this record.
8584      * @param {String} id (optional) A new record id if you don't want to use this record's id
8585      * @return {Record}
8586      */
8587     copy : function(newId) {
8588         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8589     }
8590 };/*
8591  * Based on:
8592  * Ext JS Library 1.1.1
8593  * Copyright(c) 2006-2007, Ext JS, LLC.
8594  *
8595  * Originally Released Under LGPL - original licence link has changed is not relivant.
8596  *
8597  * Fork - LGPL
8598  * <script type="text/javascript">
8599  */
8600
8601
8602
8603 /**
8604  * @class Roo.data.Store
8605  * @extends Roo.util.Observable
8606  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8607  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8608  * <p>
8609  * 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
8610  * has no knowledge of the format of the data returned by the Proxy.<br>
8611  * <p>
8612  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8613  * instances from the data object. These records are cached and made available through accessor functions.
8614  * @constructor
8615  * Creates a new Store.
8616  * @param {Object} config A config object containing the objects needed for the Store to access data,
8617  * and read the data into Records.
8618  */
8619 Roo.data.Store = function(config){
8620     this.data = new Roo.util.MixedCollection(false);
8621     this.data.getKey = function(o){
8622         return o.id;
8623     };
8624     this.baseParams = {};
8625     // private
8626     this.paramNames = {
8627         "start" : "start",
8628         "limit" : "limit",
8629         "sort" : "sort",
8630         "dir" : "dir",
8631         "multisort" : "_multisort"
8632     };
8633
8634     if(config && config.data){
8635         this.inlineData = config.data;
8636         delete config.data;
8637     }
8638
8639     Roo.apply(this, config);
8640     
8641     if(this.reader){ // reader passed
8642         this.reader = Roo.factory(this.reader, Roo.data);
8643         this.reader.xmodule = this.xmodule || false;
8644         if(!this.recordType){
8645             this.recordType = this.reader.recordType;
8646         }
8647         if(this.reader.onMetaChange){
8648             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8649         }
8650     }
8651
8652     if(this.recordType){
8653         this.fields = this.recordType.prototype.fields;
8654     }
8655     this.modified = [];
8656
8657     this.addEvents({
8658         /**
8659          * @event datachanged
8660          * Fires when the data cache has changed, and a widget which is using this Store
8661          * as a Record cache should refresh its view.
8662          * @param {Store} this
8663          */
8664         datachanged : true,
8665         /**
8666          * @event metachange
8667          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8668          * @param {Store} this
8669          * @param {Object} meta The JSON metadata
8670          */
8671         metachange : true,
8672         /**
8673          * @event add
8674          * Fires when Records have been added to the Store
8675          * @param {Store} this
8676          * @param {Roo.data.Record[]} records The array of Records added
8677          * @param {Number} index The index at which the record(s) were added
8678          */
8679         add : true,
8680         /**
8681          * @event remove
8682          * Fires when a Record has been removed from the Store
8683          * @param {Store} this
8684          * @param {Roo.data.Record} record The Record that was removed
8685          * @param {Number} index The index at which the record was removed
8686          */
8687         remove : true,
8688         /**
8689          * @event update
8690          * Fires when a Record has been updated
8691          * @param {Store} this
8692          * @param {Roo.data.Record} record The Record that was updated
8693          * @param {String} operation The update operation being performed.  Value may be one of:
8694          * <pre><code>
8695  Roo.data.Record.EDIT
8696  Roo.data.Record.REJECT
8697  Roo.data.Record.COMMIT
8698          * </code></pre>
8699          */
8700         update : true,
8701         /**
8702          * @event clear
8703          * Fires when the data cache has been cleared.
8704          * @param {Store} this
8705          */
8706         clear : true,
8707         /**
8708          * @event beforeload
8709          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8710          * the load action will be canceled.
8711          * @param {Store} this
8712          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8713          */
8714         beforeload : true,
8715         /**
8716          * @event beforeloadadd
8717          * Fires after a new set of Records has been loaded.
8718          * @param {Store} this
8719          * @param {Roo.data.Record[]} records The Records that were loaded
8720          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8721          */
8722         beforeloadadd : true,
8723         /**
8724          * @event load
8725          * Fires after a new set of Records has been loaded, before they are added to the store.
8726          * @param {Store} this
8727          * @param {Roo.data.Record[]} records The Records that were loaded
8728          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8729          * @params {Object} return from reader
8730          */
8731         load : true,
8732         /**
8733          * @event loadexception
8734          * Fires if an exception occurs in the Proxy during loading.
8735          * Called with the signature of the Proxy's "loadexception" event.
8736          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8737          * 
8738          * @param {Proxy} 
8739          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8740          * @param {Object} load options 
8741          * @param {Object} jsonData from your request (normally this contains the Exception)
8742          */
8743         loadexception : true
8744     });
8745     
8746     if(this.proxy){
8747         this.proxy = Roo.factory(this.proxy, Roo.data);
8748         this.proxy.xmodule = this.xmodule || false;
8749         this.relayEvents(this.proxy,  ["loadexception"]);
8750     }
8751     this.sortToggle = {};
8752     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8753
8754     Roo.data.Store.superclass.constructor.call(this);
8755
8756     if(this.inlineData){
8757         this.loadData(this.inlineData);
8758         delete this.inlineData;
8759     }
8760 };
8761
8762 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8763      /**
8764     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8765     * without a remote query - used by combo/forms at present.
8766     */
8767     
8768     /**
8769     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8770     */
8771     /**
8772     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8773     */
8774     /**
8775     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8776     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8777     */
8778     /**
8779     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8780     * on any HTTP request
8781     */
8782     /**
8783     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8784     */
8785     /**
8786     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8787     */
8788     multiSort: false,
8789     /**
8790     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8791     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8792     */
8793     remoteSort : false,
8794
8795     /**
8796     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8797      * loaded or when a record is removed. (defaults to false).
8798     */
8799     pruneModifiedRecords : false,
8800
8801     // private
8802     lastOptions : null,
8803
8804     /**
8805      * Add Records to the Store and fires the add event.
8806      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8807      */
8808     add : function(records){
8809         records = [].concat(records);
8810         for(var i = 0, len = records.length; i < len; i++){
8811             records[i].join(this);
8812         }
8813         var index = this.data.length;
8814         this.data.addAll(records);
8815         this.fireEvent("add", this, records, index);
8816     },
8817
8818     /**
8819      * Remove a Record from the Store and fires the remove event.
8820      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8821      */
8822     remove : function(record){
8823         var index = this.data.indexOf(record);
8824         this.data.removeAt(index);
8825         if(this.pruneModifiedRecords){
8826             this.modified.remove(record);
8827         }
8828         this.fireEvent("remove", this, record, index);
8829     },
8830
8831     /**
8832      * Remove all Records from the Store and fires the clear event.
8833      */
8834     removeAll : function(){
8835         this.data.clear();
8836         if(this.pruneModifiedRecords){
8837             this.modified = [];
8838         }
8839         this.fireEvent("clear", this);
8840     },
8841
8842     /**
8843      * Inserts Records to the Store at the given index and fires the add event.
8844      * @param {Number} index The start index at which to insert the passed Records.
8845      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8846      */
8847     insert : function(index, records){
8848         records = [].concat(records);
8849         for(var i = 0, len = records.length; i < len; i++){
8850             this.data.insert(index, records[i]);
8851             records[i].join(this);
8852         }
8853         this.fireEvent("add", this, records, index);
8854     },
8855
8856     /**
8857      * Get the index within the cache of the passed Record.
8858      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8859      * @return {Number} The index of the passed Record. Returns -1 if not found.
8860      */
8861     indexOf : function(record){
8862         return this.data.indexOf(record);
8863     },
8864
8865     /**
8866      * Get the index within the cache of the Record with the passed id.
8867      * @param {String} id The id of the Record to find.
8868      * @return {Number} The index of the Record. Returns -1 if not found.
8869      */
8870     indexOfId : function(id){
8871         return this.data.indexOfKey(id);
8872     },
8873
8874     /**
8875      * Get the Record with the specified id.
8876      * @param {String} id The id of the Record to find.
8877      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8878      */
8879     getById : function(id){
8880         return this.data.key(id);
8881     },
8882
8883     /**
8884      * Get the Record at the specified index.
8885      * @param {Number} index The index of the Record to find.
8886      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8887      */
8888     getAt : function(index){
8889         return this.data.itemAt(index);
8890     },
8891
8892     /**
8893      * Returns a range of Records between specified indices.
8894      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8895      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8896      * @return {Roo.data.Record[]} An array of Records
8897      */
8898     getRange : function(start, end){
8899         return this.data.getRange(start, end);
8900     },
8901
8902     // private
8903     storeOptions : function(o){
8904         o = Roo.apply({}, o);
8905         delete o.callback;
8906         delete o.scope;
8907         this.lastOptions = o;
8908     },
8909
8910     /**
8911      * Loads the Record cache from the configured Proxy using the configured Reader.
8912      * <p>
8913      * If using remote paging, then the first load call must specify the <em>start</em>
8914      * and <em>limit</em> properties in the options.params property to establish the initial
8915      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8916      * <p>
8917      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8918      * and this call will return before the new data has been loaded. Perform any post-processing
8919      * in a callback function, or in a "load" event handler.</strong>
8920      * <p>
8921      * @param {Object} options An object containing properties which control loading options:<ul>
8922      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8923      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8924      * passed the following arguments:<ul>
8925      * <li>r : Roo.data.Record[]</li>
8926      * <li>options: Options object from the load call</li>
8927      * <li>success: Boolean success indicator</li></ul></li>
8928      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8929      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8930      * </ul>
8931      */
8932     load : function(options){
8933         options = options || {};
8934         if(this.fireEvent("beforeload", this, options) !== false){
8935             this.storeOptions(options);
8936             var p = Roo.apply(options.params || {}, this.baseParams);
8937             // if meta was not loaded from remote source.. try requesting it.
8938             if (!this.reader.metaFromRemote) {
8939                 p._requestMeta = 1;
8940             }
8941             if(this.sortInfo && this.remoteSort){
8942                 var pn = this.paramNames;
8943                 p[pn["sort"]] = this.sortInfo.field;
8944                 p[pn["dir"]] = this.sortInfo.direction;
8945             }
8946             if (this.multiSort) {
8947                 var pn = this.paramNames;
8948                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8949             }
8950             
8951             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8952         }
8953     },
8954
8955     /**
8956      * Reloads the Record cache from the configured Proxy using the configured Reader and
8957      * the options from the last load operation performed.
8958      * @param {Object} options (optional) An object containing properties which may override the options
8959      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8960      * the most recently used options are reused).
8961      */
8962     reload : function(options){
8963         this.load(Roo.applyIf(options||{}, this.lastOptions));
8964     },
8965
8966     // private
8967     // Called as a callback by the Reader during a load operation.
8968     loadRecords : function(o, options, success){
8969         if(!o || success === false){
8970             if(success !== false){
8971                 this.fireEvent("load", this, [], options, o);
8972             }
8973             if(options.callback){
8974                 options.callback.call(options.scope || this, [], options, false);
8975             }
8976             return;
8977         }
8978         // if data returned failure - throw an exception.
8979         if (o.success === false) {
8980             // show a message if no listener is registered.
8981             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8982                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8983             }
8984             // loadmask wil be hooked into this..
8985             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8986             return;
8987         }
8988         var r = o.records, t = o.totalRecords || r.length;
8989         
8990         this.fireEvent("beforeloadadd", this, r, options, o);
8991         
8992         if(!options || options.add !== true){
8993             if(this.pruneModifiedRecords){
8994                 this.modified = [];
8995             }
8996             for(var i = 0, len = r.length; i < len; i++){
8997                 r[i].join(this);
8998             }
8999             if(this.snapshot){
9000                 this.data = this.snapshot;
9001                 delete this.snapshot;
9002             }
9003             this.data.clear();
9004             this.data.addAll(r);
9005             this.totalLength = t;
9006             this.applySort();
9007             this.fireEvent("datachanged", this);
9008         }else{
9009             this.totalLength = Math.max(t, this.data.length+r.length);
9010             this.add(r);
9011         }
9012         this.fireEvent("load", this, r, options, o);
9013         if(options.callback){
9014             options.callback.call(options.scope || this, r, options, true);
9015         }
9016     },
9017
9018
9019     /**
9020      * Loads data from a passed data block. A Reader which understands the format of the data
9021      * must have been configured in the constructor.
9022      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9023      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9024      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9025      */
9026     loadData : function(o, append){
9027         var r = this.reader.readRecords(o);
9028         this.loadRecords(r, {add: append}, true);
9029     },
9030
9031     /**
9032      * Gets the number of cached records.
9033      * <p>
9034      * <em>If using paging, this may not be the total size of the dataset. If the data object
9035      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9036      * the data set size</em>
9037      */
9038     getCount : function(){
9039         return this.data.length || 0;
9040     },
9041
9042     /**
9043      * Gets the total number of records in the dataset as returned by the server.
9044      * <p>
9045      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9046      * the dataset size</em>
9047      */
9048     getTotalCount : function(){
9049         return this.totalLength || 0;
9050     },
9051
9052     /**
9053      * Returns the sort state of the Store as an object with two properties:
9054      * <pre><code>
9055  field {String} The name of the field by which the Records are sorted
9056  direction {String} The sort order, "ASC" or "DESC"
9057      * </code></pre>
9058      */
9059     getSortState : function(){
9060         return this.sortInfo;
9061     },
9062
9063     // private
9064     applySort : function(){
9065         if(this.sortInfo && !this.remoteSort){
9066             var s = this.sortInfo, f = s.field;
9067             var st = this.fields.get(f).sortType;
9068             var fn = function(r1, r2){
9069                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9070                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9071             };
9072             this.data.sort(s.direction, fn);
9073             if(this.snapshot && this.snapshot != this.data){
9074                 this.snapshot.sort(s.direction, fn);
9075             }
9076         }
9077     },
9078
9079     /**
9080      * Sets the default sort column and order to be used by the next load operation.
9081      * @param {String} fieldName The name of the field to sort by.
9082      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9083      */
9084     setDefaultSort : function(field, dir){
9085         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9086     },
9087
9088     /**
9089      * Sort the Records.
9090      * If remote sorting is used, the sort is performed on the server, and the cache is
9091      * reloaded. If local sorting is used, the cache is sorted internally.
9092      * @param {String} fieldName The name of the field to sort by.
9093      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9094      */
9095     sort : function(fieldName, dir){
9096         var f = this.fields.get(fieldName);
9097         if(!dir){
9098             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9099             
9100             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9101                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9102             }else{
9103                 dir = f.sortDir;
9104             }
9105         }
9106         this.sortToggle[f.name] = dir;
9107         this.sortInfo = {field: f.name, direction: dir};
9108         if(!this.remoteSort){
9109             this.applySort();
9110             this.fireEvent("datachanged", this);
9111         }else{
9112             this.load(this.lastOptions);
9113         }
9114     },
9115
9116     /**
9117      * Calls the specified function for each of the Records in the cache.
9118      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9119      * Returning <em>false</em> aborts and exits the iteration.
9120      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9121      */
9122     each : function(fn, scope){
9123         this.data.each(fn, scope);
9124     },
9125
9126     /**
9127      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9128      * (e.g., during paging).
9129      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9130      */
9131     getModifiedRecords : function(){
9132         return this.modified;
9133     },
9134
9135     // private
9136     createFilterFn : function(property, value, anyMatch){
9137         if(!value.exec){ // not a regex
9138             value = String(value);
9139             if(value.length == 0){
9140                 return false;
9141             }
9142             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9143         }
9144         return function(r){
9145             return value.test(r.data[property]);
9146         };
9147     },
9148
9149     /**
9150      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9151      * @param {String} property A field on your records
9152      * @param {Number} start The record index to start at (defaults to 0)
9153      * @param {Number} end The last record index to include (defaults to length - 1)
9154      * @return {Number} The sum
9155      */
9156     sum : function(property, start, end){
9157         var rs = this.data.items, v = 0;
9158         start = start || 0;
9159         end = (end || end === 0) ? end : rs.length-1;
9160
9161         for(var i = start; i <= end; i++){
9162             v += (rs[i].data[property] || 0);
9163         }
9164         return v;
9165     },
9166
9167     /**
9168      * Filter the records by a specified property.
9169      * @param {String} field A field on your records
9170      * @param {String/RegExp} value Either a string that the field
9171      * should start with or a RegExp to test against the field
9172      * @param {Boolean} anyMatch True to match any part not just the beginning
9173      */
9174     filter : function(property, value, anyMatch){
9175         var fn = this.createFilterFn(property, value, anyMatch);
9176         return fn ? this.filterBy(fn) : this.clearFilter();
9177     },
9178
9179     /**
9180      * Filter by a function. The specified function will be called with each
9181      * record in this data source. If the function returns true the record is included,
9182      * otherwise it is filtered.
9183      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9184      * @param {Object} scope (optional) The scope of the function (defaults to this)
9185      */
9186     filterBy : function(fn, scope){
9187         this.snapshot = this.snapshot || this.data;
9188         this.data = this.queryBy(fn, scope||this);
9189         this.fireEvent("datachanged", this);
9190     },
9191
9192     /**
9193      * Query the records by a specified property.
9194      * @param {String} field A field on your records
9195      * @param {String/RegExp} value Either a string that the field
9196      * should start with or a RegExp to test against the field
9197      * @param {Boolean} anyMatch True to match any part not just the beginning
9198      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9199      */
9200     query : function(property, value, anyMatch){
9201         var fn = this.createFilterFn(property, value, anyMatch);
9202         return fn ? this.queryBy(fn) : this.data.clone();
9203     },
9204
9205     /**
9206      * Query by a function. The specified function will be called with each
9207      * record in this data source. If the function returns true the record is included
9208      * in the results.
9209      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9210      * @param {Object} scope (optional) The scope of the function (defaults to this)
9211       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9212      **/
9213     queryBy : function(fn, scope){
9214         var data = this.snapshot || this.data;
9215         return data.filterBy(fn, scope||this);
9216     },
9217
9218     /**
9219      * Collects unique values for a particular dataIndex from this store.
9220      * @param {String} dataIndex The property to collect
9221      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9222      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9223      * @return {Array} An array of the unique values
9224      **/
9225     collect : function(dataIndex, allowNull, bypassFilter){
9226         var d = (bypassFilter === true && this.snapshot) ?
9227                 this.snapshot.items : this.data.items;
9228         var v, sv, r = [], l = {};
9229         for(var i = 0, len = d.length; i < len; i++){
9230             v = d[i].data[dataIndex];
9231             sv = String(v);
9232             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9233                 l[sv] = true;
9234                 r[r.length] = v;
9235             }
9236         }
9237         return r;
9238     },
9239
9240     /**
9241      * Revert to a view of the Record cache with no filtering applied.
9242      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9243      */
9244     clearFilter : function(suppressEvent){
9245         if(this.snapshot && this.snapshot != this.data){
9246             this.data = this.snapshot;
9247             delete this.snapshot;
9248             if(suppressEvent !== true){
9249                 this.fireEvent("datachanged", this);
9250             }
9251         }
9252     },
9253
9254     // private
9255     afterEdit : function(record){
9256         if(this.modified.indexOf(record) == -1){
9257             this.modified.push(record);
9258         }
9259         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9260     },
9261     
9262     // private
9263     afterReject : function(record){
9264         this.modified.remove(record);
9265         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9266     },
9267
9268     // private
9269     afterCommit : function(record){
9270         this.modified.remove(record);
9271         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9272     },
9273
9274     /**
9275      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9276      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9277      */
9278     commitChanges : function(){
9279         var m = this.modified.slice(0);
9280         this.modified = [];
9281         for(var i = 0, len = m.length; i < len; i++){
9282             m[i].commit();
9283         }
9284     },
9285
9286     /**
9287      * Cancel outstanding changes on all changed records.
9288      */
9289     rejectChanges : function(){
9290         var m = this.modified.slice(0);
9291         this.modified = [];
9292         for(var i = 0, len = m.length; i < len; i++){
9293             m[i].reject();
9294         }
9295     },
9296
9297     onMetaChange : function(meta, rtype, o){
9298         this.recordType = rtype;
9299         this.fields = rtype.prototype.fields;
9300         delete this.snapshot;
9301         this.sortInfo = meta.sortInfo || this.sortInfo;
9302         this.modified = [];
9303         this.fireEvent('metachange', this, this.reader.meta);
9304     },
9305     
9306     moveIndex : function(data, type)
9307     {
9308         var index = this.indexOf(data);
9309         
9310         var newIndex = index + type;
9311         
9312         this.remove(data);
9313         
9314         this.insert(newIndex, data);
9315         
9316     }
9317 });/*
9318  * Based on:
9319  * Ext JS Library 1.1.1
9320  * Copyright(c) 2006-2007, Ext JS, LLC.
9321  *
9322  * Originally Released Under LGPL - original licence link has changed is not relivant.
9323  *
9324  * Fork - LGPL
9325  * <script type="text/javascript">
9326  */
9327
9328 /**
9329  * @class Roo.data.SimpleStore
9330  * @extends Roo.data.Store
9331  * Small helper class to make creating Stores from Array data easier.
9332  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9333  * @cfg {Array} fields An array of field definition objects, or field name strings.
9334  * @cfg {Array} data The multi-dimensional array of data
9335  * @constructor
9336  * @param {Object} config
9337  */
9338 Roo.data.SimpleStore = function(config){
9339     Roo.data.SimpleStore.superclass.constructor.call(this, {
9340         isLocal : true,
9341         reader: new Roo.data.ArrayReader({
9342                 id: config.id
9343             },
9344             Roo.data.Record.create(config.fields)
9345         ),
9346         proxy : new Roo.data.MemoryProxy(config.data)
9347     });
9348     this.load();
9349 };
9350 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9351  * Based on:
9352  * Ext JS Library 1.1.1
9353  * Copyright(c) 2006-2007, Ext JS, LLC.
9354  *
9355  * Originally Released Under LGPL - original licence link has changed is not relivant.
9356  *
9357  * Fork - LGPL
9358  * <script type="text/javascript">
9359  */
9360
9361 /**
9362 /**
9363  * @extends Roo.data.Store
9364  * @class Roo.data.JsonStore
9365  * Small helper class to make creating Stores for JSON data easier. <br/>
9366 <pre><code>
9367 var store = new Roo.data.JsonStore({
9368     url: 'get-images.php',
9369     root: 'images',
9370     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9371 });
9372 </code></pre>
9373  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9374  * JsonReader and HttpProxy (unless inline data is provided).</b>
9375  * @cfg {Array} fields An array of field definition objects, or field name strings.
9376  * @constructor
9377  * @param {Object} config
9378  */
9379 Roo.data.JsonStore = function(c){
9380     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9381         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9382         reader: new Roo.data.JsonReader(c, c.fields)
9383     }));
9384 };
9385 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9386  * Based on:
9387  * Ext JS Library 1.1.1
9388  * Copyright(c) 2006-2007, Ext JS, LLC.
9389  *
9390  * Originally Released Under LGPL - original licence link has changed is not relivant.
9391  *
9392  * Fork - LGPL
9393  * <script type="text/javascript">
9394  */
9395
9396  
9397 Roo.data.Field = function(config){
9398     if(typeof config == "string"){
9399         config = {name: config};
9400     }
9401     Roo.apply(this, config);
9402     
9403     if(!this.type){
9404         this.type = "auto";
9405     }
9406     
9407     var st = Roo.data.SortTypes;
9408     // named sortTypes are supported, here we look them up
9409     if(typeof this.sortType == "string"){
9410         this.sortType = st[this.sortType];
9411     }
9412     
9413     // set default sortType for strings and dates
9414     if(!this.sortType){
9415         switch(this.type){
9416             case "string":
9417                 this.sortType = st.asUCString;
9418                 break;
9419             case "date":
9420                 this.sortType = st.asDate;
9421                 break;
9422             default:
9423                 this.sortType = st.none;
9424         }
9425     }
9426
9427     // define once
9428     var stripRe = /[\$,%]/g;
9429
9430     // prebuilt conversion function for this field, instead of
9431     // switching every time we're reading a value
9432     if(!this.convert){
9433         var cv, dateFormat = this.dateFormat;
9434         switch(this.type){
9435             case "":
9436             case "auto":
9437             case undefined:
9438                 cv = function(v){ return v; };
9439                 break;
9440             case "string":
9441                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9442                 break;
9443             case "int":
9444                 cv = function(v){
9445                     return v !== undefined && v !== null && v !== '' ?
9446                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9447                     };
9448                 break;
9449             case "float":
9450                 cv = function(v){
9451                     return v !== undefined && v !== null && v !== '' ?
9452                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9453                     };
9454                 break;
9455             case "bool":
9456             case "boolean":
9457                 cv = function(v){ return v === true || v === "true" || v == 1; };
9458                 break;
9459             case "date":
9460                 cv = function(v){
9461                     if(!v){
9462                         return '';
9463                     }
9464                     if(v instanceof Date){
9465                         return v;
9466                     }
9467                     if(dateFormat){
9468                         if(dateFormat == "timestamp"){
9469                             return new Date(v*1000);
9470                         }
9471                         return Date.parseDate(v, dateFormat);
9472                     }
9473                     var parsed = Date.parse(v);
9474                     return parsed ? new Date(parsed) : null;
9475                 };
9476              break;
9477             
9478         }
9479         this.convert = cv;
9480     }
9481 };
9482
9483 Roo.data.Field.prototype = {
9484     dateFormat: null,
9485     defaultValue: "",
9486     mapping: null,
9487     sortType : null,
9488     sortDir : "ASC"
9489 };/*
9490  * Based on:
9491  * Ext JS Library 1.1.1
9492  * Copyright(c) 2006-2007, Ext JS, LLC.
9493  *
9494  * Originally Released Under LGPL - original licence link has changed is not relivant.
9495  *
9496  * Fork - LGPL
9497  * <script type="text/javascript">
9498  */
9499  
9500 // Base class for reading structured data from a data source.  This class is intended to be
9501 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9502
9503 /**
9504  * @class Roo.data.DataReader
9505  * Base class for reading structured data from a data source.  This class is intended to be
9506  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9507  */
9508
9509 Roo.data.DataReader = function(meta, recordType){
9510     
9511     this.meta = meta;
9512     
9513     this.recordType = recordType instanceof Array ? 
9514         Roo.data.Record.create(recordType) : recordType;
9515 };
9516
9517 Roo.data.DataReader.prototype = {
9518      /**
9519      * Create an empty record
9520      * @param {Object} data (optional) - overlay some values
9521      * @return {Roo.data.Record} record created.
9522      */
9523     newRow :  function(d) {
9524         var da =  {};
9525         this.recordType.prototype.fields.each(function(c) {
9526             switch( c.type) {
9527                 case 'int' : da[c.name] = 0; break;
9528                 case 'date' : da[c.name] = new Date(); break;
9529                 case 'float' : da[c.name] = 0.0; break;
9530                 case 'boolean' : da[c.name] = false; break;
9531                 default : da[c.name] = ""; break;
9532             }
9533             
9534         });
9535         return new this.recordType(Roo.apply(da, d));
9536     }
9537     
9538 };/*
9539  * Based on:
9540  * Ext JS Library 1.1.1
9541  * Copyright(c) 2006-2007, Ext JS, LLC.
9542  *
9543  * Originally Released Under LGPL - original licence link has changed is not relivant.
9544  *
9545  * Fork - LGPL
9546  * <script type="text/javascript">
9547  */
9548
9549 /**
9550  * @class Roo.data.DataProxy
9551  * @extends Roo.data.Observable
9552  * This class is an abstract base class for implementations which provide retrieval of
9553  * unformatted data objects.<br>
9554  * <p>
9555  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9556  * (of the appropriate type which knows how to parse the data object) to provide a block of
9557  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9558  * <p>
9559  * Custom implementations must implement the load method as described in
9560  * {@link Roo.data.HttpProxy#load}.
9561  */
9562 Roo.data.DataProxy = function(){
9563     this.addEvents({
9564         /**
9565          * @event beforeload
9566          * Fires before a network request is made to retrieve a data object.
9567          * @param {Object} This DataProxy object.
9568          * @param {Object} params The params parameter to the load function.
9569          */
9570         beforeload : true,
9571         /**
9572          * @event load
9573          * Fires before the load method's callback is called.
9574          * @param {Object} This DataProxy object.
9575          * @param {Object} o The data object.
9576          * @param {Object} arg The callback argument object passed to the load function.
9577          */
9578         load : true,
9579         /**
9580          * @event loadexception
9581          * Fires if an Exception occurs during data retrieval.
9582          * @param {Object} This DataProxy object.
9583          * @param {Object} o The data object.
9584          * @param {Object} arg The callback argument object passed to the load function.
9585          * @param {Object} e The Exception.
9586          */
9587         loadexception : true
9588     });
9589     Roo.data.DataProxy.superclass.constructor.call(this);
9590 };
9591
9592 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9593
9594     /**
9595      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9596      */
9597 /*
9598  * Based on:
9599  * Ext JS Library 1.1.1
9600  * Copyright(c) 2006-2007, Ext JS, LLC.
9601  *
9602  * Originally Released Under LGPL - original licence link has changed is not relivant.
9603  *
9604  * Fork - LGPL
9605  * <script type="text/javascript">
9606  */
9607 /**
9608  * @class Roo.data.MemoryProxy
9609  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9610  * to the Reader when its load method is called.
9611  * @constructor
9612  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9613  */
9614 Roo.data.MemoryProxy = function(data){
9615     if (data.data) {
9616         data = data.data;
9617     }
9618     Roo.data.MemoryProxy.superclass.constructor.call(this);
9619     this.data = data;
9620 };
9621
9622 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9623     /**
9624      * Load data from the requested source (in this case an in-memory
9625      * data object passed to the constructor), read the data object into
9626      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9627      * process that block using the passed callback.
9628      * @param {Object} params This parameter is not used by the MemoryProxy class.
9629      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9630      * object into a block of Roo.data.Records.
9631      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9632      * The function must be passed <ul>
9633      * <li>The Record block object</li>
9634      * <li>The "arg" argument from the load function</li>
9635      * <li>A boolean success indicator</li>
9636      * </ul>
9637      * @param {Object} scope The scope in which to call the callback
9638      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9639      */
9640     load : function(params, reader, callback, scope, arg){
9641         params = params || {};
9642         var result;
9643         try {
9644             result = reader.readRecords(this.data);
9645         }catch(e){
9646             this.fireEvent("loadexception", this, arg, null, e);
9647             callback.call(scope, null, arg, false);
9648             return;
9649         }
9650         callback.call(scope, result, arg, true);
9651     },
9652     
9653     // private
9654     update : function(params, records){
9655         
9656     }
9657 });/*
9658  * Based on:
9659  * Ext JS Library 1.1.1
9660  * Copyright(c) 2006-2007, Ext JS, LLC.
9661  *
9662  * Originally Released Under LGPL - original licence link has changed is not relivant.
9663  *
9664  * Fork - LGPL
9665  * <script type="text/javascript">
9666  */
9667 /**
9668  * @class Roo.data.HttpProxy
9669  * @extends Roo.data.DataProxy
9670  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9671  * configured to reference a certain URL.<br><br>
9672  * <p>
9673  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9674  * from which the running page was served.<br><br>
9675  * <p>
9676  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9677  * <p>
9678  * Be aware that to enable the browser to parse an XML document, the server must set
9679  * the Content-Type header in the HTTP response to "text/xml".
9680  * @constructor
9681  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9682  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9683  * will be used to make the request.
9684  */
9685 Roo.data.HttpProxy = function(conn){
9686     Roo.data.HttpProxy.superclass.constructor.call(this);
9687     // is conn a conn config or a real conn?
9688     this.conn = conn;
9689     this.useAjax = !conn || !conn.events;
9690   
9691 };
9692
9693 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9694     // thse are take from connection...
9695     
9696     /**
9697      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9698      */
9699     /**
9700      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9701      * extra parameters to each request made by this object. (defaults to undefined)
9702      */
9703     /**
9704      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9705      *  to each request made by this object. (defaults to undefined)
9706      */
9707     /**
9708      * @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)
9709      */
9710     /**
9711      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9712      */
9713      /**
9714      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9715      * @type Boolean
9716      */
9717   
9718
9719     /**
9720      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9721      * @type Boolean
9722      */
9723     /**
9724      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9725      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9726      * a finer-grained basis than the DataProxy events.
9727      */
9728     getConnection : function(){
9729         return this.useAjax ? Roo.Ajax : this.conn;
9730     },
9731
9732     /**
9733      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9734      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9735      * process that block using the passed callback.
9736      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9737      * for the request to the remote server.
9738      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9739      * object into a block of Roo.data.Records.
9740      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9741      * The function must be passed <ul>
9742      * <li>The Record block object</li>
9743      * <li>The "arg" argument from the load function</li>
9744      * <li>A boolean success indicator</li>
9745      * </ul>
9746      * @param {Object} scope The scope in which to call the callback
9747      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9748      */
9749     load : function(params, reader, callback, scope, arg){
9750         if(this.fireEvent("beforeload", this, params) !== false){
9751             var  o = {
9752                 params : params || {},
9753                 request: {
9754                     callback : callback,
9755                     scope : scope,
9756                     arg : arg
9757                 },
9758                 reader: reader,
9759                 callback : this.loadResponse,
9760                 scope: this
9761             };
9762             if(this.useAjax){
9763                 Roo.applyIf(o, this.conn);
9764                 if(this.activeRequest){
9765                     Roo.Ajax.abort(this.activeRequest);
9766                 }
9767                 this.activeRequest = Roo.Ajax.request(o);
9768             }else{
9769                 this.conn.request(o);
9770             }
9771         }else{
9772             callback.call(scope||this, null, arg, false);
9773         }
9774     },
9775
9776     // private
9777     loadResponse : function(o, success, response){
9778         delete this.activeRequest;
9779         if(!success){
9780             this.fireEvent("loadexception", this, o, response);
9781             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9782             return;
9783         }
9784         var result;
9785         try {
9786             result = o.reader.read(response);
9787         }catch(e){
9788             this.fireEvent("loadexception", this, o, response, e);
9789             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9790             return;
9791         }
9792         
9793         this.fireEvent("load", this, o, o.request.arg);
9794         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9795     },
9796
9797     // private
9798     update : function(dataSet){
9799
9800     },
9801
9802     // private
9803     updateResponse : function(dataSet){
9804
9805     }
9806 });/*
9807  * Based on:
9808  * Ext JS Library 1.1.1
9809  * Copyright(c) 2006-2007, Ext JS, LLC.
9810  *
9811  * Originally Released Under LGPL - original licence link has changed is not relivant.
9812  *
9813  * Fork - LGPL
9814  * <script type="text/javascript">
9815  */
9816
9817 /**
9818  * @class Roo.data.ScriptTagProxy
9819  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9820  * other than the originating domain of the running page.<br><br>
9821  * <p>
9822  * <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
9823  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9824  * <p>
9825  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9826  * source code that is used as the source inside a &lt;script> tag.<br><br>
9827  * <p>
9828  * In order for the browser to process the returned data, the server must wrap the data object
9829  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9830  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9831  * depending on whether the callback name was passed:
9832  * <p>
9833  * <pre><code>
9834 boolean scriptTag = false;
9835 String cb = request.getParameter("callback");
9836 if (cb != null) {
9837     scriptTag = true;
9838     response.setContentType("text/javascript");
9839 } else {
9840     response.setContentType("application/x-json");
9841 }
9842 Writer out = response.getWriter();
9843 if (scriptTag) {
9844     out.write(cb + "(");
9845 }
9846 out.print(dataBlock.toJsonString());
9847 if (scriptTag) {
9848     out.write(");");
9849 }
9850 </pre></code>
9851  *
9852  * @constructor
9853  * @param {Object} config A configuration object.
9854  */
9855 Roo.data.ScriptTagProxy = function(config){
9856     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9857     Roo.apply(this, config);
9858     this.head = document.getElementsByTagName("head")[0];
9859 };
9860
9861 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9862
9863 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9864     /**
9865      * @cfg {String} url The URL from which to request the data object.
9866      */
9867     /**
9868      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9869      */
9870     timeout : 30000,
9871     /**
9872      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9873      * the server the name of the callback function set up by the load call to process the returned data object.
9874      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9875      * javascript output which calls this named function passing the data object as its only parameter.
9876      */
9877     callbackParam : "callback",
9878     /**
9879      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9880      * name to the request.
9881      */
9882     nocache : true,
9883
9884     /**
9885      * Load data from the configured URL, read the data object into
9886      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9887      * process that block using the passed callback.
9888      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9889      * for the request to the remote server.
9890      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9891      * object into a block of Roo.data.Records.
9892      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9893      * The function must be passed <ul>
9894      * <li>The Record block object</li>
9895      * <li>The "arg" argument from the load function</li>
9896      * <li>A boolean success indicator</li>
9897      * </ul>
9898      * @param {Object} scope The scope in which to call the callback
9899      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9900      */
9901     load : function(params, reader, callback, scope, arg){
9902         if(this.fireEvent("beforeload", this, params) !== false){
9903
9904             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9905
9906             var url = this.url;
9907             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9908             if(this.nocache){
9909                 url += "&_dc=" + (new Date().getTime());
9910             }
9911             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9912             var trans = {
9913                 id : transId,
9914                 cb : "stcCallback"+transId,
9915                 scriptId : "stcScript"+transId,
9916                 params : params,
9917                 arg : arg,
9918                 url : url,
9919                 callback : callback,
9920                 scope : scope,
9921                 reader : reader
9922             };
9923             var conn = this;
9924
9925             window[trans.cb] = function(o){
9926                 conn.handleResponse(o, trans);
9927             };
9928
9929             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9930
9931             if(this.autoAbort !== false){
9932                 this.abort();
9933             }
9934
9935             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9936
9937             var script = document.createElement("script");
9938             script.setAttribute("src", url);
9939             script.setAttribute("type", "text/javascript");
9940             script.setAttribute("id", trans.scriptId);
9941             this.head.appendChild(script);
9942
9943             this.trans = trans;
9944         }else{
9945             callback.call(scope||this, null, arg, false);
9946         }
9947     },
9948
9949     // private
9950     isLoading : function(){
9951         return this.trans ? true : false;
9952     },
9953
9954     /**
9955      * Abort the current server request.
9956      */
9957     abort : function(){
9958         if(this.isLoading()){
9959             this.destroyTrans(this.trans);
9960         }
9961     },
9962
9963     // private
9964     destroyTrans : function(trans, isLoaded){
9965         this.head.removeChild(document.getElementById(trans.scriptId));
9966         clearTimeout(trans.timeoutId);
9967         if(isLoaded){
9968             window[trans.cb] = undefined;
9969             try{
9970                 delete window[trans.cb];
9971             }catch(e){}
9972         }else{
9973             // if hasn't been loaded, wait for load to remove it to prevent script error
9974             window[trans.cb] = function(){
9975                 window[trans.cb] = undefined;
9976                 try{
9977                     delete window[trans.cb];
9978                 }catch(e){}
9979             };
9980         }
9981     },
9982
9983     // private
9984     handleResponse : function(o, trans){
9985         this.trans = false;
9986         this.destroyTrans(trans, true);
9987         var result;
9988         try {
9989             result = trans.reader.readRecords(o);
9990         }catch(e){
9991             this.fireEvent("loadexception", this, o, trans.arg, e);
9992             trans.callback.call(trans.scope||window, null, trans.arg, false);
9993             return;
9994         }
9995         this.fireEvent("load", this, o, trans.arg);
9996         trans.callback.call(trans.scope||window, result, trans.arg, true);
9997     },
9998
9999     // private
10000     handleFailure : function(trans){
10001         this.trans = false;
10002         this.destroyTrans(trans, false);
10003         this.fireEvent("loadexception", this, null, trans.arg);
10004         trans.callback.call(trans.scope||window, null, trans.arg, false);
10005     }
10006 });/*
10007  * Based on:
10008  * Ext JS Library 1.1.1
10009  * Copyright(c) 2006-2007, Ext JS, LLC.
10010  *
10011  * Originally Released Under LGPL - original licence link has changed is not relivant.
10012  *
10013  * Fork - LGPL
10014  * <script type="text/javascript">
10015  */
10016
10017 /**
10018  * @class Roo.data.JsonReader
10019  * @extends Roo.data.DataReader
10020  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10021  * based on mappings in a provided Roo.data.Record constructor.
10022  * 
10023  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10024  * in the reply previously. 
10025  * 
10026  * <p>
10027  * Example code:
10028  * <pre><code>
10029 var RecordDef = Roo.data.Record.create([
10030     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10031     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10032 ]);
10033 var myReader = new Roo.data.JsonReader({
10034     totalProperty: "results",    // The property which contains the total dataset size (optional)
10035     root: "rows",                // The property which contains an Array of row objects
10036     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10037 }, RecordDef);
10038 </code></pre>
10039  * <p>
10040  * This would consume a JSON file like this:
10041  * <pre><code>
10042 { 'results': 2, 'rows': [
10043     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10044     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10045 }
10046 </code></pre>
10047  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10048  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10049  * paged from the remote server.
10050  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10051  * @cfg {String} root name of the property which contains the Array of row objects.
10052  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10053  * @constructor
10054  * Create a new JsonReader
10055  * @param {Object} meta Metadata configuration options
10056  * @param {Object} recordType Either an Array of field definition objects,
10057  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10058  */
10059 Roo.data.JsonReader = function(meta, recordType){
10060     
10061     meta = meta || {};
10062     // set some defaults:
10063     Roo.applyIf(meta, {
10064         totalProperty: 'total',
10065         successProperty : 'success',
10066         root : 'data',
10067         id : 'id'
10068     });
10069     
10070     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10071 };
10072 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10073     
10074     /**
10075      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10076      * Used by Store query builder to append _requestMeta to params.
10077      * 
10078      */
10079     metaFromRemote : false,
10080     /**
10081      * This method is only used by a DataProxy which has retrieved data from a remote server.
10082      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10083      * @return {Object} data A data block which is used by an Roo.data.Store object as
10084      * a cache of Roo.data.Records.
10085      */
10086     read : function(response){
10087         var json = response.responseText;
10088        
10089         var o = /* eval:var:o */ eval("("+json+")");
10090         if(!o) {
10091             throw {message: "JsonReader.read: Json object not found"};
10092         }
10093         
10094         if(o.metaData){
10095             
10096             delete this.ef;
10097             this.metaFromRemote = true;
10098             this.meta = o.metaData;
10099             this.recordType = Roo.data.Record.create(o.metaData.fields);
10100             this.onMetaChange(this.meta, this.recordType, o);
10101         }
10102         return this.readRecords(o);
10103     },
10104
10105     // private function a store will implement
10106     onMetaChange : function(meta, recordType, o){
10107
10108     },
10109
10110     /**
10111          * @ignore
10112          */
10113     simpleAccess: function(obj, subsc) {
10114         return obj[subsc];
10115     },
10116
10117         /**
10118          * @ignore
10119          */
10120     getJsonAccessor: function(){
10121         var re = /[\[\.]/;
10122         return function(expr) {
10123             try {
10124                 return(re.test(expr))
10125                     ? new Function("obj", "return obj." + expr)
10126                     : function(obj){
10127                         return obj[expr];
10128                     };
10129             } catch(e){}
10130             return Roo.emptyFn;
10131         };
10132     }(),
10133
10134     /**
10135      * Create a data block containing Roo.data.Records from an XML document.
10136      * @param {Object} o An object which contains an Array of row objects in the property specified
10137      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10138      * which contains the total size of the dataset.
10139      * @return {Object} data A data block which is used by an Roo.data.Store object as
10140      * a cache of Roo.data.Records.
10141      */
10142     readRecords : function(o){
10143         /**
10144          * After any data loads, the raw JSON data is available for further custom processing.
10145          * @type Object
10146          */
10147         this.o = o;
10148         var s = this.meta, Record = this.recordType,
10149             f = Record.prototype.fields, fi = f.items, fl = f.length;
10150
10151 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10152         if (!this.ef) {
10153             if(s.totalProperty) {
10154                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10155                 }
10156                 if(s.successProperty) {
10157                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10158                 }
10159                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10160                 if (s.id) {
10161                         var g = this.getJsonAccessor(s.id);
10162                         this.getId = function(rec) {
10163                                 var r = g(rec);  
10164                                 return (r === undefined || r === "") ? null : r;
10165                         };
10166                 } else {
10167                         this.getId = function(){return null;};
10168                 }
10169             this.ef = [];
10170             for(var jj = 0; jj < fl; jj++){
10171                 f = fi[jj];
10172                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10173                 this.ef[jj] = this.getJsonAccessor(map);
10174             }
10175         }
10176
10177         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10178         if(s.totalProperty){
10179             var vt = parseInt(this.getTotal(o), 10);
10180             if(!isNaN(vt)){
10181                 totalRecords = vt;
10182             }
10183         }
10184         if(s.successProperty){
10185             var vs = this.getSuccess(o);
10186             if(vs === false || vs === 'false'){
10187                 success = false;
10188             }
10189         }
10190         var records = [];
10191             for(var i = 0; i < c; i++){
10192                     var n = root[i];
10193                 var values = {};
10194                 var id = this.getId(n);
10195                 for(var j = 0; j < fl; j++){
10196                     f = fi[j];
10197                 var v = this.ef[j](n);
10198                 if (!f.convert) {
10199                     Roo.log('missing convert for ' + f.name);
10200                     Roo.log(f);
10201                     continue;
10202                 }
10203                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10204                 }
10205                 var record = new Record(values, id);
10206                 record.json = n;
10207                 records[i] = record;
10208             }
10209             return {
10210             raw : o,
10211                 success : success,
10212                 records : records,
10213                 totalRecords : totalRecords
10214             };
10215     }
10216 });/*
10217  * Based on:
10218  * Ext JS Library 1.1.1
10219  * Copyright(c) 2006-2007, Ext JS, LLC.
10220  *
10221  * Originally Released Under LGPL - original licence link has changed is not relivant.
10222  *
10223  * Fork - LGPL
10224  * <script type="text/javascript">
10225  */
10226
10227 /**
10228  * @class Roo.data.ArrayReader
10229  * @extends Roo.data.DataReader
10230  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10231  * Each element of that Array represents a row of data fields. The
10232  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10233  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10234  * <p>
10235  * Example code:.
10236  * <pre><code>
10237 var RecordDef = Roo.data.Record.create([
10238     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10239     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10240 ]);
10241 var myReader = new Roo.data.ArrayReader({
10242     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10243 }, RecordDef);
10244 </code></pre>
10245  * <p>
10246  * This would consume an Array like this:
10247  * <pre><code>
10248 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10249   </code></pre>
10250  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10251  * @constructor
10252  * Create a new JsonReader
10253  * @param {Object} meta Metadata configuration options.
10254  * @param {Object} recordType Either an Array of field definition objects
10255  * as specified to {@link Roo.data.Record#create},
10256  * or an {@link Roo.data.Record} object
10257  * created using {@link Roo.data.Record#create}.
10258  */
10259 Roo.data.ArrayReader = function(meta, recordType){
10260     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10261 };
10262
10263 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10264     /**
10265      * Create a data block containing Roo.data.Records from an XML document.
10266      * @param {Object} o An Array of row objects which represents the dataset.
10267      * @return {Object} data A data block which is used by an Roo.data.Store object as
10268      * a cache of Roo.data.Records.
10269      */
10270     readRecords : function(o){
10271         var sid = this.meta ? this.meta.id : null;
10272         var recordType = this.recordType, fields = recordType.prototype.fields;
10273         var records = [];
10274         var root = o;
10275             for(var i = 0; i < root.length; i++){
10276                     var n = root[i];
10277                 var values = {};
10278                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10279                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10280                 var f = fields.items[j];
10281                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10282                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10283                 v = f.convert(v);
10284                 values[f.name] = v;
10285             }
10286                 var record = new recordType(values, id);
10287                 record.json = n;
10288                 records[records.length] = record;
10289             }
10290             return {
10291                 records : records,
10292                 totalRecords : records.length
10293             };
10294     }
10295 });/*
10296  * - LGPL
10297  * * 
10298  */
10299
10300 /**
10301  * @class Roo.bootstrap.ComboBox
10302  * @extends Roo.bootstrap.TriggerField
10303  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10304  * @cfg {Boolean} append (true|false) default false
10305  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10306  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10307  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10308  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10309  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10310  * @constructor
10311  * Create a new ComboBox.
10312  * @param {Object} config Configuration options
10313  */
10314 Roo.bootstrap.ComboBox = function(config){
10315     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10316     this.addEvents({
10317         /**
10318          * @event expand
10319          * Fires when the dropdown list is expanded
10320              * @param {Roo.bootstrap.ComboBox} combo This combo box
10321              */
10322         'expand' : true,
10323         /**
10324          * @event collapse
10325          * Fires when the dropdown list is collapsed
10326              * @param {Roo.bootstrap.ComboBox} combo This combo box
10327              */
10328         'collapse' : true,
10329         /**
10330          * @event beforeselect
10331          * Fires before a list item is selected. Return false to cancel the selection.
10332              * @param {Roo.bootstrap.ComboBox} combo This combo box
10333              * @param {Roo.data.Record} record The data record returned from the underlying store
10334              * @param {Number} index The index of the selected item in the dropdown list
10335              */
10336         'beforeselect' : true,
10337         /**
10338          * @event select
10339          * Fires when a list item is selected
10340              * @param {Roo.bootstrap.ComboBox} combo This combo box
10341              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10342              * @param {Number} index The index of the selected item in the dropdown list
10343              */
10344         'select' : true,
10345         /**
10346          * @event beforequery
10347          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10348          * The event object passed has these properties:
10349              * @param {Roo.bootstrap.ComboBox} combo This combo box
10350              * @param {String} query The query
10351              * @param {Boolean} forceAll true to force "all" query
10352              * @param {Boolean} cancel true to cancel the query
10353              * @param {Object} e The query event object
10354              */
10355         'beforequery': true,
10356          /**
10357          * @event add
10358          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10359              * @param {Roo.bootstrap.ComboBox} combo This combo box
10360              */
10361         'add' : true,
10362         /**
10363          * @event edit
10364          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10365              * @param {Roo.bootstrap.ComboBox} combo This combo box
10366              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10367              */
10368         'edit' : true,
10369         /**
10370          * @event remove
10371          * Fires when the remove value from the combobox array
10372              * @param {Roo.bootstrap.ComboBox} combo This combo box
10373              */
10374         'remove' : true
10375         
10376     });
10377     
10378     this.item = [];
10379     this.tickItems = [];
10380     
10381     this.selectedIndex = -1;
10382     if(this.mode == 'local'){
10383         if(config.queryDelay === undefined){
10384             this.queryDelay = 10;
10385         }
10386         if(config.minChars === undefined){
10387             this.minChars = 0;
10388         }
10389     }
10390 };
10391
10392 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10393      
10394     /**
10395      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10396      * rendering into an Roo.Editor, defaults to false)
10397      */
10398     /**
10399      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10400      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10401      */
10402     /**
10403      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10404      */
10405     /**
10406      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10407      * the dropdown list (defaults to undefined, with no header element)
10408      */
10409
10410      /**
10411      * @cfg {String/Roo.Template} tpl The template to use to render the output
10412      */
10413      
10414      /**
10415      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10416      */
10417     listWidth: undefined,
10418     /**
10419      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10420      * mode = 'remote' or 'text' if mode = 'local')
10421      */
10422     displayField: undefined,
10423     /**
10424      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10425      * mode = 'remote' or 'value' if mode = 'local'). 
10426      * Note: use of a valueField requires the user make a selection
10427      * in order for a value to be mapped.
10428      */
10429     valueField: undefined,
10430     
10431     
10432     /**
10433      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10434      * field's data value (defaults to the underlying DOM element's name)
10435      */
10436     hiddenName: undefined,
10437     /**
10438      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10439      */
10440     listClass: '',
10441     /**
10442      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10443      */
10444     selectedClass: 'active',
10445     
10446     /**
10447      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10448      */
10449     shadow:'sides',
10450     /**
10451      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10452      * anchor positions (defaults to 'tl-bl')
10453      */
10454     listAlign: 'tl-bl?',
10455     /**
10456      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10457      */
10458     maxHeight: 300,
10459     /**
10460      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10461      * query specified by the allQuery config option (defaults to 'query')
10462      */
10463     triggerAction: 'query',
10464     /**
10465      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10466      * (defaults to 4, does not apply if editable = false)
10467      */
10468     minChars : 4,
10469     /**
10470      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10471      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10472      */
10473     typeAhead: false,
10474     /**
10475      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10476      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10477      */
10478     queryDelay: 500,
10479     /**
10480      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10481      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10482      */
10483     pageSize: 0,
10484     /**
10485      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10486      * when editable = true (defaults to false)
10487      */
10488     selectOnFocus:false,
10489     /**
10490      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10491      */
10492     queryParam: 'query',
10493     /**
10494      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10495      * when mode = 'remote' (defaults to 'Loading...')
10496      */
10497     loadingText: 'Loading...',
10498     /**
10499      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10500      */
10501     resizable: false,
10502     /**
10503      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10504      */
10505     handleHeight : 8,
10506     /**
10507      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10508      * traditional select (defaults to true)
10509      */
10510     editable: true,
10511     /**
10512      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10513      */
10514     allQuery: '',
10515     /**
10516      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10517      */
10518     mode: 'remote',
10519     /**
10520      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10521      * listWidth has a higher value)
10522      */
10523     minListWidth : 70,
10524     /**
10525      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10526      * allow the user to set arbitrary text into the field (defaults to false)
10527      */
10528     forceSelection:false,
10529     /**
10530      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10531      * if typeAhead = true (defaults to 250)
10532      */
10533     typeAheadDelay : 250,
10534     /**
10535      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10536      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10537      */
10538     valueNotFoundText : undefined,
10539     /**
10540      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10541      */
10542     blockFocus : false,
10543     
10544     /**
10545      * @cfg {Boolean} disableClear Disable showing of clear button.
10546      */
10547     disableClear : false,
10548     /**
10549      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10550      */
10551     alwaysQuery : false,
10552     
10553     /**
10554      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10555      */
10556     multiple : false,
10557     
10558     //private
10559     addicon : false,
10560     editicon: false,
10561     
10562     page: 0,
10563     hasQuery: false,
10564     append: false,
10565     loadNext: false,
10566     autoFocus : true,
10567     tickable : false,
10568     btnPosition : 'right',
10569     triggerList : true,
10570     showToggleBtn : true,
10571     // element that contains real text value.. (when hidden is used..)
10572     
10573     getAutoCreate : function()
10574     {
10575         var cfg = false;
10576         
10577         /*
10578          *  Normal ComboBox
10579          */
10580         if(!this.tickable){
10581             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10582             return cfg;
10583         }
10584         
10585         /*
10586          *  ComboBox with tickable selections
10587          */
10588              
10589         var align = this.labelAlign || this.parentLabelAlign();
10590         
10591         cfg = {
10592             cls : 'form-group roo-combobox-tickable' //input-group
10593         };
10594         
10595         
10596         var buttons = {
10597             tag : 'div',
10598             cls : 'tickable-buttons',
10599             cn : [
10600                 {
10601                     tag : 'button',
10602                     type : 'button',
10603                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10604                     html : 'Edit'
10605                 },
10606                 {
10607                     tag : 'button',
10608                     type : 'button',
10609                     name : 'ok',
10610                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10611                     html : 'Done'
10612                 },
10613                 {
10614                     tag : 'button',
10615                     type : 'button',
10616                     name : 'cancel',
10617                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10618                     html : 'Cancel'
10619                 }
10620             ]
10621         };
10622         
10623         var _this = this;
10624         Roo.each(buttons.cn, function(c){
10625             if (_this.size) {
10626                 c.cls += ' btn-' + _this.size;
10627             }
10628
10629             if (_this.disabled) {
10630                 c.disabled = true;
10631             }
10632         });
10633         
10634         var box = {
10635             tag: 'div',
10636             cn: [
10637                 {
10638                     tag: 'input',
10639                     type : 'hidden',
10640                     cls: 'form-hidden-field'
10641                 },
10642                 {
10643                     tag: 'ul',
10644                     cls: 'select2-choices',
10645                     cn:[
10646                         {
10647                             tag: 'li',
10648                             cls: 'select2-search-field',
10649                             cn: [
10650
10651                                 buttons
10652                             ]
10653                         }
10654                     ]
10655                 }
10656             ]
10657         }
10658         
10659         var combobox = {
10660             cls: 'select2-container input-group select2-container-multi',
10661             cn: [
10662                 box
10663 //                {
10664 //                    tag: 'ul',
10665 //                    cls: 'typeahead typeahead-long dropdown-menu',
10666 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10667 //                }
10668             ]
10669         };
10670         
10671         if (align ==='left' && this.fieldLabel.length) {
10672             
10673                 Roo.log("left and has label");
10674                 cfg.cn = [
10675                     
10676                     {
10677                         tag: 'label',
10678                         'for' :  id,
10679                         cls : 'control-label col-sm-' + this.labelWidth,
10680                         html : this.fieldLabel
10681                         
10682                     },
10683                     {
10684                         cls : "col-sm-" + (12 - this.labelWidth), 
10685                         cn: [
10686                             combobox
10687                         ]
10688                     }
10689                     
10690                 ];
10691         } else if ( this.fieldLabel.length) {
10692                 Roo.log(" label");
10693                  cfg.cn = [
10694                    
10695                     {
10696                         tag: 'label',
10697                         //cls : 'input-group-addon',
10698                         html : this.fieldLabel
10699                         
10700                     },
10701                     
10702                     combobox
10703                     
10704                 ];
10705
10706         } else {
10707             
10708                 Roo.log(" no label && no align");
10709                 cfg = combobox
10710                      
10711                 
10712         }
10713          
10714         var settings=this;
10715         ['xs','sm','md','lg'].map(function(size){
10716             if (settings[size]) {
10717                 cfg.cls += ' col-' + size + '-' + settings[size];
10718             }
10719         });
10720         
10721         return cfg;
10722         
10723     },
10724     
10725     // private
10726     initEvents: function()
10727     {
10728         
10729         if (!this.store) {
10730             throw "can not find store for combo";
10731         }
10732         this.store = Roo.factory(this.store, Roo.data);
10733         
10734         if(this.tickable){
10735             this.initTickableEvents();
10736             return;
10737         }
10738         
10739         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10740         
10741         if(this.hiddenName){
10742             
10743             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10744             
10745             this.hiddenField.dom.value =
10746                 this.hiddenValue !== undefined ? this.hiddenValue :
10747                 this.value !== undefined ? this.value : '';
10748
10749             // prevent input submission
10750             this.el.dom.removeAttribute('name');
10751             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10752              
10753              
10754         }
10755         //if(Roo.isGecko){
10756         //    this.el.dom.setAttribute('autocomplete', 'off');
10757         //}
10758         
10759         var cls = 'x-combo-list';
10760         
10761         //this.list = new Roo.Layer({
10762         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10763         //});
10764         
10765         var _this = this;
10766         
10767         (function(){
10768             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10769             _this.list.setWidth(lw);
10770         }).defer(100);
10771         
10772         this.list.on('mouseover', this.onViewOver, this);
10773         this.list.on('mousemove', this.onViewMove, this);
10774         
10775         this.list.on('scroll', this.onViewScroll, this);
10776         
10777         /*
10778         this.list.swallowEvent('mousewheel');
10779         this.assetHeight = 0;
10780
10781         if(this.title){
10782             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10783             this.assetHeight += this.header.getHeight();
10784         }
10785
10786         this.innerList = this.list.createChild({cls:cls+'-inner'});
10787         this.innerList.on('mouseover', this.onViewOver, this);
10788         this.innerList.on('mousemove', this.onViewMove, this);
10789         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10790         
10791         if(this.allowBlank && !this.pageSize && !this.disableClear){
10792             this.footer = this.list.createChild({cls:cls+'-ft'});
10793             this.pageTb = new Roo.Toolbar(this.footer);
10794            
10795         }
10796         if(this.pageSize){
10797             this.footer = this.list.createChild({cls:cls+'-ft'});
10798             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10799                     {pageSize: this.pageSize});
10800             
10801         }
10802         
10803         if (this.pageTb && this.allowBlank && !this.disableClear) {
10804             var _this = this;
10805             this.pageTb.add(new Roo.Toolbar.Fill(), {
10806                 cls: 'x-btn-icon x-btn-clear',
10807                 text: '&#160;',
10808                 handler: function()
10809                 {
10810                     _this.collapse();
10811                     _this.clearValue();
10812                     _this.onSelect(false, -1);
10813                 }
10814             });
10815         }
10816         if (this.footer) {
10817             this.assetHeight += this.footer.getHeight();
10818         }
10819         */
10820             
10821         if(!this.tpl){
10822             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10823         }
10824
10825         this.view = new Roo.View(this.list, this.tpl, {
10826             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10827         });
10828         //this.view.wrapEl.setDisplayed(false);
10829         this.view.on('click', this.onViewClick, this);
10830         
10831         
10832         
10833         this.store.on('beforeload', this.onBeforeLoad, this);
10834         this.store.on('load', this.onLoad, this);
10835         this.store.on('loadexception', this.onLoadException, this);
10836         /*
10837         if(this.resizable){
10838             this.resizer = new Roo.Resizable(this.list,  {
10839                pinned:true, handles:'se'
10840             });
10841             this.resizer.on('resize', function(r, w, h){
10842                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10843                 this.listWidth = w;
10844                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10845                 this.restrictHeight();
10846             }, this);
10847             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10848         }
10849         */
10850         if(!this.editable){
10851             this.editable = true;
10852             this.setEditable(false);
10853         }
10854         
10855         /*
10856         
10857         if (typeof(this.events.add.listeners) != 'undefined') {
10858             
10859             this.addicon = this.wrap.createChild(
10860                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10861        
10862             this.addicon.on('click', function(e) {
10863                 this.fireEvent('add', this);
10864             }, this);
10865         }
10866         if (typeof(this.events.edit.listeners) != 'undefined') {
10867             
10868             this.editicon = this.wrap.createChild(
10869                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10870             if (this.addicon) {
10871                 this.editicon.setStyle('margin-left', '40px');
10872             }
10873             this.editicon.on('click', function(e) {
10874                 
10875                 // we fire even  if inothing is selected..
10876                 this.fireEvent('edit', this, this.lastData );
10877                 
10878             }, this);
10879         }
10880         */
10881         
10882         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10883             "up" : function(e){
10884                 this.inKeyMode = true;
10885                 this.selectPrev();
10886             },
10887
10888             "down" : function(e){
10889                 if(!this.isExpanded()){
10890                     this.onTriggerClick();
10891                 }else{
10892                     this.inKeyMode = true;
10893                     this.selectNext();
10894                 }
10895             },
10896
10897             "enter" : function(e){
10898 //                this.onViewClick();
10899                 //return true;
10900                 this.collapse();
10901                 
10902                 if(this.fireEvent("specialkey", this, e)){
10903                     this.onViewClick(false);
10904                 }
10905                 
10906                 return true;
10907             },
10908
10909             "esc" : function(e){
10910                 this.collapse();
10911             },
10912
10913             "tab" : function(e){
10914                 this.collapse();
10915                 
10916                 if(this.fireEvent("specialkey", this, e)){
10917                     this.onViewClick(false);
10918                 }
10919                 
10920                 return true;
10921             },
10922
10923             scope : this,
10924
10925             doRelay : function(foo, bar, hname){
10926                 if(hname == 'down' || this.scope.isExpanded()){
10927                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10928                 }
10929                 return true;
10930             },
10931
10932             forceKeyDown: true
10933         });
10934         
10935         
10936         this.queryDelay = Math.max(this.queryDelay || 10,
10937                 this.mode == 'local' ? 10 : 250);
10938         
10939         
10940         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10941         
10942         if(this.typeAhead){
10943             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10944         }
10945         if(this.editable !== false){
10946             this.inputEl().on("keyup", this.onKeyUp, this);
10947         }
10948         if(this.forceSelection){
10949             this.inputEl().on('blur', this.doForce, this);
10950         }
10951         
10952         if(this.multiple){
10953             this.choices = this.el.select('ul.select2-choices', true).first();
10954             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10955         }
10956     },
10957     
10958     initTickableEvents: function()
10959     {   
10960         this.createList();
10961         
10962         if(this.hiddenName){
10963             
10964             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10965             
10966             this.hiddenField.dom.value =
10967                 this.hiddenValue !== undefined ? this.hiddenValue :
10968                 this.value !== undefined ? this.value : '';
10969
10970             // prevent input submission
10971             this.el.dom.removeAttribute('name');
10972             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10973              
10974              
10975         }
10976         
10977 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10978         
10979         this.choices = this.el.select('ul.select2-choices', true).first();
10980         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10981         if(this.triggerList){
10982             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10983         }
10984          
10985         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10986         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10987         
10988         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10989         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10990         
10991         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10992         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10993         
10994         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10995         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10996         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10997         
10998         this.okBtn.hide();
10999         this.cancelBtn.hide();
11000         
11001         var _this = this;
11002         
11003         (function(){
11004             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11005             _this.list.setWidth(lw);
11006         }).defer(100);
11007         
11008         this.list.on('mouseover', this.onViewOver, this);
11009         this.list.on('mousemove', this.onViewMove, this);
11010         
11011         this.list.on('scroll', this.onViewScroll, this);
11012         
11013         if(!this.tpl){
11014             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>';
11015         }
11016
11017         this.view = new Roo.View(this.list, this.tpl, {
11018             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11019         });
11020         
11021         //this.view.wrapEl.setDisplayed(false);
11022         this.view.on('click', this.onViewClick, this);
11023         
11024         
11025         
11026         this.store.on('beforeload', this.onBeforeLoad, this);
11027         this.store.on('load', this.onLoad, this);
11028         this.store.on('loadexception', this.onLoadException, this);
11029         
11030 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11031 //            "up" : function(e){
11032 //                this.inKeyMode = true;
11033 //                this.selectPrev();
11034 //            },
11035 //
11036 //            "down" : function(e){
11037 //                if(!this.isExpanded()){
11038 //                    this.onTriggerClick();
11039 //                }else{
11040 //                    this.inKeyMode = true;
11041 //                    this.selectNext();
11042 //                }
11043 //            },
11044 //
11045 //            "enter" : function(e){
11046 ////                this.onViewClick();
11047 //                //return true;
11048 //                this.collapse();
11049 //                
11050 //                if(this.fireEvent("specialkey", this, e)){
11051 //                    this.onViewClick(false);
11052 //                }
11053 //                
11054 //                return true;
11055 //            },
11056 //
11057 //            "esc" : function(e){
11058 //                this.collapse();
11059 //            },
11060 //
11061 //            "tab" : function(e){
11062 //                this.collapse();
11063 //                
11064 //                if(this.fireEvent("specialkey", this, e)){
11065 //                    this.onViewClick(false);
11066 //                }
11067 //                
11068 //                return true;
11069 //            },
11070 //
11071 //            scope : this,
11072 //
11073 //            doRelay : function(foo, bar, hname){
11074 //                if(hname == 'down' || this.scope.isExpanded()){
11075 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11076 //                }
11077 //                return true;
11078 //            },
11079 //
11080 //            forceKeyDown: true
11081 //        });
11082         
11083         
11084         this.queryDelay = Math.max(this.queryDelay || 10,
11085                 this.mode == 'local' ? 10 : 250);
11086         
11087         
11088         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11089         
11090         if(this.typeAhead){
11091             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11092         }
11093     },
11094
11095     onDestroy : function(){
11096         if(this.view){
11097             this.view.setStore(null);
11098             this.view.el.removeAllListeners();
11099             this.view.el.remove();
11100             this.view.purgeListeners();
11101         }
11102         if(this.list){
11103             this.list.dom.innerHTML  = '';
11104         }
11105         
11106         if(this.store){
11107             this.store.un('beforeload', this.onBeforeLoad, this);
11108             this.store.un('load', this.onLoad, this);
11109             this.store.un('loadexception', this.onLoadException, this);
11110         }
11111         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11112     },
11113
11114     // private
11115     fireKey : function(e){
11116         if(e.isNavKeyPress() && !this.list.isVisible()){
11117             this.fireEvent("specialkey", this, e);
11118         }
11119     },
11120
11121     // private
11122     onResize: function(w, h){
11123 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11124 //        
11125 //        if(typeof w != 'number'){
11126 //            // we do not handle it!?!?
11127 //            return;
11128 //        }
11129 //        var tw = this.trigger.getWidth();
11130 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11131 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11132 //        var x = w - tw;
11133 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11134 //            
11135 //        //this.trigger.setStyle('left', x+'px');
11136 //        
11137 //        if(this.list && this.listWidth === undefined){
11138 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11139 //            this.list.setWidth(lw);
11140 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11141 //        }
11142         
11143     
11144         
11145     },
11146
11147     /**
11148      * Allow or prevent the user from directly editing the field text.  If false is passed,
11149      * the user will only be able to select from the items defined in the dropdown list.  This method
11150      * is the runtime equivalent of setting the 'editable' config option at config time.
11151      * @param {Boolean} value True to allow the user to directly edit the field text
11152      */
11153     setEditable : function(value){
11154         if(value == this.editable){
11155             return;
11156         }
11157         this.editable = value;
11158         if(!value){
11159             this.inputEl().dom.setAttribute('readOnly', true);
11160             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11161             this.inputEl().addClass('x-combo-noedit');
11162         }else{
11163             this.inputEl().dom.setAttribute('readOnly', false);
11164             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11165             this.inputEl().removeClass('x-combo-noedit');
11166         }
11167     },
11168
11169     // private
11170     
11171     onBeforeLoad : function(combo,opts){
11172         if(!this.hasFocus){
11173             return;
11174         }
11175          if (!opts.add) {
11176             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11177          }
11178         this.restrictHeight();
11179         this.selectedIndex = -1;
11180     },
11181
11182     // private
11183     onLoad : function(){
11184         
11185         this.hasQuery = false;
11186         
11187         if(!this.hasFocus){
11188             return;
11189         }
11190         
11191         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11192             this.loading.hide();
11193         }
11194         
11195         if(this.store.getCount() > 0){
11196             this.expand();
11197 //            this.restrictHeight();
11198             if(this.lastQuery == this.allQuery){
11199                 if(this.editable && !this.tickable){
11200                     this.inputEl().dom.select();
11201                 }
11202                 
11203                 if(
11204                     !this.selectByValue(this.value, true) &&
11205                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11206                     this.store.lastOptions.add != true)
11207                 ){
11208                     this.select(0, true);
11209                 }
11210             }else{
11211                 if(this.autoFocus){
11212                     this.selectNext();
11213                 }
11214                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11215                     this.taTask.delay(this.typeAheadDelay);
11216                 }
11217             }
11218         }else{
11219             this.onEmptyResults();
11220         }
11221         
11222         //this.el.focus();
11223     },
11224     // private
11225     onLoadException : function()
11226     {
11227         this.hasQuery = false;
11228         
11229         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11230             this.loading.hide();
11231         }
11232         
11233         this.collapse();
11234         Roo.log(this.store.reader.jsonData);
11235         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11236             // fixme
11237             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11238         }
11239         
11240         
11241     },
11242     // private
11243     onTypeAhead : function(){
11244         if(this.store.getCount() > 0){
11245             var r = this.store.getAt(0);
11246             var newValue = r.data[this.displayField];
11247             var len = newValue.length;
11248             var selStart = this.getRawValue().length;
11249             
11250             if(selStart != len){
11251                 this.setRawValue(newValue);
11252                 this.selectText(selStart, newValue.length);
11253             }
11254         }
11255     },
11256
11257     // private
11258     onSelect : function(record, index){
11259         
11260         if(this.fireEvent('beforeselect', this, record, index) !== false){
11261         
11262             this.setFromData(index > -1 ? record.data : false);
11263             
11264             this.collapse();
11265             this.fireEvent('select', this, record, index);
11266         }
11267     },
11268
11269     /**
11270      * Returns the currently selected field value or empty string if no value is set.
11271      * @return {String} value The selected value
11272      */
11273     getValue : function(){
11274         
11275         if(this.multiple){
11276             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11277         }
11278         
11279         if(this.valueField){
11280             return typeof this.value != 'undefined' ? this.value : '';
11281         }else{
11282             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11283         }
11284     },
11285
11286     /**
11287      * Clears any text/value currently set in the field
11288      */
11289     clearValue : function(){
11290         if(this.hiddenField){
11291             this.hiddenField.dom.value = '';
11292         }
11293         this.value = '';
11294         this.setRawValue('');
11295         this.lastSelectionText = '';
11296         
11297     },
11298
11299     /**
11300      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11301      * will be displayed in the field.  If the value does not match the data value of an existing item,
11302      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11303      * Otherwise the field will be blank (although the value will still be set).
11304      * @param {String} value The value to match
11305      */
11306     setValue : function(v){
11307         if(this.multiple){
11308             this.syncValue();
11309             return;
11310         }
11311         
11312         var text = v;
11313         if(this.valueField){
11314             var r = this.findRecord(this.valueField, v);
11315             if(r){
11316                 text = r.data[this.displayField];
11317             }else if(this.valueNotFoundText !== undefined){
11318                 text = this.valueNotFoundText;
11319             }
11320         }
11321         this.lastSelectionText = text;
11322         if(this.hiddenField){
11323             this.hiddenField.dom.value = v;
11324         }
11325         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11326         this.value = v;
11327     },
11328     /**
11329      * @property {Object} the last set data for the element
11330      */
11331     
11332     lastData : false,
11333     /**
11334      * Sets the value of the field based on a object which is related to the record format for the store.
11335      * @param {Object} value the value to set as. or false on reset?
11336      */
11337     setFromData : function(o){
11338         
11339         if(this.multiple){
11340             this.addItem(o);
11341             return;
11342         }
11343             
11344         var dv = ''; // display value
11345         var vv = ''; // value value..
11346         this.lastData = o;
11347         if (this.displayField) {
11348             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11349         } else {
11350             // this is an error condition!!!
11351             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11352         }
11353         
11354         if(this.valueField){
11355             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11356         }
11357         
11358         if(this.hiddenField){
11359             this.hiddenField.dom.value = vv;
11360             
11361             this.lastSelectionText = dv;
11362             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11363             this.value = vv;
11364             return;
11365         }
11366         // no hidden field.. - we store the value in 'value', but still display
11367         // display field!!!!
11368         this.lastSelectionText = dv;
11369         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11370         this.value = vv;
11371         
11372         
11373     },
11374     // private
11375     reset : function(){
11376         // overridden so that last data is reset..
11377         this.setValue(this.originalValue);
11378         this.clearInvalid();
11379         this.lastData = false;
11380         if (this.view) {
11381             this.view.clearSelections();
11382         }
11383     },
11384     // private
11385     findRecord : function(prop, value){
11386         var record;
11387         if(this.store.getCount() > 0){
11388             this.store.each(function(r){
11389                 if(r.data[prop] == value){
11390                     record = r;
11391                     return false;
11392                 }
11393                 return true;
11394             });
11395         }
11396         return record;
11397     },
11398     
11399     getName: function()
11400     {
11401         // returns hidden if it's set..
11402         if (!this.rendered) {return ''};
11403         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11404         
11405     },
11406     // private
11407     onViewMove : function(e, t){
11408         this.inKeyMode = false;
11409     },
11410
11411     // private
11412     onViewOver : function(e, t){
11413         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11414             return;
11415         }
11416         var item = this.view.findItemFromChild(t);
11417         
11418         if(item){
11419             var index = this.view.indexOf(item);
11420             this.select(index, false);
11421         }
11422     },
11423
11424     // private
11425     onViewClick : function(view, doFocus, el, e)
11426     {
11427         var index = this.view.getSelectedIndexes()[0];
11428         
11429         var r = this.store.getAt(index);
11430         
11431         if(this.tickable){
11432             
11433             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11434                 return;
11435             }
11436             
11437             var rm = false;
11438             var _this = this;
11439             
11440             Roo.each(this.tickItems, function(v,k){
11441                 
11442                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11443                     _this.tickItems.splice(k, 1);
11444                     rm = true;
11445                     return;
11446                 }
11447             })
11448             
11449             if(rm){
11450                 return;
11451             }
11452             
11453             this.tickItems.push(r.data);
11454             return;
11455         }
11456         
11457         if(r){
11458             this.onSelect(r, index);
11459         }
11460         if(doFocus !== false && !this.blockFocus){
11461             this.inputEl().focus();
11462         }
11463     },
11464
11465     // private
11466     restrictHeight : function(){
11467         //this.innerList.dom.style.height = '';
11468         //var inner = this.innerList.dom;
11469         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11470         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11471         //this.list.beginUpdate();
11472         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11473         this.list.alignTo(this.inputEl(), this.listAlign);
11474         this.list.alignTo(this.inputEl(), this.listAlign);
11475         //this.list.endUpdate();
11476     },
11477
11478     // private
11479     onEmptyResults : function(){
11480         this.collapse();
11481     },
11482
11483     /**
11484      * Returns true if the dropdown list is expanded, else false.
11485      */
11486     isExpanded : function(){
11487         return this.list.isVisible();
11488     },
11489
11490     /**
11491      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11492      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11493      * @param {String} value The data value of the item to select
11494      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11495      * selected item if it is not currently in view (defaults to true)
11496      * @return {Boolean} True if the value matched an item in the list, else false
11497      */
11498     selectByValue : function(v, scrollIntoView){
11499         if(v !== undefined && v !== null){
11500             var r = this.findRecord(this.valueField || this.displayField, v);
11501             if(r){
11502                 this.select(this.store.indexOf(r), scrollIntoView);
11503                 return true;
11504             }
11505         }
11506         return false;
11507     },
11508
11509     /**
11510      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11511      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11512      * @param {Number} index The zero-based index of the list item to select
11513      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11514      * selected item if it is not currently in view (defaults to true)
11515      */
11516     select : function(index, scrollIntoView){
11517         this.selectedIndex = index;
11518         this.view.select(index);
11519         if(scrollIntoView !== false){
11520             var el = this.view.getNode(index);
11521             if(el && !this.multiple && !this.tickable){
11522                 this.list.scrollChildIntoView(el, false);
11523             }
11524         }
11525     },
11526
11527     // private
11528     selectNext : function(){
11529         var ct = this.store.getCount();
11530         if(ct > 0){
11531             if(this.selectedIndex == -1){
11532                 this.select(0);
11533             }else if(this.selectedIndex < ct-1){
11534                 this.select(this.selectedIndex+1);
11535             }
11536         }
11537     },
11538
11539     // private
11540     selectPrev : function(){
11541         var ct = this.store.getCount();
11542         if(ct > 0){
11543             if(this.selectedIndex == -1){
11544                 this.select(0);
11545             }else if(this.selectedIndex != 0){
11546                 this.select(this.selectedIndex-1);
11547             }
11548         }
11549     },
11550
11551     // private
11552     onKeyUp : function(e){
11553         if(this.editable !== false && !e.isSpecialKey()){
11554             this.lastKey = e.getKey();
11555             this.dqTask.delay(this.queryDelay);
11556         }
11557     },
11558
11559     // private
11560     validateBlur : function(){
11561         return !this.list || !this.list.isVisible();   
11562     },
11563
11564     // private
11565     initQuery : function(){
11566         this.doQuery(this.getRawValue());
11567     },
11568
11569     // private
11570     doForce : function(){
11571         if(this.inputEl().dom.value.length > 0){
11572             this.inputEl().dom.value =
11573                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11574              
11575         }
11576     },
11577
11578     /**
11579      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11580      * query allowing the query action to be canceled if needed.
11581      * @param {String} query The SQL query to execute
11582      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11583      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11584      * saved in the current store (defaults to false)
11585      */
11586     doQuery : function(q, forceAll){
11587         
11588         if(q === undefined || q === null){
11589             q = '';
11590         }
11591         var qe = {
11592             query: q,
11593             forceAll: forceAll,
11594             combo: this,
11595             cancel:false
11596         };
11597         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11598             return false;
11599         }
11600         q = qe.query;
11601         
11602         forceAll = qe.forceAll;
11603         if(forceAll === true || (q.length >= this.minChars)){
11604             
11605             this.hasQuery = true;
11606             
11607             if(this.lastQuery != q || this.alwaysQuery){
11608                 this.lastQuery = q;
11609                 if(this.mode == 'local'){
11610                     this.selectedIndex = -1;
11611                     if(forceAll){
11612                         this.store.clearFilter();
11613                     }else{
11614                         this.store.filter(this.displayField, q);
11615                     }
11616                     this.onLoad();
11617                 }else{
11618                     this.store.baseParams[this.queryParam] = q;
11619                     
11620                     var options = {params : this.getParams(q)};
11621                     
11622                     if(this.loadNext){
11623                         options.add = true;
11624                         options.params.start = this.page * this.pageSize;
11625                     }
11626                     
11627                     this.store.load(options);
11628                     /*
11629                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11630                      *  we should expand the list on onLoad
11631                      *  so command out it
11632                      */
11633 //                    this.expand();
11634                 }
11635             }else{
11636                 this.selectedIndex = -1;
11637                 this.onLoad();   
11638             }
11639         }
11640         
11641         this.loadNext = false;
11642     },
11643
11644     // private
11645     getParams : function(q){
11646         var p = {};
11647         //p[this.queryParam] = q;
11648         
11649         if(this.pageSize){
11650             p.start = 0;
11651             p.limit = this.pageSize;
11652         }
11653         return p;
11654     },
11655
11656     /**
11657      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11658      */
11659     collapse : function(){
11660         if(!this.isExpanded()){
11661             return;
11662         }
11663         
11664         this.list.hide();
11665         
11666         if(this.tickable){
11667             this.okBtn.hide();
11668             this.cancelBtn.hide();
11669             this.trigger.show();
11670         }
11671         
11672         Roo.get(document).un('mousedown', this.collapseIf, this);
11673         Roo.get(document).un('mousewheel', this.collapseIf, this);
11674         if (!this.editable) {
11675             Roo.get(document).un('keydown', this.listKeyPress, this);
11676         }
11677         this.fireEvent('collapse', this);
11678     },
11679
11680     // private
11681     collapseIf : function(e){
11682         var in_combo  = e.within(this.el);
11683         var in_list =  e.within(this.list);
11684         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11685         
11686         if (in_combo || in_list || is_list) {
11687             //e.stopPropagation();
11688             return;
11689         }
11690         
11691         if(this.tickable){
11692             this.onTickableFooterButtonClick(e, false, false);
11693         }
11694
11695         this.collapse();
11696         
11697     },
11698
11699     /**
11700      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11701      */
11702     expand : function(){
11703        
11704         if(this.isExpanded() || !this.hasFocus){
11705             return;
11706         }
11707         
11708         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11709         this.list.setWidth(lw);
11710         
11711         
11712          Roo.log('expand');
11713         
11714         this.list.show();
11715         
11716         this.restrictHeight();
11717         
11718         if(this.tickable){
11719             
11720             this.tickItems = Roo.apply([], this.item);
11721             
11722             this.okBtn.show();
11723             this.cancelBtn.show();
11724             this.trigger.hide();
11725             
11726         }
11727         
11728         Roo.get(document).on('mousedown', this.collapseIf, this);
11729         Roo.get(document).on('mousewheel', this.collapseIf, this);
11730         if (!this.editable) {
11731             Roo.get(document).on('keydown', this.listKeyPress, this);
11732         }
11733         
11734         this.fireEvent('expand', this);
11735     },
11736
11737     // private
11738     // Implements the default empty TriggerField.onTriggerClick function
11739     onTriggerClick : function(e)
11740     {
11741         Roo.log('trigger click');
11742         
11743         if(this.disabled || !this.triggerList){
11744             return;
11745         }
11746         
11747         this.page = 0;
11748         this.loadNext = false;
11749         
11750         if(this.isExpanded()){
11751             this.collapse();
11752             if (!this.blockFocus) {
11753                 this.inputEl().focus();
11754             }
11755             
11756         }else {
11757             this.hasFocus = true;
11758             if(this.triggerAction == 'all') {
11759                 this.doQuery(this.allQuery, true);
11760             } else {
11761                 this.doQuery(this.getRawValue());
11762             }
11763             if (!this.blockFocus) {
11764                 this.inputEl().focus();
11765             }
11766         }
11767     },
11768     
11769     onTickableTriggerClick : function(e)
11770     {
11771         if(this.disabled){
11772             return;
11773         }
11774         
11775         this.page = 0;
11776         this.loadNext = false;
11777         this.hasFocus = true;
11778         
11779         if(this.triggerAction == 'all') {
11780             this.doQuery(this.allQuery, true);
11781         } else {
11782             this.doQuery(this.getRawValue());
11783         }
11784     },
11785     
11786     onSearchFieldClick : function(e)
11787     {
11788         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11789             return;
11790         }
11791         
11792         this.page = 0;
11793         this.loadNext = false;
11794         this.hasFocus = true;
11795         
11796         if(this.triggerAction == 'all') {
11797             this.doQuery(this.allQuery, true);
11798         } else {
11799             this.doQuery(this.getRawValue());
11800         }
11801     },
11802     
11803     listKeyPress : function(e)
11804     {
11805         //Roo.log('listkeypress');
11806         // scroll to first matching element based on key pres..
11807         if (e.isSpecialKey()) {
11808             return false;
11809         }
11810         var k = String.fromCharCode(e.getKey()).toUpperCase();
11811         //Roo.log(k);
11812         var match  = false;
11813         var csel = this.view.getSelectedNodes();
11814         var cselitem = false;
11815         if (csel.length) {
11816             var ix = this.view.indexOf(csel[0]);
11817             cselitem  = this.store.getAt(ix);
11818             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11819                 cselitem = false;
11820             }
11821             
11822         }
11823         
11824         this.store.each(function(v) { 
11825             if (cselitem) {
11826                 // start at existing selection.
11827                 if (cselitem.id == v.id) {
11828                     cselitem = false;
11829                 }
11830                 return true;
11831             }
11832                 
11833             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11834                 match = this.store.indexOf(v);
11835                 return false;
11836             }
11837             return true;
11838         }, this);
11839         
11840         if (match === false) {
11841             return true; // no more action?
11842         }
11843         // scroll to?
11844         this.view.select(match);
11845         var sn = Roo.get(this.view.getSelectedNodes()[0])
11846         //sn.scrollIntoView(sn.dom.parentNode, false);
11847     },
11848     
11849     onViewScroll : function(e, t){
11850         
11851         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){
11852             return;
11853         }
11854         
11855         this.hasQuery = true;
11856         
11857         this.loading = this.list.select('.loading', true).first();
11858         
11859         if(this.loading === null){
11860             this.list.createChild({
11861                 tag: 'div',
11862                 cls: 'loading select2-more-results select2-active',
11863                 html: 'Loading more results...'
11864             })
11865             
11866             this.loading = this.list.select('.loading', true).first();
11867             
11868             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11869             
11870             this.loading.hide();
11871         }
11872         
11873         this.loading.show();
11874         
11875         var _combo = this;
11876         
11877         this.page++;
11878         this.loadNext = true;
11879         
11880         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11881         
11882         return;
11883     },
11884     
11885     addItem : function(o)
11886     {   
11887         var dv = ''; // display value
11888         
11889         if (this.displayField) {
11890             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11891         } else {
11892             // this is an error condition!!!
11893             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11894         }
11895         
11896         if(!dv.length){
11897             return;
11898         }
11899         
11900         var choice = this.choices.createChild({
11901             tag: 'li',
11902             cls: 'select2-search-choice',
11903             cn: [
11904                 {
11905                     tag: 'div',
11906                     html: dv
11907                 },
11908                 {
11909                     tag: 'a',
11910                     href: '#',
11911                     cls: 'select2-search-choice-close',
11912                     tabindex: '-1'
11913                 }
11914             ]
11915             
11916         }, this.searchField);
11917         
11918         var close = choice.select('a.select2-search-choice-close', true).first()
11919         
11920         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11921         
11922         this.item.push(o);
11923         
11924         this.lastData = o;
11925         
11926         this.syncValue();
11927         
11928         this.inputEl().dom.value = '';
11929         
11930     },
11931     
11932     onRemoveItem : function(e, _self, o)
11933     {
11934         e.preventDefault();
11935         var index = this.item.indexOf(o.data) * 1;
11936         
11937         if( index < 0){
11938             Roo.log('not this item?!');
11939             return;
11940         }
11941         
11942         this.item.splice(index, 1);
11943         o.item.remove();
11944         
11945         this.syncValue();
11946         
11947         this.fireEvent('remove', this, e);
11948         
11949     },
11950     
11951     syncValue : function()
11952     {
11953         if(!this.item.length){
11954             this.clearValue();
11955             return;
11956         }
11957             
11958         var value = [];
11959         var _this = this;
11960         Roo.each(this.item, function(i){
11961             if(_this.valueField){
11962                 value.push(i[_this.valueField]);
11963                 return;
11964             }
11965
11966             value.push(i);
11967         });
11968
11969         this.value = value.join(',');
11970
11971         if(this.hiddenField){
11972             this.hiddenField.dom.value = this.value;
11973         }
11974     },
11975     
11976     clearItem : function()
11977     {
11978         if(!this.multiple){
11979             return;
11980         }
11981         
11982         this.item = [];
11983         
11984         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11985            c.remove();
11986         });
11987         
11988         this.syncValue();
11989     },
11990     
11991     inputEl: function ()
11992     {
11993         if(this.tickable){
11994             return this.searchField;
11995         }
11996         return this.el.select('input.form-control',true).first();
11997     },
11998     
11999     
12000     onTickableFooterButtonClick : function(e, btn, el)
12001     {
12002         e.preventDefault();
12003         
12004         if(btn && btn.name == 'cancel'){
12005             this.tickItems = Roo.apply([], this.item);
12006             this.collapse();
12007             return;
12008         }
12009         
12010         this.clearItem();
12011         
12012         var _this = this;
12013         
12014         Roo.each(this.tickItems, function(o){
12015             _this.addItem(o);
12016         });
12017         
12018         this.collapse();
12019         
12020     }
12021     
12022     
12023
12024     /** 
12025     * @cfg {Boolean} grow 
12026     * @hide 
12027     */
12028     /** 
12029     * @cfg {Number} growMin 
12030     * @hide 
12031     */
12032     /** 
12033     * @cfg {Number} growMax 
12034     * @hide 
12035     */
12036     /**
12037      * @hide
12038      * @method autoSize
12039      */
12040 });
12041 /*
12042  * Based on:
12043  * Ext JS Library 1.1.1
12044  * Copyright(c) 2006-2007, Ext JS, LLC.
12045  *
12046  * Originally Released Under LGPL - original licence link has changed is not relivant.
12047  *
12048  * Fork - LGPL
12049  * <script type="text/javascript">
12050  */
12051
12052 /**
12053  * @class Roo.View
12054  * @extends Roo.util.Observable
12055  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12056  * This class also supports single and multi selection modes. <br>
12057  * Create a data model bound view:
12058  <pre><code>
12059  var store = new Roo.data.Store(...);
12060
12061  var view = new Roo.View({
12062     el : "my-element",
12063     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12064  
12065     singleSelect: true,
12066     selectedClass: "ydataview-selected",
12067     store: store
12068  });
12069
12070  // listen for node click?
12071  view.on("click", function(vw, index, node, e){
12072  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12073  });
12074
12075  // load XML data
12076  dataModel.load("foobar.xml");
12077  </code></pre>
12078  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12079  * <br><br>
12080  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12081  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12082  * 
12083  * Note: old style constructor is still suported (container, template, config)
12084  * 
12085  * @constructor
12086  * Create a new View
12087  * @param {Object} config The config object
12088  * 
12089  */
12090 Roo.View = function(config, depreciated_tpl, depreciated_config){
12091     
12092     this.parent = false;
12093     
12094     if (typeof(depreciated_tpl) == 'undefined') {
12095         // new way.. - universal constructor.
12096         Roo.apply(this, config);
12097         this.el  = Roo.get(this.el);
12098     } else {
12099         // old format..
12100         this.el  = Roo.get(config);
12101         this.tpl = depreciated_tpl;
12102         Roo.apply(this, depreciated_config);
12103     }
12104     this.wrapEl  = this.el.wrap().wrap();
12105     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12106     
12107     
12108     if(typeof(this.tpl) == "string"){
12109         this.tpl = new Roo.Template(this.tpl);
12110     } else {
12111         // support xtype ctors..
12112         this.tpl = new Roo.factory(this.tpl, Roo);
12113     }
12114     
12115     
12116     this.tpl.compile();
12117     
12118     /** @private */
12119     this.addEvents({
12120         /**
12121          * @event beforeclick
12122          * Fires before a click is processed. Returns false to cancel the default action.
12123          * @param {Roo.View} this
12124          * @param {Number} index The index of the target node
12125          * @param {HTMLElement} node The target node
12126          * @param {Roo.EventObject} e The raw event object
12127          */
12128             "beforeclick" : true,
12129         /**
12130          * @event click
12131          * Fires when a template node is clicked.
12132          * @param {Roo.View} this
12133          * @param {Number} index The index of the target node
12134          * @param {HTMLElement} node The target node
12135          * @param {Roo.EventObject} e The raw event object
12136          */
12137             "click" : true,
12138         /**
12139          * @event dblclick
12140          * Fires when a template node is double clicked.
12141          * @param {Roo.View} this
12142          * @param {Number} index The index of the target node
12143          * @param {HTMLElement} node The target node
12144          * @param {Roo.EventObject} e The raw event object
12145          */
12146             "dblclick" : true,
12147         /**
12148          * @event contextmenu
12149          * Fires when a template node is right clicked.
12150          * @param {Roo.View} this
12151          * @param {Number} index The index of the target node
12152          * @param {HTMLElement} node The target node
12153          * @param {Roo.EventObject} e The raw event object
12154          */
12155             "contextmenu" : true,
12156         /**
12157          * @event selectionchange
12158          * Fires when the selected nodes change.
12159          * @param {Roo.View} this
12160          * @param {Array} selections Array of the selected nodes
12161          */
12162             "selectionchange" : true,
12163     
12164         /**
12165          * @event beforeselect
12166          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12167          * @param {Roo.View} this
12168          * @param {HTMLElement} node The node to be selected
12169          * @param {Array} selections Array of currently selected nodes
12170          */
12171             "beforeselect" : true,
12172         /**
12173          * @event preparedata
12174          * Fires on every row to render, to allow you to change the data.
12175          * @param {Roo.View} this
12176          * @param {Object} data to be rendered (change this)
12177          */
12178           "preparedata" : true
12179           
12180           
12181         });
12182
12183
12184
12185     this.el.on({
12186         "click": this.onClick,
12187         "dblclick": this.onDblClick,
12188         "contextmenu": this.onContextMenu,
12189         scope:this
12190     });
12191
12192     this.selections = [];
12193     this.nodes = [];
12194     this.cmp = new Roo.CompositeElementLite([]);
12195     if(this.store){
12196         this.store = Roo.factory(this.store, Roo.data);
12197         this.setStore(this.store, true);
12198     }
12199     
12200     if ( this.footer && this.footer.xtype) {
12201            
12202          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12203         
12204         this.footer.dataSource = this.store
12205         this.footer.container = fctr;
12206         this.footer = Roo.factory(this.footer, Roo);
12207         fctr.insertFirst(this.el);
12208         
12209         // this is a bit insane - as the paging toolbar seems to detach the el..
12210 //        dom.parentNode.parentNode.parentNode
12211          // they get detached?
12212     }
12213     
12214     
12215     Roo.View.superclass.constructor.call(this);
12216     
12217     
12218 };
12219
12220 Roo.extend(Roo.View, Roo.util.Observable, {
12221     
12222      /**
12223      * @cfg {Roo.data.Store} store Data store to load data from.
12224      */
12225     store : false,
12226     
12227     /**
12228      * @cfg {String|Roo.Element} el The container element.
12229      */
12230     el : '',
12231     
12232     /**
12233      * @cfg {String|Roo.Template} tpl The template used by this View 
12234      */
12235     tpl : false,
12236     /**
12237      * @cfg {String} dataName the named area of the template to use as the data area
12238      *                          Works with domtemplates roo-name="name"
12239      */
12240     dataName: false,
12241     /**
12242      * @cfg {String} selectedClass The css class to add to selected nodes
12243      */
12244     selectedClass : "x-view-selected",
12245      /**
12246      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12247      */
12248     emptyText : "",
12249     
12250     /**
12251      * @cfg {String} text to display on mask (default Loading)
12252      */
12253     mask : false,
12254     /**
12255      * @cfg {Boolean} multiSelect Allow multiple selection
12256      */
12257     multiSelect : false,
12258     /**
12259      * @cfg {Boolean} singleSelect Allow single selection
12260      */
12261     singleSelect:  false,
12262     
12263     /**
12264      * @cfg {Boolean} toggleSelect - selecting 
12265      */
12266     toggleSelect : false,
12267     
12268     /**
12269      * @cfg {Boolean} tickable - selecting 
12270      */
12271     tickable : false,
12272     
12273     /**
12274      * Returns the element this view is bound to.
12275      * @return {Roo.Element}
12276      */
12277     getEl : function(){
12278         return this.wrapEl;
12279     },
12280     
12281     
12282
12283     /**
12284      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12285      */
12286     refresh : function(){
12287         Roo.log('refresh');
12288         var t = this.tpl;
12289         
12290         // if we are using something like 'domtemplate', then
12291         // the what gets used is:
12292         // t.applySubtemplate(NAME, data, wrapping data..)
12293         // the outer template then get' applied with
12294         //     the store 'extra data'
12295         // and the body get's added to the
12296         //      roo-name="data" node?
12297         //      <span class='roo-tpl-{name}'></span> ?????
12298         
12299         
12300         
12301         this.clearSelections();
12302         this.el.update("");
12303         var html = [];
12304         var records = this.store.getRange();
12305         if(records.length < 1) {
12306             
12307             // is this valid??  = should it render a template??
12308             
12309             this.el.update(this.emptyText);
12310             return;
12311         }
12312         var el = this.el;
12313         if (this.dataName) {
12314             this.el.update(t.apply(this.store.meta)); //????
12315             el = this.el.child('.roo-tpl-' + this.dataName);
12316         }
12317         
12318         for(var i = 0, len = records.length; i < len; i++){
12319             var data = this.prepareData(records[i].data, i, records[i]);
12320             this.fireEvent("preparedata", this, data, i, records[i]);
12321             
12322             var d = Roo.apply({}, data);
12323             
12324             if(this.tickable){
12325                 Roo.apply(d, {'roo-id' : Roo.id()});
12326                 
12327                 var _this = this;
12328             
12329                 Roo.each(this.parent.item, function(item){
12330                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12331                         return;
12332                     }
12333                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12334                 });
12335             }
12336             
12337             html[html.length] = Roo.util.Format.trim(
12338                 this.dataName ?
12339                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12340                     t.apply(d)
12341             );
12342         }
12343         
12344         
12345         
12346         el.update(html.join(""));
12347         this.nodes = el.dom.childNodes;
12348         this.updateIndexes(0);
12349     },
12350     
12351
12352     /**
12353      * Function to override to reformat the data that is sent to
12354      * the template for each node.
12355      * DEPRICATED - use the preparedata event handler.
12356      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12357      * a JSON object for an UpdateManager bound view).
12358      */
12359     prepareData : function(data, index, record)
12360     {
12361         this.fireEvent("preparedata", this, data, index, record);
12362         return data;
12363     },
12364
12365     onUpdate : function(ds, record){
12366          Roo.log('on update');   
12367         this.clearSelections();
12368         var index = this.store.indexOf(record);
12369         var n = this.nodes[index];
12370         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12371         n.parentNode.removeChild(n);
12372         this.updateIndexes(index, index);
12373     },
12374
12375     
12376     
12377 // --------- FIXME     
12378     onAdd : function(ds, records, index)
12379     {
12380         Roo.log(['on Add', ds, records, index] );        
12381         this.clearSelections();
12382         if(this.nodes.length == 0){
12383             this.refresh();
12384             return;
12385         }
12386         var n = this.nodes[index];
12387         for(var i = 0, len = records.length; i < len; i++){
12388             var d = this.prepareData(records[i].data, i, records[i]);
12389             if(n){
12390                 this.tpl.insertBefore(n, d);
12391             }else{
12392                 
12393                 this.tpl.append(this.el, d);
12394             }
12395         }
12396         this.updateIndexes(index);
12397     },
12398
12399     onRemove : function(ds, record, index){
12400         Roo.log('onRemove');
12401         this.clearSelections();
12402         var el = this.dataName  ?
12403             this.el.child('.roo-tpl-' + this.dataName) :
12404             this.el; 
12405         
12406         el.dom.removeChild(this.nodes[index]);
12407         this.updateIndexes(index);
12408     },
12409
12410     /**
12411      * Refresh an individual node.
12412      * @param {Number} index
12413      */
12414     refreshNode : function(index){
12415         this.onUpdate(this.store, this.store.getAt(index));
12416     },
12417
12418     updateIndexes : function(startIndex, endIndex){
12419         var ns = this.nodes;
12420         startIndex = startIndex || 0;
12421         endIndex = endIndex || ns.length - 1;
12422         for(var i = startIndex; i <= endIndex; i++){
12423             ns[i].nodeIndex = i;
12424         }
12425     },
12426
12427     /**
12428      * Changes the data store this view uses and refresh the view.
12429      * @param {Store} store
12430      */
12431     setStore : function(store, initial){
12432         if(!initial && this.store){
12433             this.store.un("datachanged", this.refresh);
12434             this.store.un("add", this.onAdd);
12435             this.store.un("remove", this.onRemove);
12436             this.store.un("update", this.onUpdate);
12437             this.store.un("clear", this.refresh);
12438             this.store.un("beforeload", this.onBeforeLoad);
12439             this.store.un("load", this.onLoad);
12440             this.store.un("loadexception", this.onLoad);
12441         }
12442         if(store){
12443           
12444             store.on("datachanged", this.refresh, this);
12445             store.on("add", this.onAdd, this);
12446             store.on("remove", this.onRemove, this);
12447             store.on("update", this.onUpdate, this);
12448             store.on("clear", this.refresh, this);
12449             store.on("beforeload", this.onBeforeLoad, this);
12450             store.on("load", this.onLoad, this);
12451             store.on("loadexception", this.onLoad, this);
12452         }
12453         
12454         if(store){
12455             this.refresh();
12456         }
12457     },
12458     /**
12459      * onbeforeLoad - masks the loading area.
12460      *
12461      */
12462     onBeforeLoad : function(store,opts)
12463     {
12464          Roo.log('onBeforeLoad');   
12465         if (!opts.add) {
12466             this.el.update("");
12467         }
12468         this.el.mask(this.mask ? this.mask : "Loading" ); 
12469     },
12470     onLoad : function ()
12471     {
12472         this.el.unmask();
12473     },
12474     
12475
12476     /**
12477      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12478      * @param {HTMLElement} node
12479      * @return {HTMLElement} The template node
12480      */
12481     findItemFromChild : function(node){
12482         var el = this.dataName  ?
12483             this.el.child('.roo-tpl-' + this.dataName,true) :
12484             this.el.dom; 
12485         
12486         if(!node || node.parentNode == el){
12487                     return node;
12488             }
12489             var p = node.parentNode;
12490             while(p && p != el){
12491             if(p.parentNode == el){
12492                 return p;
12493             }
12494             p = p.parentNode;
12495         }
12496             return null;
12497     },
12498
12499     /** @ignore */
12500     onClick : function(e){
12501         var item = this.findItemFromChild(e.getTarget());
12502         if(item){
12503             var index = this.indexOf(item);
12504             if(this.onItemClick(item, index, e) !== false){
12505                 this.fireEvent("click", this, index, item, e);
12506             }
12507         }else{
12508             this.clearSelections();
12509         }
12510     },
12511
12512     /** @ignore */
12513     onContextMenu : function(e){
12514         var item = this.findItemFromChild(e.getTarget());
12515         if(item){
12516             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12517         }
12518     },
12519
12520     /** @ignore */
12521     onDblClick : function(e){
12522         var item = this.findItemFromChild(e.getTarget());
12523         if(item){
12524             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12525         }
12526     },
12527
12528     onItemClick : function(item, index, e)
12529     {
12530         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12531             return false;
12532         }
12533         if (this.toggleSelect) {
12534             var m = this.isSelected(item) ? 'unselect' : 'select';
12535             Roo.log(m);
12536             var _t = this;
12537             _t[m](item, true, false);
12538             return true;
12539         }
12540         if(this.multiSelect || this.singleSelect){
12541             if(this.multiSelect && e.shiftKey && this.lastSelection){
12542                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12543             }else{
12544                 this.select(item, this.multiSelect && e.ctrlKey);
12545                 this.lastSelection = item;
12546             }
12547             
12548             if(!this.tickable){
12549                 e.preventDefault();
12550             }
12551             
12552         }
12553         return true;
12554     },
12555
12556     /**
12557      * Get the number of selected nodes.
12558      * @return {Number}
12559      */
12560     getSelectionCount : function(){
12561         return this.selections.length;
12562     },
12563
12564     /**
12565      * Get the currently selected nodes.
12566      * @return {Array} An array of HTMLElements
12567      */
12568     getSelectedNodes : function(){
12569         return this.selections;
12570     },
12571
12572     /**
12573      * Get the indexes of the selected nodes.
12574      * @return {Array}
12575      */
12576     getSelectedIndexes : function(){
12577         var indexes = [], s = this.selections;
12578         for(var i = 0, len = s.length; i < len; i++){
12579             indexes.push(s[i].nodeIndex);
12580         }
12581         return indexes;
12582     },
12583
12584     /**
12585      * Clear all selections
12586      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12587      */
12588     clearSelections : function(suppressEvent){
12589         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12590             this.cmp.elements = this.selections;
12591             this.cmp.removeClass(this.selectedClass);
12592             this.selections = [];
12593             if(!suppressEvent){
12594                 this.fireEvent("selectionchange", this, this.selections);
12595             }
12596         }
12597     },
12598
12599     /**
12600      * Returns true if the passed node is selected
12601      * @param {HTMLElement/Number} node The node or node index
12602      * @return {Boolean}
12603      */
12604     isSelected : function(node){
12605         var s = this.selections;
12606         if(s.length < 1){
12607             return false;
12608         }
12609         node = this.getNode(node);
12610         return s.indexOf(node) !== -1;
12611     },
12612
12613     /**
12614      * Selects nodes.
12615      * @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
12616      * @param {Boolean} keepExisting (optional) true to keep existing selections
12617      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12618      */
12619     select : function(nodeInfo, keepExisting, suppressEvent){
12620         if(nodeInfo instanceof Array){
12621             if(!keepExisting){
12622                 this.clearSelections(true);
12623             }
12624             for(var i = 0, len = nodeInfo.length; i < len; i++){
12625                 this.select(nodeInfo[i], true, true);
12626             }
12627             return;
12628         } 
12629         var node = this.getNode(nodeInfo);
12630         if(!node || this.isSelected(node)){
12631             return; // already selected.
12632         }
12633         if(!keepExisting){
12634             this.clearSelections(true);
12635         }
12636         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12637             Roo.fly(node).addClass(this.selectedClass);
12638             this.selections.push(node);
12639             if(!suppressEvent){
12640                 this.fireEvent("selectionchange", this, this.selections);
12641             }
12642         }
12643         
12644         
12645     },
12646       /**
12647      * Unselects nodes.
12648      * @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
12649      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12650      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12651      */
12652     unselect : function(nodeInfo, keepExisting, suppressEvent)
12653     {
12654         if(nodeInfo instanceof Array){
12655             Roo.each(this.selections, function(s) {
12656                 this.unselect(s, nodeInfo);
12657             }, this);
12658             return;
12659         }
12660         var node = this.getNode(nodeInfo);
12661         if(!node || !this.isSelected(node)){
12662             Roo.log("not selected");
12663             return; // not selected.
12664         }
12665         // fireevent???
12666         var ns = [];
12667         Roo.each(this.selections, function(s) {
12668             if (s == node ) {
12669                 Roo.fly(node).removeClass(this.selectedClass);
12670
12671                 return;
12672             }
12673             ns.push(s);
12674         },this);
12675         
12676         this.selections= ns;
12677         this.fireEvent("selectionchange", this, this.selections);
12678     },
12679
12680     /**
12681      * Gets a template node.
12682      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12683      * @return {HTMLElement} The node or null if it wasn't found
12684      */
12685     getNode : function(nodeInfo){
12686         if(typeof nodeInfo == "string"){
12687             return document.getElementById(nodeInfo);
12688         }else if(typeof nodeInfo == "number"){
12689             return this.nodes[nodeInfo];
12690         }
12691         return nodeInfo;
12692     },
12693
12694     /**
12695      * Gets a range template nodes.
12696      * @param {Number} startIndex
12697      * @param {Number} endIndex
12698      * @return {Array} An array of nodes
12699      */
12700     getNodes : function(start, end){
12701         var ns = this.nodes;
12702         start = start || 0;
12703         end = typeof end == "undefined" ? ns.length - 1 : end;
12704         var nodes = [];
12705         if(start <= end){
12706             for(var i = start; i <= end; i++){
12707                 nodes.push(ns[i]);
12708             }
12709         } else{
12710             for(var i = start; i >= end; i--){
12711                 nodes.push(ns[i]);
12712             }
12713         }
12714         return nodes;
12715     },
12716
12717     /**
12718      * Finds the index of the passed node
12719      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12720      * @return {Number} The index of the node or -1
12721      */
12722     indexOf : function(node){
12723         node = this.getNode(node);
12724         if(typeof node.nodeIndex == "number"){
12725             return node.nodeIndex;
12726         }
12727         var ns = this.nodes;
12728         for(var i = 0, len = ns.length; i < len; i++){
12729             if(ns[i] == node){
12730                 return i;
12731             }
12732         }
12733         return -1;
12734     }
12735 });
12736 /*
12737  * - LGPL
12738  *
12739  * based on jquery fullcalendar
12740  * 
12741  */
12742
12743 Roo.bootstrap = Roo.bootstrap || {};
12744 /**
12745  * @class Roo.bootstrap.Calendar
12746  * @extends Roo.bootstrap.Component
12747  * Bootstrap Calendar class
12748  * @cfg {Boolean} loadMask (true|false) default false
12749  * @cfg {Object} header generate the user specific header of the calendar, default false
12750
12751  * @constructor
12752  * Create a new Container
12753  * @param {Object} config The config object
12754  */
12755
12756
12757
12758 Roo.bootstrap.Calendar = function(config){
12759     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12760      this.addEvents({
12761         /**
12762              * @event select
12763              * Fires when a date is selected
12764              * @param {DatePicker} this
12765              * @param {Date} date The selected date
12766              */
12767         'select': true,
12768         /**
12769              * @event monthchange
12770              * Fires when the displayed month changes 
12771              * @param {DatePicker} this
12772              * @param {Date} date The selected month
12773              */
12774         'monthchange': true,
12775         /**
12776              * @event evententer
12777              * Fires when mouse over an event
12778              * @param {Calendar} this
12779              * @param {event} Event
12780              */
12781         'evententer': true,
12782         /**
12783              * @event eventleave
12784              * Fires when the mouse leaves an
12785              * @param {Calendar} this
12786              * @param {event}
12787              */
12788         'eventleave': true,
12789         /**
12790              * @event eventclick
12791              * Fires when the mouse click an
12792              * @param {Calendar} this
12793              * @param {event}
12794              */
12795         'eventclick': true
12796         
12797     });
12798
12799 };
12800
12801 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12802     
12803      /**
12804      * @cfg {Number} startDay
12805      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12806      */
12807     startDay : 0,
12808     
12809     loadMask : false,
12810     
12811     header : false,
12812       
12813     getAutoCreate : function(){
12814         
12815         
12816         var fc_button = function(name, corner, style, content ) {
12817             return Roo.apply({},{
12818                 tag : 'span',
12819                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12820                          (corner.length ?
12821                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12822                             ''
12823                         ),
12824                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12825                 unselectable: 'on'
12826             });
12827         };
12828         
12829         var header = {};
12830         
12831         if(!this.header){
12832             header = {
12833                 tag : 'table',
12834                 cls : 'fc-header',
12835                 style : 'width:100%',
12836                 cn : [
12837                     {
12838                         tag: 'tr',
12839                         cn : [
12840                             {
12841                                 tag : 'td',
12842                                 cls : 'fc-header-left',
12843                                 cn : [
12844                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12845                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12846                                     { tag: 'span', cls: 'fc-header-space' },
12847                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12848
12849
12850                                 ]
12851                             },
12852
12853                             {
12854                                 tag : 'td',
12855                                 cls : 'fc-header-center',
12856                                 cn : [
12857                                     {
12858                                         tag: 'span',
12859                                         cls: 'fc-header-title',
12860                                         cn : {
12861                                             tag: 'H2',
12862                                             html : 'month / year'
12863                                         }
12864                                     }
12865
12866                                 ]
12867                             },
12868                             {
12869                                 tag : 'td',
12870                                 cls : 'fc-header-right',
12871                                 cn : [
12872                               /*      fc_button('month', 'left', '', 'month' ),
12873                                     fc_button('week', '', '', 'week' ),
12874                                     fc_button('day', 'right', '', 'day' )
12875                                 */    
12876
12877                                 ]
12878                             }
12879
12880                         ]
12881                     }
12882                 ]
12883             };
12884         }
12885         
12886         header = this.header;
12887         
12888        
12889         var cal_heads = function() {
12890             var ret = [];
12891             // fixme - handle this.
12892             
12893             for (var i =0; i < Date.dayNames.length; i++) {
12894                 var d = Date.dayNames[i];
12895                 ret.push({
12896                     tag: 'th',
12897                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12898                     html : d.substring(0,3)
12899                 });
12900                 
12901             }
12902             ret[0].cls += ' fc-first';
12903             ret[6].cls += ' fc-last';
12904             return ret;
12905         };
12906         var cal_cell = function(n) {
12907             return  {
12908                 tag: 'td',
12909                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12910                 cn : [
12911                     {
12912                         cn : [
12913                             {
12914                                 cls: 'fc-day-number',
12915                                 html: 'D'
12916                             },
12917                             {
12918                                 cls: 'fc-day-content',
12919                              
12920                                 cn : [
12921                                      {
12922                                         style: 'position: relative;' // height: 17px;
12923                                     }
12924                                 ]
12925                             }
12926                             
12927                             
12928                         ]
12929                     }
12930                 ]
12931                 
12932             }
12933         };
12934         var cal_rows = function() {
12935             
12936             var ret = []
12937             for (var r = 0; r < 6; r++) {
12938                 var row= {
12939                     tag : 'tr',
12940                     cls : 'fc-week',
12941                     cn : []
12942                 };
12943                 
12944                 for (var i =0; i < Date.dayNames.length; i++) {
12945                     var d = Date.dayNames[i];
12946                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12947
12948                 }
12949                 row.cn[0].cls+=' fc-first';
12950                 row.cn[0].cn[0].style = 'min-height:90px';
12951                 row.cn[6].cls+=' fc-last';
12952                 ret.push(row);
12953                 
12954             }
12955             ret[0].cls += ' fc-first';
12956             ret[4].cls += ' fc-prev-last';
12957             ret[5].cls += ' fc-last';
12958             return ret;
12959             
12960         };
12961         
12962         var cal_table = {
12963             tag: 'table',
12964             cls: 'fc-border-separate',
12965             style : 'width:100%',
12966             cellspacing  : 0,
12967             cn : [
12968                 { 
12969                     tag: 'thead',
12970                     cn : [
12971                         { 
12972                             tag: 'tr',
12973                             cls : 'fc-first fc-last',
12974                             cn : cal_heads()
12975                         }
12976                     ]
12977                 },
12978                 { 
12979                     tag: 'tbody',
12980                     cn : cal_rows()
12981                 }
12982                   
12983             ]
12984         };
12985          
12986          var cfg = {
12987             cls : 'fc fc-ltr',
12988             cn : [
12989                 header,
12990                 {
12991                     cls : 'fc-content',
12992                     style : "position: relative;",
12993                     cn : [
12994                         {
12995                             cls : 'fc-view fc-view-month fc-grid',
12996                             style : 'position: relative',
12997                             unselectable : 'on',
12998                             cn : [
12999                                 {
13000                                     cls : 'fc-event-container',
13001                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13002                                 },
13003                                 cal_table
13004                             ]
13005                         }
13006                     ]
13007     
13008                 }
13009            ] 
13010             
13011         };
13012         
13013          
13014         
13015         return cfg;
13016     },
13017     
13018     
13019     initEvents : function()
13020     {
13021         if(!this.store){
13022             throw "can not find store for calendar";
13023         }
13024         
13025         var mark = {
13026             tag: "div",
13027             cls:"x-dlg-mask",
13028             style: "text-align:center",
13029             cn: [
13030                 {
13031                     tag: "div",
13032                     style: "background-color:white;width:50%;margin:250 auto",
13033                     cn: [
13034                         {
13035                             tag: "img",
13036                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13037                         },
13038                         {
13039                             tag: "span",
13040                             html: "Loading"
13041                         }
13042                         
13043                     ]
13044                 }
13045             ]
13046         }
13047         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13048         
13049         var size = this.el.select('.fc-content', true).first().getSize();
13050         this.maskEl.setSize(size.width, size.height);
13051         this.maskEl.enableDisplayMode("block");
13052         if(!this.loadMask){
13053             this.maskEl.hide();
13054         }
13055         
13056         this.store = Roo.factory(this.store, Roo.data);
13057         this.store.on('load', this.onLoad, this);
13058         this.store.on('beforeload', this.onBeforeLoad, this);
13059         
13060         this.resize();
13061         
13062         this.cells = this.el.select('.fc-day',true);
13063         //Roo.log(this.cells);
13064         this.textNodes = this.el.query('.fc-day-number');
13065         this.cells.addClassOnOver('fc-state-hover');
13066         
13067         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13068         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13069         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13070         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13071         
13072         this.on('monthchange', this.onMonthChange, this);
13073         
13074         this.update(new Date().clearTime());
13075     },
13076     
13077     resize : function() {
13078         var sz  = this.el.getSize();
13079         
13080         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13081         this.el.select('.fc-day-content div',true).setHeight(34);
13082     },
13083     
13084     
13085     // private
13086     showPrevMonth : function(e){
13087         this.update(this.activeDate.add("mo", -1));
13088     },
13089     showToday : function(e){
13090         this.update(new Date().clearTime());
13091     },
13092     // private
13093     showNextMonth : function(e){
13094         this.update(this.activeDate.add("mo", 1));
13095     },
13096
13097     // private
13098     showPrevYear : function(){
13099         this.update(this.activeDate.add("y", -1));
13100     },
13101
13102     // private
13103     showNextYear : function(){
13104         this.update(this.activeDate.add("y", 1));
13105     },
13106
13107     
13108    // private
13109     update : function(date)
13110     {
13111         var vd = this.activeDate;
13112         this.activeDate = date;
13113 //        if(vd && this.el){
13114 //            var t = date.getTime();
13115 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13116 //                Roo.log('using add remove');
13117 //                
13118 //                this.fireEvent('monthchange', this, date);
13119 //                
13120 //                this.cells.removeClass("fc-state-highlight");
13121 //                this.cells.each(function(c){
13122 //                   if(c.dateValue == t){
13123 //                       c.addClass("fc-state-highlight");
13124 //                       setTimeout(function(){
13125 //                            try{c.dom.firstChild.focus();}catch(e){}
13126 //                       }, 50);
13127 //                       return false;
13128 //                   }
13129 //                   return true;
13130 //                });
13131 //                return;
13132 //            }
13133 //        }
13134         
13135         var days = date.getDaysInMonth();
13136         
13137         var firstOfMonth = date.getFirstDateOfMonth();
13138         var startingPos = firstOfMonth.getDay()-this.startDay;
13139         
13140         if(startingPos < this.startDay){
13141             startingPos += 7;
13142         }
13143         
13144         var pm = date.add(Date.MONTH, -1);
13145         var prevStart = pm.getDaysInMonth()-startingPos;
13146 //        
13147         this.cells = this.el.select('.fc-day',true);
13148         this.textNodes = this.el.query('.fc-day-number');
13149         this.cells.addClassOnOver('fc-state-hover');
13150         
13151         var cells = this.cells.elements;
13152         var textEls = this.textNodes;
13153         
13154         Roo.each(cells, function(cell){
13155             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13156         });
13157         
13158         days += startingPos;
13159
13160         // convert everything to numbers so it's fast
13161         var day = 86400000;
13162         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13163         //Roo.log(d);
13164         //Roo.log(pm);
13165         //Roo.log(prevStart);
13166         
13167         var today = new Date().clearTime().getTime();
13168         var sel = date.clearTime().getTime();
13169         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13170         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13171         var ddMatch = this.disabledDatesRE;
13172         var ddText = this.disabledDatesText;
13173         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13174         var ddaysText = this.disabledDaysText;
13175         var format = this.format;
13176         
13177         var setCellClass = function(cal, cell){
13178             cell.row = 0;
13179             cell.events = [];
13180             cell.more = [];
13181             //Roo.log('set Cell Class');
13182             cell.title = "";
13183             var t = d.getTime();
13184             
13185             //Roo.log(d);
13186             
13187             cell.dateValue = t;
13188             if(t == today){
13189                 cell.className += " fc-today";
13190                 cell.className += " fc-state-highlight";
13191                 cell.title = cal.todayText;
13192             }
13193             if(t == sel){
13194                 // disable highlight in other month..
13195                 //cell.className += " fc-state-highlight";
13196                 
13197             }
13198             // disabling
13199             if(t < min) {
13200                 cell.className = " fc-state-disabled";
13201                 cell.title = cal.minText;
13202                 return;
13203             }
13204             if(t > max) {
13205                 cell.className = " fc-state-disabled";
13206                 cell.title = cal.maxText;
13207                 return;
13208             }
13209             if(ddays){
13210                 if(ddays.indexOf(d.getDay()) != -1){
13211                     cell.title = ddaysText;
13212                     cell.className = " fc-state-disabled";
13213                 }
13214             }
13215             if(ddMatch && format){
13216                 var fvalue = d.dateFormat(format);
13217                 if(ddMatch.test(fvalue)){
13218                     cell.title = ddText.replace("%0", fvalue);
13219                     cell.className = " fc-state-disabled";
13220                 }
13221             }
13222             
13223             if (!cell.initialClassName) {
13224                 cell.initialClassName = cell.dom.className;
13225             }
13226             
13227             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13228         };
13229
13230         var i = 0;
13231         
13232         for(; i < startingPos; i++) {
13233             textEls[i].innerHTML = (++prevStart);
13234             d.setDate(d.getDate()+1);
13235             
13236             cells[i].className = "fc-past fc-other-month";
13237             setCellClass(this, cells[i]);
13238         }
13239         
13240         var intDay = 0;
13241         
13242         for(; i < days; i++){
13243             intDay = i - startingPos + 1;
13244             textEls[i].innerHTML = (intDay);
13245             d.setDate(d.getDate()+1);
13246             
13247             cells[i].className = ''; // "x-date-active";
13248             setCellClass(this, cells[i]);
13249         }
13250         var extraDays = 0;
13251         
13252         for(; i < 42; i++) {
13253             textEls[i].innerHTML = (++extraDays);
13254             d.setDate(d.getDate()+1);
13255             
13256             cells[i].className = "fc-future fc-other-month";
13257             setCellClass(this, cells[i]);
13258         }
13259         
13260         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13261         
13262         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13263         
13264         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13265         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13266         
13267         if(totalRows != 6){
13268             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13269             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13270         }
13271         
13272         this.fireEvent('monthchange', this, date);
13273         
13274         
13275         /*
13276         if(!this.internalRender){
13277             var main = this.el.dom.firstChild;
13278             var w = main.offsetWidth;
13279             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13280             Roo.fly(main).setWidth(w);
13281             this.internalRender = true;
13282             // opera does not respect the auto grow header center column
13283             // then, after it gets a width opera refuses to recalculate
13284             // without a second pass
13285             if(Roo.isOpera && !this.secondPass){
13286                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13287                 this.secondPass = true;
13288                 this.update.defer(10, this, [date]);
13289             }
13290         }
13291         */
13292         
13293     },
13294     
13295     findCell : function(dt) {
13296         dt = dt.clearTime().getTime();
13297         var ret = false;
13298         this.cells.each(function(c){
13299             //Roo.log("check " +c.dateValue + '?=' + dt);
13300             if(c.dateValue == dt){
13301                 ret = c;
13302                 return false;
13303             }
13304             return true;
13305         });
13306         
13307         return ret;
13308     },
13309     
13310     findCells : function(ev) {
13311         var s = ev.start.clone().clearTime().getTime();
13312        // Roo.log(s);
13313         var e= ev.end.clone().clearTime().getTime();
13314        // Roo.log(e);
13315         var ret = [];
13316         this.cells.each(function(c){
13317              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13318             
13319             if(c.dateValue > e){
13320                 return ;
13321             }
13322             if(c.dateValue < s){
13323                 return ;
13324             }
13325             ret.push(c);
13326         });
13327         
13328         return ret;    
13329     },
13330     
13331 //    findBestRow: function(cells)
13332 //    {
13333 //        var ret = 0;
13334 //        
13335 //        for (var i =0 ; i < cells.length;i++) {
13336 //            ret  = Math.max(cells[i].rows || 0,ret);
13337 //        }
13338 //        return ret;
13339 //        
13340 //    },
13341     
13342     
13343     addItem : function(ev)
13344     {
13345         // look for vertical location slot in
13346         var cells = this.findCells(ev);
13347         
13348 //        ev.row = this.findBestRow(cells);
13349         
13350         // work out the location.
13351         
13352         var crow = false;
13353         var rows = [];
13354         for(var i =0; i < cells.length; i++) {
13355             
13356             cells[i].row = cells[0].row;
13357             
13358             if(i == 0){
13359                 cells[i].row = cells[i].row + 1;
13360             }
13361             
13362             if (!crow) {
13363                 crow = {
13364                     start : cells[i],
13365                     end :  cells[i]
13366                 };
13367                 continue;
13368             }
13369             if (crow.start.getY() == cells[i].getY()) {
13370                 // on same row.
13371                 crow.end = cells[i];
13372                 continue;
13373             }
13374             // different row.
13375             rows.push(crow);
13376             crow = {
13377                 start: cells[i],
13378                 end : cells[i]
13379             };
13380             
13381         }
13382         
13383         rows.push(crow);
13384         ev.els = [];
13385         ev.rows = rows;
13386         ev.cells = cells;
13387         
13388         cells[0].events.push(ev);
13389         
13390         this.calevents.push(ev);
13391     },
13392     
13393     clearEvents: function() {
13394         
13395         if(!this.calevents){
13396             return;
13397         }
13398         
13399         Roo.each(this.cells.elements, function(c){
13400             c.row = 0;
13401             c.events = [];
13402             c.more = [];
13403         });
13404         
13405         Roo.each(this.calevents, function(e) {
13406             Roo.each(e.els, function(el) {
13407                 el.un('mouseenter' ,this.onEventEnter, this);
13408                 el.un('mouseleave' ,this.onEventLeave, this);
13409                 el.remove();
13410             },this);
13411         },this);
13412         
13413         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13414             e.remove();
13415         });
13416         
13417     },
13418     
13419     renderEvents: function()
13420     {   
13421         var _this = this;
13422         
13423         this.cells.each(function(c) {
13424             
13425             if(c.row < 5){
13426                 return;
13427             }
13428             
13429             var ev = c.events;
13430             
13431             var r = 4;
13432             if(c.row != c.events.length){
13433                 r = 4 - (4 - (c.row - c.events.length));
13434             }
13435             
13436             c.events = ev.slice(0, r);
13437             c.more = ev.slice(r);
13438             
13439             if(c.more.length && c.more.length == 1){
13440                 c.events.push(c.more.pop());
13441             }
13442             
13443             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13444             
13445         });
13446             
13447         this.cells.each(function(c) {
13448             
13449             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13450             
13451             
13452             for (var e = 0; e < c.events.length; e++){
13453                 var ev = c.events[e];
13454                 var rows = ev.rows;
13455                 
13456                 for(var i = 0; i < rows.length; i++) {
13457                 
13458                     // how many rows should it span..
13459
13460                     var  cfg = {
13461                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13462                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13463
13464                         unselectable : "on",
13465                         cn : [
13466                             {
13467                                 cls: 'fc-event-inner',
13468                                 cn : [
13469     //                                {
13470     //                                  tag:'span',
13471     //                                  cls: 'fc-event-time',
13472     //                                  html : cells.length > 1 ? '' : ev.time
13473     //                                },
13474                                     {
13475                                       tag:'span',
13476                                       cls: 'fc-event-title',
13477                                       html : String.format('{0}', ev.title)
13478                                     }
13479
13480
13481                                 ]
13482                             },
13483                             {
13484                                 cls: 'ui-resizable-handle ui-resizable-e',
13485                                 html : '&nbsp;&nbsp;&nbsp'
13486                             }
13487
13488                         ]
13489                     };
13490
13491                     if (i == 0) {
13492                         cfg.cls += ' fc-event-start';
13493                     }
13494                     if ((i+1) == rows.length) {
13495                         cfg.cls += ' fc-event-end';
13496                     }
13497
13498                     var ctr = _this.el.select('.fc-event-container',true).first();
13499                     var cg = ctr.createChild(cfg);
13500
13501                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13502                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13503
13504                     var r = (c.more.length) ? 1 : 0;
13505                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13506                     cg.setWidth(ebox.right - sbox.x -2);
13507
13508                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13509                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13510                     cg.on('click', _this.onEventClick, _this, ev);
13511
13512                     ev.els.push(cg);
13513                     
13514                 }
13515                 
13516             }
13517             
13518             
13519             if(c.more.length){
13520                 var  cfg = {
13521                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13522                     style : 'position: absolute',
13523                     unselectable : "on",
13524                     cn : [
13525                         {
13526                             cls: 'fc-event-inner',
13527                             cn : [
13528                                 {
13529                                   tag:'span',
13530                                   cls: 'fc-event-title',
13531                                   html : 'More'
13532                                 }
13533
13534
13535                             ]
13536                         },
13537                         {
13538                             cls: 'ui-resizable-handle ui-resizable-e',
13539                             html : '&nbsp;&nbsp;&nbsp'
13540                         }
13541
13542                     ]
13543                 };
13544
13545                 var ctr = _this.el.select('.fc-event-container',true).first();
13546                 var cg = ctr.createChild(cfg);
13547
13548                 var sbox = c.select('.fc-day-content',true).first().getBox();
13549                 var ebox = c.select('.fc-day-content',true).first().getBox();
13550                 //Roo.log(cg);
13551                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13552                 cg.setWidth(ebox.right - sbox.x -2);
13553
13554                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13555                 
13556             }
13557             
13558         });
13559         
13560         
13561         
13562     },
13563     
13564     onEventEnter: function (e, el,event,d) {
13565         this.fireEvent('evententer', this, el, event);
13566     },
13567     
13568     onEventLeave: function (e, el,event,d) {
13569         this.fireEvent('eventleave', this, el, event);
13570     },
13571     
13572     onEventClick: function (e, el,event,d) {
13573         this.fireEvent('eventclick', this, el, event);
13574     },
13575     
13576     onMonthChange: function () {
13577         this.store.load();
13578     },
13579     
13580     onMoreEventClick: function(e, el, more)
13581     {
13582         var _this = this;
13583         
13584         this.calpopover.placement = 'right';
13585         this.calpopover.setTitle('More');
13586         
13587         this.calpopover.setContent('');
13588         
13589         var ctr = this.calpopover.el.select('.popover-content', true).first();
13590         
13591         Roo.each(more, function(m){
13592             var cfg = {
13593                 cls : 'fc-event-hori fc-event-draggable',
13594                 html : m.title
13595             }
13596             var cg = ctr.createChild(cfg);
13597             
13598             cg.on('click', _this.onEventClick, _this, m);
13599         });
13600         
13601         this.calpopover.show(el);
13602         
13603         
13604     },
13605     
13606     onLoad: function () 
13607     {   
13608         this.calevents = [];
13609         var cal = this;
13610         
13611         if(this.store.getCount() > 0){
13612             this.store.data.each(function(d){
13613                cal.addItem({
13614                     id : d.data.id,
13615                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13616                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13617                     time : d.data.start_time,
13618                     title : d.data.title,
13619                     description : d.data.description,
13620                     venue : d.data.venue
13621                 });
13622             });
13623         }
13624         
13625         this.renderEvents();
13626         
13627         if(this.calevents.length && this.loadMask){
13628             this.maskEl.hide();
13629         }
13630     },
13631     
13632     onBeforeLoad: function()
13633     {
13634         this.clearEvents();
13635         if(this.loadMask){
13636             this.maskEl.show();
13637         }
13638     }
13639 });
13640
13641  
13642  /*
13643  * - LGPL
13644  *
13645  * element
13646  * 
13647  */
13648
13649 /**
13650  * @class Roo.bootstrap.Popover
13651  * @extends Roo.bootstrap.Component
13652  * Bootstrap Popover class
13653  * @cfg {String} html contents of the popover   (or false to use children..)
13654  * @cfg {String} title of popover (or false to hide)
13655  * @cfg {String} placement how it is placed
13656  * @cfg {String} trigger click || hover (or false to trigger manually)
13657  * @cfg {String} over what (parent or false to trigger manually.)
13658  * @cfg {Number} delay - delay before showing
13659  
13660  * @constructor
13661  * Create a new Popover
13662  * @param {Object} config The config object
13663  */
13664
13665 Roo.bootstrap.Popover = function(config){
13666     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13667 };
13668
13669 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13670     
13671     title: 'Fill in a title',
13672     html: false,
13673     
13674     placement : 'right',
13675     trigger : 'hover', // hover
13676     
13677     delay : 0,
13678     
13679     over: 'parent',
13680     
13681     can_build_overlaid : false,
13682     
13683     getChildContainer : function()
13684     {
13685         return this.el.select('.popover-content',true).first();
13686     },
13687     
13688     getAutoCreate : function(){
13689          Roo.log('make popover?');
13690         var cfg = {
13691            cls : 'popover roo-dynamic',
13692            style: 'display:block',
13693            cn : [
13694                 {
13695                     cls : 'arrow'
13696                 },
13697                 {
13698                     cls : 'popover-inner',
13699                     cn : [
13700                         {
13701                             tag: 'h3',
13702                             cls: 'popover-title',
13703                             html : this.title
13704                         },
13705                         {
13706                             cls : 'popover-content',
13707                             html : this.html
13708                         }
13709                     ]
13710                     
13711                 }
13712            ]
13713         };
13714         
13715         return cfg;
13716     },
13717     setTitle: function(str)
13718     {
13719         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13720     },
13721     setContent: function(str)
13722     {
13723         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13724     },
13725     // as it get's added to the bottom of the page.
13726     onRender : function(ct, position)
13727     {
13728         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13729         if(!this.el){
13730             var cfg = Roo.apply({},  this.getAutoCreate());
13731             cfg.id = Roo.id();
13732             
13733             if (this.cls) {
13734                 cfg.cls += ' ' + this.cls;
13735             }
13736             if (this.style) {
13737                 cfg.style = this.style;
13738             }
13739             Roo.log("adding to ")
13740             this.el = Roo.get(document.body).createChild(cfg, position);
13741             Roo.log(this.el);
13742         }
13743         this.initEvents();
13744     },
13745     
13746     initEvents : function()
13747     {
13748         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13749         this.el.enableDisplayMode('block');
13750         this.el.hide();
13751         if (this.over === false) {
13752             return; 
13753         }
13754         if (this.triggers === false) {
13755             return;
13756         }
13757         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13758         var triggers = this.trigger ? this.trigger.split(' ') : [];
13759         Roo.each(triggers, function(trigger) {
13760         
13761             if (trigger == 'click') {
13762                 on_el.on('click', this.toggle, this);
13763             } else if (trigger != 'manual') {
13764                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13765                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13766       
13767                 on_el.on(eventIn  ,this.enter, this);
13768                 on_el.on(eventOut, this.leave, this);
13769             }
13770         }, this);
13771         
13772     },
13773     
13774     
13775     // private
13776     timeout : null,
13777     hoverState : null,
13778     
13779     toggle : function () {
13780         this.hoverState == 'in' ? this.leave() : this.enter();
13781     },
13782     
13783     enter : function () {
13784        
13785     
13786         clearTimeout(this.timeout);
13787     
13788         this.hoverState = 'in'
13789     
13790         if (!this.delay || !this.delay.show) {
13791             this.show();
13792             return 
13793         }
13794         var _t = this;
13795         this.timeout = setTimeout(function () {
13796             if (_t.hoverState == 'in') {
13797                 _t.show();
13798             }
13799         }, this.delay.show)
13800     },
13801     leave : function() {
13802         clearTimeout(this.timeout);
13803     
13804         this.hoverState = 'out'
13805     
13806         if (!this.delay || !this.delay.hide) {
13807             this.hide();
13808             return 
13809         }
13810         var _t = this;
13811         this.timeout = setTimeout(function () {
13812             if (_t.hoverState == 'out') {
13813                 _t.hide();
13814             }
13815         }, this.delay.hide)
13816     },
13817     
13818     show : function (on_el)
13819     {
13820         if (!on_el) {
13821             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13822         }
13823         // set content.
13824         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13825         if (this.html !== false) {
13826             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13827         }
13828         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13829         if (!this.title.length) {
13830             this.el.select('.popover-title',true).hide();
13831         }
13832         
13833         var placement = typeof this.placement == 'function' ?
13834             this.placement.call(this, this.el, on_el) :
13835             this.placement;
13836             
13837         var autoToken = /\s?auto?\s?/i;
13838         var autoPlace = autoToken.test(placement);
13839         if (autoPlace) {
13840             placement = placement.replace(autoToken, '') || 'top';
13841         }
13842         
13843         //this.el.detach()
13844         //this.el.setXY([0,0]);
13845         this.el.show();
13846         this.el.dom.style.display='block';
13847         this.el.addClass(placement);
13848         
13849         //this.el.appendTo(on_el);
13850         
13851         var p = this.getPosition();
13852         var box = this.el.getBox();
13853         
13854         if (autoPlace) {
13855             // fixme..
13856         }
13857         var align = Roo.bootstrap.Popover.alignment[placement]
13858         this.el.alignTo(on_el, align[0],align[1]);
13859         //var arrow = this.el.select('.arrow',true).first();
13860         //arrow.set(align[2], 
13861         
13862         this.el.addClass('in');
13863         this.hoverState = null;
13864         
13865         if (this.el.hasClass('fade')) {
13866             // fade it?
13867         }
13868         
13869     },
13870     hide : function()
13871     {
13872         this.el.setXY([0,0]);
13873         this.el.removeClass('in');
13874         this.el.hide();
13875         
13876     }
13877     
13878 });
13879
13880 Roo.bootstrap.Popover.alignment = {
13881     'left' : ['r-l', [-10,0], 'right'],
13882     'right' : ['l-r', [10,0], 'left'],
13883     'bottom' : ['t-b', [0,10], 'top'],
13884     'top' : [ 'b-t', [0,-10], 'bottom']
13885 };
13886
13887  /*
13888  * - LGPL
13889  *
13890  * Progress
13891  * 
13892  */
13893
13894 /**
13895  * @class Roo.bootstrap.Progress
13896  * @extends Roo.bootstrap.Component
13897  * Bootstrap Progress class
13898  * @cfg {Boolean} striped striped of the progress bar
13899  * @cfg {Boolean} active animated of the progress bar
13900  * 
13901  * 
13902  * @constructor
13903  * Create a new Progress
13904  * @param {Object} config The config object
13905  */
13906
13907 Roo.bootstrap.Progress = function(config){
13908     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13909 };
13910
13911 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13912     
13913     striped : false,
13914     active: false,
13915     
13916     getAutoCreate : function(){
13917         var cfg = {
13918             tag: 'div',
13919             cls: 'progress'
13920         };
13921         
13922         
13923         if(this.striped){
13924             cfg.cls += ' progress-striped';
13925         }
13926       
13927         if(this.active){
13928             cfg.cls += ' active';
13929         }
13930         
13931         
13932         return cfg;
13933     }
13934    
13935 });
13936
13937  
13938
13939  /*
13940  * - LGPL
13941  *
13942  * ProgressBar
13943  * 
13944  */
13945
13946 /**
13947  * @class Roo.bootstrap.ProgressBar
13948  * @extends Roo.bootstrap.Component
13949  * Bootstrap ProgressBar class
13950  * @cfg {Number} aria_valuenow aria-value now
13951  * @cfg {Number} aria_valuemin aria-value min
13952  * @cfg {Number} aria_valuemax aria-value max
13953  * @cfg {String} label label for the progress bar
13954  * @cfg {String} panel (success | info | warning | danger )
13955  * @cfg {String} role role of the progress bar
13956  * @cfg {String} sr_only text
13957  * 
13958  * 
13959  * @constructor
13960  * Create a new ProgressBar
13961  * @param {Object} config The config object
13962  */
13963
13964 Roo.bootstrap.ProgressBar = function(config){
13965     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13966 };
13967
13968 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13969     
13970     aria_valuenow : 0,
13971     aria_valuemin : 0,
13972     aria_valuemax : 100,
13973     label : false,
13974     panel : false,
13975     role : false,
13976     sr_only: false,
13977     
13978     getAutoCreate : function()
13979     {
13980         
13981         var cfg = {
13982             tag: 'div',
13983             cls: 'progress-bar',
13984             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13985         };
13986         
13987         if(this.sr_only){
13988             cfg.cn = {
13989                 tag: 'span',
13990                 cls: 'sr-only',
13991                 html: this.sr_only
13992             }
13993         }
13994         
13995         if(this.role){
13996             cfg.role = this.role;
13997         }
13998         
13999         if(this.aria_valuenow){
14000             cfg['aria-valuenow'] = this.aria_valuenow;
14001         }
14002         
14003         if(this.aria_valuemin){
14004             cfg['aria-valuemin'] = this.aria_valuemin;
14005         }
14006         
14007         if(this.aria_valuemax){
14008             cfg['aria-valuemax'] = this.aria_valuemax;
14009         }
14010         
14011         if(this.label && !this.sr_only){
14012             cfg.html = this.label;
14013         }
14014         
14015         if(this.panel){
14016             cfg.cls += ' progress-bar-' + this.panel;
14017         }
14018         
14019         return cfg;
14020     },
14021     
14022     update : function(aria_valuenow)
14023     {
14024         this.aria_valuenow = aria_valuenow;
14025         
14026         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14027     }
14028    
14029 });
14030
14031  
14032
14033  /*
14034  * - LGPL
14035  *
14036  * column
14037  * 
14038  */
14039
14040 /**
14041  * @class Roo.bootstrap.TabGroup
14042  * @extends Roo.bootstrap.Column
14043  * Bootstrap Column class
14044  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14045  * @cfg {Boolean} carousel true to make the group behave like a carousel
14046  * 
14047  * @constructor
14048  * Create a new TabGroup
14049  * @param {Object} config The config object
14050  */
14051
14052 Roo.bootstrap.TabGroup = function(config){
14053     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14054     if (!this.navId) {
14055         this.navId = Roo.id();
14056     }
14057     this.tabs = [];
14058     Roo.bootstrap.TabGroup.register(this);
14059     
14060 };
14061
14062 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14063     
14064     carousel : false,
14065     transition : false,
14066      
14067     getAutoCreate : function()
14068     {
14069         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14070         
14071         cfg.cls += ' tab-content';
14072         
14073         if (this.carousel) {
14074             cfg.cls += ' carousel slide';
14075             cfg.cn = [{
14076                cls : 'carousel-inner'
14077             }]
14078         }
14079         
14080         
14081         return cfg;
14082     },
14083     getChildContainer : function()
14084     {
14085         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14086     },
14087     
14088     /**
14089     * register a Navigation item
14090     * @param {Roo.bootstrap.NavItem} the navitem to add
14091     */
14092     register : function(item)
14093     {
14094         this.tabs.push( item);
14095         item.navId = this.navId; // not really needed..
14096     
14097     },
14098     
14099     getActivePanel : function()
14100     {
14101         var r = false;
14102         Roo.each(this.tabs, function(t) {
14103             if (t.active) {
14104                 r = t;
14105                 return false;
14106             }
14107             return null;
14108         });
14109         return r;
14110         
14111     },
14112     getPanelByName : function(n)
14113     {
14114         var r = false;
14115         Roo.each(this.tabs, function(t) {
14116             if (t.tabId == n) {
14117                 r = t;
14118                 return false;
14119             }
14120             return null;
14121         });
14122         return r;
14123     },
14124     indexOfPanel : function(p)
14125     {
14126         var r = false;
14127         Roo.each(this.tabs, function(t,i) {
14128             if (t.tabId == p.tabId) {
14129                 r = i;
14130                 return false;
14131             }
14132             return null;
14133         });
14134         return r;
14135     },
14136     /**
14137      * show a specific panel
14138      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14139      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14140      */
14141     showPanel : function (pan)
14142     {
14143         
14144         if (typeof(pan) == 'number') {
14145             pan = this.tabs[pan];
14146         }
14147         if (typeof(pan) == 'string') {
14148             pan = this.getPanelByName(pan);
14149         }
14150         if (pan.tabId == this.getActivePanel().tabId) {
14151             return true;
14152         }
14153         var cur = this.getActivePanel();
14154         
14155         if (false === cur.fireEvent('beforedeactivate')) {
14156             return false;
14157         }
14158         
14159         if (this.carousel) {
14160             this.transition = true;
14161             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14162             var lr = dir == 'next' ? 'left' : 'right';
14163             pan.el.addClass(dir); // or prev
14164             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14165             cur.el.addClass(lr); // or right
14166             pan.el.addClass(lr);
14167             
14168             var _this = this;
14169             cur.el.on('transitionend', function() {
14170                 Roo.log("trans end?");
14171                 
14172                 pan.el.removeClass([lr,dir]);
14173                 pan.setActive(true);
14174                 
14175                 cur.el.removeClass([lr]);
14176                 cur.setActive(false);
14177                 
14178                 _this.transition = false;
14179                 
14180             }, this, { single:  true } );
14181             return true;
14182         }
14183         
14184         cur.setActive(false);
14185         pan.setActive(true);
14186         return true;
14187         
14188     },
14189     showPanelNext : function()
14190     {
14191         var i = this.indexOfPanel(this.getActivePanel());
14192         if (i > this.tabs.length) {
14193             return;
14194         }
14195         this.showPanel(this.tabs[i+1]);
14196     },
14197     showPanelPrev : function()
14198     {
14199         var i = this.indexOfPanel(this.getActivePanel());
14200         if (i  < 1) {
14201             return;
14202         }
14203         this.showPanel(this.tabs[i-1]);
14204     }
14205     
14206     
14207   
14208 });
14209
14210  
14211
14212  
14213  
14214 Roo.apply(Roo.bootstrap.TabGroup, {
14215     
14216     groups: {},
14217      /**
14218     * register a Navigation Group
14219     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14220     */
14221     register : function(navgrp)
14222     {
14223         this.groups[navgrp.navId] = navgrp;
14224         
14225     },
14226     /**
14227     * fetch a Navigation Group based on the navigation ID
14228     * if one does not exist , it will get created.
14229     * @param {string} the navgroup to add
14230     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14231     */
14232     get: function(navId) {
14233         if (typeof(this.groups[navId]) == 'undefined') {
14234             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14235         }
14236         return this.groups[navId] ;
14237     }
14238     
14239     
14240     
14241 });
14242
14243  /*
14244  * - LGPL
14245  *
14246  * TabPanel
14247  * 
14248  */
14249
14250 /**
14251  * @class Roo.bootstrap.TabPanel
14252  * @extends Roo.bootstrap.Component
14253  * Bootstrap TabPanel class
14254  * @cfg {Boolean} active panel active
14255  * @cfg {String} html panel content
14256  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14257  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14258  * 
14259  * 
14260  * @constructor
14261  * Create a new TabPanel
14262  * @param {Object} config The config object
14263  */
14264
14265 Roo.bootstrap.TabPanel = function(config){
14266     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14267     this.addEvents({
14268         /**
14269              * @event changed
14270              * Fires when the active status changes
14271              * @param {Roo.bootstrap.TabPanel} this
14272              * @param {Boolean} state the new state
14273             
14274          */
14275         'changed': true,
14276         /**
14277              * @event beforedeactivate
14278              * Fires before a tab is de-activated - can be used to do validation on a form.
14279              * @param {Roo.bootstrap.TabPanel} this
14280              * @return {Boolean} false if there is an error
14281             
14282          */
14283         'beforedeactivate': true
14284      });
14285     
14286     this.tabId = this.tabId || Roo.id();
14287   
14288 };
14289
14290 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14291     
14292     active: false,
14293     html: false,
14294     tabId: false,
14295     navId : false,
14296     
14297     getAutoCreate : function(){
14298         var cfg = {
14299             tag: 'div',
14300             // item is needed for carousel - not sure if it has any effect otherwise
14301             cls: 'tab-pane item',
14302             html: this.html || ''
14303         };
14304         
14305         if(this.active){
14306             cfg.cls += ' active';
14307         }
14308         
14309         if(this.tabId){
14310             cfg.tabId = this.tabId;
14311         }
14312         
14313         
14314         return cfg;
14315     },
14316     
14317     initEvents:  function()
14318     {
14319         Roo.log('-------- init events on tab panel ---------');
14320         
14321         var p = this.parent();
14322         this.navId = this.navId || p.navId;
14323         
14324         if (typeof(this.navId) != 'undefined') {
14325             // not really needed.. but just in case.. parent should be a NavGroup.
14326             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14327             Roo.log(['register', tg, this]);
14328             tg.register(this);
14329         }
14330     },
14331     
14332     
14333     onRender : function(ct, position)
14334     {
14335        // Roo.log("Call onRender: " + this.xtype);
14336         
14337         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14338         
14339         
14340         
14341         
14342         
14343     },
14344     
14345     setActive: function(state)
14346     {
14347         Roo.log("panel - set active " + this.tabId + "=" + state);
14348         
14349         this.active = state;
14350         if (!state) {
14351             this.el.removeClass('active');
14352             
14353         } else  if (!this.el.hasClass('active')) {
14354             this.el.addClass('active');
14355         }
14356         this.fireEvent('changed', this, state);
14357     }
14358     
14359     
14360 });
14361  
14362
14363  
14364
14365  /*
14366  * - LGPL
14367  *
14368  * DateField
14369  * 
14370  */
14371
14372 /**
14373  * @class Roo.bootstrap.DateField
14374  * @extends Roo.bootstrap.Input
14375  * Bootstrap DateField class
14376  * @cfg {Number} weekStart default 0
14377  * @cfg {Number} weekStart default 0
14378  * @cfg {Number} viewMode default empty, (months|years)
14379  * @cfg {Number} minViewMode default empty, (months|years)
14380  * @cfg {Number} startDate default -Infinity
14381  * @cfg {Number} endDate default Infinity
14382  * @cfg {Boolean} todayHighlight default false
14383  * @cfg {Boolean} todayBtn default false
14384  * @cfg {Boolean} calendarWeeks default false
14385  * @cfg {Object} daysOfWeekDisabled default empty
14386  * 
14387  * @cfg {Boolean} keyboardNavigation default true
14388  * @cfg {String} language default en
14389  * 
14390  * @constructor
14391  * Create a new DateField
14392  * @param {Object} config The config object
14393  */
14394
14395 Roo.bootstrap.DateField = function(config){
14396     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14397      this.addEvents({
14398             /**
14399              * @event show
14400              * Fires when this field show.
14401              * @param {Roo.bootstrap.DateField} this
14402              * @param {Mixed} date The date value
14403              */
14404             show : true,
14405             /**
14406              * @event show
14407              * Fires when this field hide.
14408              * @param {Roo.bootstrap.DateField} this
14409              * @param {Mixed} date The date value
14410              */
14411             hide : true,
14412             /**
14413              * @event select
14414              * Fires when select a date.
14415              * @param {Roo.bootstrap.DateField} this
14416              * @param {Mixed} date The date value
14417              */
14418             select : true
14419         });
14420 };
14421
14422 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14423     
14424     /**
14425      * @cfg {String} format
14426      * The default date format string which can be overriden for localization support.  The format must be
14427      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14428      */
14429     format : "m/d/y",
14430     /**
14431      * @cfg {String} altFormats
14432      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14433      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14434      */
14435     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14436     
14437     weekStart : 0,
14438     
14439     viewMode : '',
14440     
14441     minViewMode : '',
14442     
14443     todayHighlight : false,
14444     
14445     todayBtn: false,
14446     
14447     language: 'en',
14448     
14449     keyboardNavigation: true,
14450     
14451     calendarWeeks: false,
14452     
14453     startDate: -Infinity,
14454     
14455     endDate: Infinity,
14456     
14457     daysOfWeekDisabled: [],
14458     
14459     _events: [],
14460     
14461     UTCDate: function()
14462     {
14463         return new Date(Date.UTC.apply(Date, arguments));
14464     },
14465     
14466     UTCToday: function()
14467     {
14468         var today = new Date();
14469         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14470     },
14471     
14472     getDate: function() {
14473             var d = this.getUTCDate();
14474             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14475     },
14476     
14477     getUTCDate: function() {
14478             return this.date;
14479     },
14480     
14481     setDate: function(d) {
14482             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14483     },
14484     
14485     setUTCDate: function(d) {
14486             this.date = d;
14487             this.setValue(this.formatDate(this.date));
14488     },
14489         
14490     onRender: function(ct, position)
14491     {
14492         
14493         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14494         
14495         this.language = this.language || 'en';
14496         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14497         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14498         
14499         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14500         this.format = this.format || 'm/d/y';
14501         this.isInline = false;
14502         this.isInput = true;
14503         this.component = this.el.select('.add-on', true).first() || false;
14504         this.component = (this.component && this.component.length === 0) ? false : this.component;
14505         this.hasInput = this.component && this.inputEL().length;
14506         
14507         if (typeof(this.minViewMode === 'string')) {
14508             switch (this.minViewMode) {
14509                 case 'months':
14510                     this.minViewMode = 1;
14511                     break;
14512                 case 'years':
14513                     this.minViewMode = 2;
14514                     break;
14515                 default:
14516                     this.minViewMode = 0;
14517                     break;
14518             }
14519         }
14520         
14521         if (typeof(this.viewMode === 'string')) {
14522             switch (this.viewMode) {
14523                 case 'months':
14524                     this.viewMode = 1;
14525                     break;
14526                 case 'years':
14527                     this.viewMode = 2;
14528                     break;
14529                 default:
14530                     this.viewMode = 0;
14531                     break;
14532             }
14533         }
14534                 
14535         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14536         
14537 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14538         
14539         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14540         
14541         this.picker().on('mousedown', this.onMousedown, this);
14542         this.picker().on('click', this.onClick, this);
14543         
14544         this.picker().addClass('datepicker-dropdown');
14545         
14546         this.startViewMode = this.viewMode;
14547         
14548         
14549         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14550             if(!this.calendarWeeks){
14551                 v.remove();
14552                 return;
14553             };
14554             
14555             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14556             v.attr('colspan', function(i, val){
14557                 return parseInt(val) + 1;
14558             });
14559         })
14560                         
14561         
14562         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14563         
14564         this.setStartDate(this.startDate);
14565         this.setEndDate(this.endDate);
14566         
14567         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14568         
14569         this.fillDow();
14570         this.fillMonths();
14571         this.update();
14572         this.showMode();
14573         
14574         if(this.isInline) {
14575             this.show();
14576         }
14577     },
14578     
14579     picker : function()
14580     {
14581         return this.pickerEl;
14582 //        return this.el.select('.datepicker', true).first();
14583     },
14584     
14585     fillDow: function()
14586     {
14587         var dowCnt = this.weekStart;
14588         
14589         var dow = {
14590             tag: 'tr',
14591             cn: [
14592                 
14593             ]
14594         };
14595         
14596         if(this.calendarWeeks){
14597             dow.cn.push({
14598                 tag: 'th',
14599                 cls: 'cw',
14600                 html: '&nbsp;'
14601             })
14602         }
14603         
14604         while (dowCnt < this.weekStart + 7) {
14605             dow.cn.push({
14606                 tag: 'th',
14607                 cls: 'dow',
14608                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14609             });
14610         }
14611         
14612         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14613     },
14614     
14615     fillMonths: function()
14616     {    
14617         var i = 0
14618         var months = this.picker().select('>.datepicker-months td', true).first();
14619         
14620         months.dom.innerHTML = '';
14621         
14622         while (i < 12) {
14623             var month = {
14624                 tag: 'span',
14625                 cls: 'month',
14626                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14627             }
14628             
14629             months.createChild(month);
14630         }
14631         
14632     },
14633     
14634     update: function()
14635     {
14636         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;
14637         
14638         if (this.date < this.startDate) {
14639             this.viewDate = new Date(this.startDate);
14640         } else if (this.date > this.endDate) {
14641             this.viewDate = new Date(this.endDate);
14642         } else {
14643             this.viewDate = new Date(this.date);
14644         }
14645         
14646         this.fill();
14647     },
14648     
14649     fill: function() 
14650     {
14651         var d = new Date(this.viewDate),
14652                 year = d.getUTCFullYear(),
14653                 month = d.getUTCMonth(),
14654                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14655                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14656                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14657                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14658                 currentDate = this.date && this.date.valueOf(),
14659                 today = this.UTCToday();
14660         
14661         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14662         
14663 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14664         
14665 //        this.picker.select('>tfoot th.today').
14666 //                                              .text(dates[this.language].today)
14667 //                                              .toggle(this.todayBtn !== false);
14668     
14669         this.updateNavArrows();
14670         this.fillMonths();
14671                                                 
14672         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14673         
14674         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14675          
14676         prevMonth.setUTCDate(day);
14677         
14678         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14679         
14680         var nextMonth = new Date(prevMonth);
14681         
14682         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14683         
14684         nextMonth = nextMonth.valueOf();
14685         
14686         var fillMonths = false;
14687         
14688         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14689         
14690         while(prevMonth.valueOf() < nextMonth) {
14691             var clsName = '';
14692             
14693             if (prevMonth.getUTCDay() === this.weekStart) {
14694                 if(fillMonths){
14695                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14696                 }
14697                     
14698                 fillMonths = {
14699                     tag: 'tr',
14700                     cn: []
14701                 };
14702                 
14703                 if(this.calendarWeeks){
14704                     // ISO 8601: First week contains first thursday.
14705                     // ISO also states week starts on Monday, but we can be more abstract here.
14706                     var
14707                     // Start of current week: based on weekstart/current date
14708                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14709                     // Thursday of this week
14710                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14711                     // First Thursday of year, year from thursday
14712                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14713                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14714                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14715                     
14716                     fillMonths.cn.push({
14717                         tag: 'td',
14718                         cls: 'cw',
14719                         html: calWeek
14720                     });
14721                 }
14722             }
14723             
14724             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14725                 clsName += ' old';
14726             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14727                 clsName += ' new';
14728             }
14729             if (this.todayHighlight &&
14730                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14731                 prevMonth.getUTCMonth() == today.getMonth() &&
14732                 prevMonth.getUTCDate() == today.getDate()) {
14733                 clsName += ' today';
14734             }
14735             
14736             if (currentDate && prevMonth.valueOf() === currentDate) {
14737                 clsName += ' active';
14738             }
14739             
14740             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14741                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14742                     clsName += ' disabled';
14743             }
14744             
14745             fillMonths.cn.push({
14746                 tag: 'td',
14747                 cls: 'day ' + clsName,
14748                 html: prevMonth.getDate()
14749             })
14750             
14751             prevMonth.setDate(prevMonth.getDate()+1);
14752         }
14753           
14754         var currentYear = this.date && this.date.getUTCFullYear();
14755         var currentMonth = this.date && this.date.getUTCMonth();
14756         
14757         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14758         
14759         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14760             v.removeClass('active');
14761             
14762             if(currentYear === year && k === currentMonth){
14763                 v.addClass('active');
14764             }
14765             
14766             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14767                 v.addClass('disabled');
14768             }
14769             
14770         });
14771         
14772         
14773         year = parseInt(year/10, 10) * 10;
14774         
14775         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14776         
14777         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14778         
14779         year -= 1;
14780         for (var i = -1; i < 11; i++) {
14781             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14782                 tag: 'span',
14783                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14784                 html: year
14785             })
14786             
14787             year += 1;
14788         }
14789     },
14790     
14791     showMode: function(dir) 
14792     {
14793         if (dir) {
14794             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14795         }
14796         Roo.each(this.picker().select('>div',true).elements, function(v){
14797             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14798             v.hide();
14799         });
14800         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14801     },
14802     
14803     place: function()
14804     {
14805         if(this.isInline) return;
14806         
14807         this.picker().removeClass(['bottom', 'top']);
14808         
14809         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14810             /*
14811              * place to the top of element!
14812              *
14813              */
14814             
14815             this.picker().addClass('top');
14816             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14817             
14818             return;
14819         }
14820         
14821         this.picker().addClass('bottom');
14822         
14823         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14824     },
14825     
14826     parseDate : function(value)
14827     {
14828         if(!value || value instanceof Date){
14829             return value;
14830         }
14831         var v = Date.parseDate(value, this.format);
14832         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14833             v = Date.parseDate(value, 'Y-m-d');
14834         }
14835         if(!v && this.altFormats){
14836             if(!this.altFormatsArray){
14837                 this.altFormatsArray = this.altFormats.split("|");
14838             }
14839             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14840                 v = Date.parseDate(value, this.altFormatsArray[i]);
14841             }
14842         }
14843         return v;
14844     },
14845     
14846     formatDate : function(date, fmt)
14847     {
14848         return (!date || !(date instanceof Date)) ?
14849         date : date.dateFormat(fmt || this.format);
14850     },
14851     
14852     onFocus : function()
14853     {
14854         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14855         this.show();
14856     },
14857     
14858     onBlur : function()
14859     {
14860         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14861         
14862         var d = this.inputEl().getValue();
14863         
14864         this.setValue(d);
14865                 
14866         this.hide();
14867     },
14868     
14869     show : function()
14870     {
14871         this.picker().show();
14872         this.update();
14873         this.place();
14874         
14875         this.fireEvent('show', this, this.date);
14876     },
14877     
14878     hide : function()
14879     {
14880         if(this.isInline) return;
14881         this.picker().hide();
14882         this.viewMode = this.startViewMode;
14883         this.showMode();
14884         
14885         this.fireEvent('hide', this, this.date);
14886         
14887     },
14888     
14889     onMousedown: function(e)
14890     {
14891         e.stopPropagation();
14892         e.preventDefault();
14893     },
14894     
14895     keyup: function(e)
14896     {
14897         Roo.bootstrap.DateField.superclass.keyup.call(this);
14898         this.update();
14899     },
14900
14901     setValue: function(v)
14902     {
14903         
14904         // v can be a string or a date..
14905         
14906         
14907         var d = new Date(this.parseDate(v) ).clearTime();
14908         
14909         Roo.log(d);
14910         Roo.log(d);
14911         if(isNaN(d.getTime())){
14912             this.date = this.viewDate = '';
14913             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14914             return;
14915         }
14916         
14917         v = this.formatDate(d);
14918         
14919         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14920         
14921         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14922      
14923         this.update();
14924
14925         this.fireEvent('select', this, this.date);
14926         
14927     },
14928     
14929     getValue: function()
14930     {
14931         return this.formatDate(this.date);
14932     },
14933     
14934     fireKey: function(e)
14935     {
14936         if (!this.picker().isVisible()){
14937             if (e.keyCode == 27) // allow escape to hide and re-show picker
14938                 this.show();
14939             return;
14940         }
14941         
14942         var dateChanged = false,
14943         dir, day, month,
14944         newDate, newViewDate;
14945         
14946         switch(e.keyCode){
14947             case 27: // escape
14948                 this.hide();
14949                 e.preventDefault();
14950                 break;
14951             case 37: // left
14952             case 39: // right
14953                 if (!this.keyboardNavigation) break;
14954                 dir = e.keyCode == 37 ? -1 : 1;
14955                 
14956                 if (e.ctrlKey){
14957                     newDate = this.moveYear(this.date, dir);
14958                     newViewDate = this.moveYear(this.viewDate, dir);
14959                 } else if (e.shiftKey){
14960                     newDate = this.moveMonth(this.date, dir);
14961                     newViewDate = this.moveMonth(this.viewDate, dir);
14962                 } else {
14963                     newDate = new Date(this.date);
14964                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14965                     newViewDate = new Date(this.viewDate);
14966                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14967                 }
14968                 if (this.dateWithinRange(newDate)){
14969                     this.date = newDate;
14970                     this.viewDate = newViewDate;
14971                     this.setValue(this.formatDate(this.date));
14972 //                    this.update();
14973                     e.preventDefault();
14974                     dateChanged = true;
14975                 }
14976                 break;
14977             case 38: // up
14978             case 40: // down
14979                 if (!this.keyboardNavigation) break;
14980                 dir = e.keyCode == 38 ? -1 : 1;
14981                 if (e.ctrlKey){
14982                     newDate = this.moveYear(this.date, dir);
14983                     newViewDate = this.moveYear(this.viewDate, dir);
14984                 } else if (e.shiftKey){
14985                     newDate = this.moveMonth(this.date, dir);
14986                     newViewDate = this.moveMonth(this.viewDate, dir);
14987                 } else {
14988                     newDate = new Date(this.date);
14989                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14990                     newViewDate = new Date(this.viewDate);
14991                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14992                 }
14993                 if (this.dateWithinRange(newDate)){
14994                     this.date = newDate;
14995                     this.viewDate = newViewDate;
14996                     this.setValue(this.formatDate(this.date));
14997 //                    this.update();
14998                     e.preventDefault();
14999                     dateChanged = true;
15000                 }
15001                 break;
15002             case 13: // enter
15003                 this.setValue(this.formatDate(this.date));
15004                 this.hide();
15005                 e.preventDefault();
15006                 break;
15007             case 9: // tab
15008                 this.setValue(this.formatDate(this.date));
15009                 this.hide();
15010                 break;
15011             case 16: // shift
15012             case 17: // ctrl
15013             case 18: // alt
15014                 break;
15015             default :
15016                 this.hide();
15017                 
15018         }
15019     },
15020     
15021     
15022     onClick: function(e) 
15023     {
15024         e.stopPropagation();
15025         e.preventDefault();
15026         
15027         var target = e.getTarget();
15028         
15029         if(target.nodeName.toLowerCase() === 'i'){
15030             target = Roo.get(target).dom.parentNode;
15031         }
15032         
15033         var nodeName = target.nodeName;
15034         var className = target.className;
15035         var html = target.innerHTML;
15036         //Roo.log(nodeName);
15037         
15038         switch(nodeName.toLowerCase()) {
15039             case 'th':
15040                 switch(className) {
15041                     case 'switch':
15042                         this.showMode(1);
15043                         break;
15044                     case 'prev':
15045                     case 'next':
15046                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15047                         switch(this.viewMode){
15048                                 case 0:
15049                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15050                                         break;
15051                                 case 1:
15052                                 case 2:
15053                                         this.viewDate = this.moveYear(this.viewDate, dir);
15054                                         break;
15055                         }
15056                         this.fill();
15057                         break;
15058                     case 'today':
15059                         var date = new Date();
15060                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15061 //                        this.fill()
15062                         this.setValue(this.formatDate(this.date));
15063                         
15064                         this.hide();
15065                         break;
15066                 }
15067                 break;
15068             case 'span':
15069                 if (className.indexOf('disabled') < 0) {
15070                     this.viewDate.setUTCDate(1);
15071                     if (className.indexOf('month') > -1) {
15072                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15073                     } else {
15074                         var year = parseInt(html, 10) || 0;
15075                         this.viewDate.setUTCFullYear(year);
15076                         
15077                     }
15078                     this.showMode(-1);
15079                     this.fill();
15080                 }
15081                 break;
15082                 
15083             case 'td':
15084                 //Roo.log(className);
15085                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15086                     var day = parseInt(html, 10) || 1;
15087                     var year = this.viewDate.getUTCFullYear(),
15088                         month = this.viewDate.getUTCMonth();
15089
15090                     if (className.indexOf('old') > -1) {
15091                         if(month === 0 ){
15092                             month = 11;
15093                             year -= 1;
15094                         }else{
15095                             month -= 1;
15096                         }
15097                     } else if (className.indexOf('new') > -1) {
15098                         if (month == 11) {
15099                             month = 0;
15100                             year += 1;
15101                         } else {
15102                             month += 1;
15103                         }
15104                     }
15105                     //Roo.log([year,month,day]);
15106                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15107                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15108 //                    this.fill();
15109                     //Roo.log(this.formatDate(this.date));
15110                     this.setValue(this.formatDate(this.date));
15111                     this.hide();
15112                 }
15113                 break;
15114         }
15115     },
15116     
15117     setStartDate: function(startDate)
15118     {
15119         this.startDate = startDate || -Infinity;
15120         if (this.startDate !== -Infinity) {
15121             this.startDate = this.parseDate(this.startDate);
15122         }
15123         this.update();
15124         this.updateNavArrows();
15125     },
15126
15127     setEndDate: function(endDate)
15128     {
15129         this.endDate = endDate || Infinity;
15130         if (this.endDate !== Infinity) {
15131             this.endDate = this.parseDate(this.endDate);
15132         }
15133         this.update();
15134         this.updateNavArrows();
15135     },
15136     
15137     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15138     {
15139         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15140         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15141             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15142         }
15143         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15144             return parseInt(d, 10);
15145         });
15146         this.update();
15147         this.updateNavArrows();
15148     },
15149     
15150     updateNavArrows: function() 
15151     {
15152         var d = new Date(this.viewDate),
15153         year = d.getUTCFullYear(),
15154         month = d.getUTCMonth();
15155         
15156         Roo.each(this.picker().select('.prev', true).elements, function(v){
15157             v.show();
15158             switch (this.viewMode) {
15159                 case 0:
15160
15161                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15162                         v.hide();
15163                     }
15164                     break;
15165                 case 1:
15166                 case 2:
15167                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15168                         v.hide();
15169                     }
15170                     break;
15171             }
15172         });
15173         
15174         Roo.each(this.picker().select('.next', true).elements, function(v){
15175             v.show();
15176             switch (this.viewMode) {
15177                 case 0:
15178
15179                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15180                         v.hide();
15181                     }
15182                     break;
15183                 case 1:
15184                 case 2:
15185                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15186                         v.hide();
15187                     }
15188                     break;
15189             }
15190         })
15191     },
15192     
15193     moveMonth: function(date, dir)
15194     {
15195         if (!dir) return date;
15196         var new_date = new Date(date.valueOf()),
15197         day = new_date.getUTCDate(),
15198         month = new_date.getUTCMonth(),
15199         mag = Math.abs(dir),
15200         new_month, test;
15201         dir = dir > 0 ? 1 : -1;
15202         if (mag == 1){
15203             test = dir == -1
15204             // If going back one month, make sure month is not current month
15205             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15206             ? function(){
15207                 return new_date.getUTCMonth() == month;
15208             }
15209             // If going forward one month, make sure month is as expected
15210             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15211             : function(){
15212                 return new_date.getUTCMonth() != new_month;
15213             };
15214             new_month = month + dir;
15215             new_date.setUTCMonth(new_month);
15216             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15217             if (new_month < 0 || new_month > 11)
15218                 new_month = (new_month + 12) % 12;
15219         } else {
15220             // For magnitudes >1, move one month at a time...
15221             for (var i=0; i<mag; i++)
15222                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15223                 new_date = this.moveMonth(new_date, dir);
15224             // ...then reset the day, keeping it in the new month
15225             new_month = new_date.getUTCMonth();
15226             new_date.setUTCDate(day);
15227             test = function(){
15228                 return new_month != new_date.getUTCMonth();
15229             };
15230         }
15231         // Common date-resetting loop -- if date is beyond end of month, make it
15232         // end of month
15233         while (test()){
15234             new_date.setUTCDate(--day);
15235             new_date.setUTCMonth(new_month);
15236         }
15237         return new_date;
15238     },
15239
15240     moveYear: function(date, dir)
15241     {
15242         return this.moveMonth(date, dir*12);
15243     },
15244
15245     dateWithinRange: function(date)
15246     {
15247         return date >= this.startDate && date <= this.endDate;
15248     },
15249
15250     
15251     remove: function() 
15252     {
15253         this.picker().remove();
15254     }
15255    
15256 });
15257
15258 Roo.apply(Roo.bootstrap.DateField,  {
15259     
15260     head : {
15261         tag: 'thead',
15262         cn: [
15263         {
15264             tag: 'tr',
15265             cn: [
15266             {
15267                 tag: 'th',
15268                 cls: 'prev',
15269                 html: '<i class="fa fa-arrow-left"/>'
15270             },
15271             {
15272                 tag: 'th',
15273                 cls: 'switch',
15274                 colspan: '5'
15275             },
15276             {
15277                 tag: 'th',
15278                 cls: 'next',
15279                 html: '<i class="fa fa-arrow-right"/>'
15280             }
15281
15282             ]
15283         }
15284         ]
15285     },
15286     
15287     content : {
15288         tag: 'tbody',
15289         cn: [
15290         {
15291             tag: 'tr',
15292             cn: [
15293             {
15294                 tag: 'td',
15295                 colspan: '7'
15296             }
15297             ]
15298         }
15299         ]
15300     },
15301     
15302     footer : {
15303         tag: 'tfoot',
15304         cn: [
15305         {
15306             tag: 'tr',
15307             cn: [
15308             {
15309                 tag: 'th',
15310                 colspan: '7',
15311                 cls: 'today'
15312             }
15313                     
15314             ]
15315         }
15316         ]
15317     },
15318     
15319     dates:{
15320         en: {
15321             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15322             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15323             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15324             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15325             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15326             today: "Today"
15327         }
15328     },
15329     
15330     modes: [
15331     {
15332         clsName: 'days',
15333         navFnc: 'Month',
15334         navStep: 1
15335     },
15336     {
15337         clsName: 'months',
15338         navFnc: 'FullYear',
15339         navStep: 1
15340     },
15341     {
15342         clsName: 'years',
15343         navFnc: 'FullYear',
15344         navStep: 10
15345     }]
15346 });
15347
15348 Roo.apply(Roo.bootstrap.DateField,  {
15349   
15350     template : {
15351         tag: 'div',
15352         cls: 'datepicker dropdown-menu',
15353         cn: [
15354         {
15355             tag: 'div',
15356             cls: 'datepicker-days',
15357             cn: [
15358             {
15359                 tag: 'table',
15360                 cls: 'table-condensed',
15361                 cn:[
15362                 Roo.bootstrap.DateField.head,
15363                 {
15364                     tag: 'tbody'
15365                 },
15366                 Roo.bootstrap.DateField.footer
15367                 ]
15368             }
15369             ]
15370         },
15371         {
15372             tag: 'div',
15373             cls: 'datepicker-months',
15374             cn: [
15375             {
15376                 tag: 'table',
15377                 cls: 'table-condensed',
15378                 cn:[
15379                 Roo.bootstrap.DateField.head,
15380                 Roo.bootstrap.DateField.content,
15381                 Roo.bootstrap.DateField.footer
15382                 ]
15383             }
15384             ]
15385         },
15386         {
15387             tag: 'div',
15388             cls: 'datepicker-years',
15389             cn: [
15390             {
15391                 tag: 'table',
15392                 cls: 'table-condensed',
15393                 cn:[
15394                 Roo.bootstrap.DateField.head,
15395                 Roo.bootstrap.DateField.content,
15396                 Roo.bootstrap.DateField.footer
15397                 ]
15398             }
15399             ]
15400         }
15401         ]
15402     }
15403 });
15404
15405  
15406
15407  /*
15408  * - LGPL
15409  *
15410  * TimeField
15411  * 
15412  */
15413
15414 /**
15415  * @class Roo.bootstrap.TimeField
15416  * @extends Roo.bootstrap.Input
15417  * Bootstrap DateField class
15418  * 
15419  * 
15420  * @constructor
15421  * Create a new TimeField
15422  * @param {Object} config The config object
15423  */
15424
15425 Roo.bootstrap.TimeField = function(config){
15426     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15427     this.addEvents({
15428             /**
15429              * @event show
15430              * Fires when this field show.
15431              * @param {Roo.bootstrap.DateField} this
15432              * @param {Mixed} date The date value
15433              */
15434             show : true,
15435             /**
15436              * @event show
15437              * Fires when this field hide.
15438              * @param {Roo.bootstrap.DateField} this
15439              * @param {Mixed} date The date value
15440              */
15441             hide : true,
15442             /**
15443              * @event select
15444              * Fires when select a date.
15445              * @param {Roo.bootstrap.DateField} this
15446              * @param {Mixed} date The date value
15447              */
15448             select : true
15449         });
15450 };
15451
15452 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15453     
15454     /**
15455      * @cfg {String} format
15456      * The default time format string which can be overriden for localization support.  The format must be
15457      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15458      */
15459     format : "H:i",
15460        
15461     onRender: function(ct, position)
15462     {
15463         
15464         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15465                 
15466         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15467         
15468         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15469         
15470         this.pop = this.picker().select('>.datepicker-time',true).first();
15471         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15472         
15473         this.picker().on('mousedown', this.onMousedown, this);
15474         this.picker().on('click', this.onClick, this);
15475         
15476         this.picker().addClass('datepicker-dropdown');
15477     
15478         this.fillTime();
15479         this.update();
15480             
15481         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15482         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15483         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15484         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15485         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15486         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15487
15488     },
15489     
15490     fireKey: function(e){
15491         if (!this.picker().isVisible()){
15492             if (e.keyCode == 27) // allow escape to hide and re-show picker
15493                 this.show();
15494             return;
15495         }
15496
15497         e.preventDefault();
15498         
15499         switch(e.keyCode){
15500             case 27: // escape
15501                 this.hide();
15502                 break;
15503             case 37: // left
15504             case 39: // right
15505                 this.onTogglePeriod();
15506                 break;
15507             case 38: // up
15508                 this.onIncrementMinutes();
15509                 break;
15510             case 40: // down
15511                 this.onDecrementMinutes();
15512                 break;
15513             case 13: // enter
15514             case 9: // tab
15515                 this.setTime();
15516                 break;
15517         }
15518     },
15519     
15520     onClick: function(e) {
15521         e.stopPropagation();
15522         e.preventDefault();
15523     },
15524     
15525     picker : function()
15526     {
15527         return this.el.select('.datepicker', true).first();
15528     },
15529     
15530     fillTime: function()
15531     {    
15532         var time = this.pop.select('tbody', true).first();
15533         
15534         time.dom.innerHTML = '';
15535         
15536         time.createChild({
15537             tag: 'tr',
15538             cn: [
15539                 {
15540                     tag: 'td',
15541                     cn: [
15542                         {
15543                             tag: 'a',
15544                             href: '#',
15545                             cls: 'btn',
15546                             cn: [
15547                                 {
15548                                     tag: 'span',
15549                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15550                                 }
15551                             ]
15552                         } 
15553                     ]
15554                 },
15555                 {
15556                     tag: 'td',
15557                     cls: 'separator'
15558                 },
15559                 {
15560                     tag: 'td',
15561                     cn: [
15562                         {
15563                             tag: 'a',
15564                             href: '#',
15565                             cls: 'btn',
15566                             cn: [
15567                                 {
15568                                     tag: 'span',
15569                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15570                                 }
15571                             ]
15572                         }
15573                     ]
15574                 },
15575                 {
15576                     tag: 'td',
15577                     cls: 'separator'
15578                 }
15579             ]
15580         });
15581         
15582         time.createChild({
15583             tag: 'tr',
15584             cn: [
15585                 {
15586                     tag: 'td',
15587                     cn: [
15588                         {
15589                             tag: 'span',
15590                             cls: 'timepicker-hour',
15591                             html: '00'
15592                         }  
15593                     ]
15594                 },
15595                 {
15596                     tag: 'td',
15597                     cls: 'separator',
15598                     html: ':'
15599                 },
15600                 {
15601                     tag: 'td',
15602                     cn: [
15603                         {
15604                             tag: 'span',
15605                             cls: 'timepicker-minute',
15606                             html: '00'
15607                         }  
15608                     ]
15609                 },
15610                 {
15611                     tag: 'td',
15612                     cls: 'separator'
15613                 },
15614                 {
15615                     tag: 'td',
15616                     cn: [
15617                         {
15618                             tag: 'button',
15619                             type: 'button',
15620                             cls: 'btn btn-primary period',
15621                             html: 'AM'
15622                             
15623                         }
15624                     ]
15625                 }
15626             ]
15627         });
15628         
15629         time.createChild({
15630             tag: 'tr',
15631             cn: [
15632                 {
15633                     tag: 'td',
15634                     cn: [
15635                         {
15636                             tag: 'a',
15637                             href: '#',
15638                             cls: 'btn',
15639                             cn: [
15640                                 {
15641                                     tag: 'span',
15642                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15643                                 }
15644                             ]
15645                         }
15646                     ]
15647                 },
15648                 {
15649                     tag: 'td',
15650                     cls: 'separator'
15651                 },
15652                 {
15653                     tag: 'td',
15654                     cn: [
15655                         {
15656                             tag: 'a',
15657                             href: '#',
15658                             cls: 'btn',
15659                             cn: [
15660                                 {
15661                                     tag: 'span',
15662                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15663                                 }
15664                             ]
15665                         }
15666                     ]
15667                 },
15668                 {
15669                     tag: 'td',
15670                     cls: 'separator'
15671                 }
15672             ]
15673         });
15674         
15675     },
15676     
15677     update: function()
15678     {
15679         
15680         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15681         
15682         this.fill();
15683     },
15684     
15685     fill: function() 
15686     {
15687         var hours = this.time.getHours();
15688         var minutes = this.time.getMinutes();
15689         var period = 'AM';
15690         
15691         if(hours > 11){
15692             period = 'PM';
15693         }
15694         
15695         if(hours == 0){
15696             hours = 12;
15697         }
15698         
15699         
15700         if(hours > 12){
15701             hours = hours - 12;
15702         }
15703         
15704         if(hours < 10){
15705             hours = '0' + hours;
15706         }
15707         
15708         if(minutes < 10){
15709             minutes = '0' + minutes;
15710         }
15711         
15712         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15713         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15714         this.pop.select('button', true).first().dom.innerHTML = period;
15715         
15716     },
15717     
15718     place: function()
15719     {   
15720         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15721         
15722         var cls = ['bottom'];
15723         
15724         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15725             cls.pop();
15726             cls.push('top');
15727         }
15728         
15729         cls.push('right');
15730         
15731         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15732             cls.pop();
15733             cls.push('left');
15734         }
15735         
15736         this.picker().addClass(cls.join('-'));
15737         
15738         var _this = this;
15739         
15740         Roo.each(cls, function(c){
15741             if(c == 'bottom'){
15742                 _this.picker().setTop(_this.inputEl().getHeight());
15743                 return;
15744             }
15745             if(c == 'top'){
15746                 _this.picker().setTop(0 - _this.picker().getHeight());
15747                 return;
15748             }
15749             
15750             if(c == 'left'){
15751                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15752                 return;
15753             }
15754             if(c == 'right'){
15755                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15756                 return;
15757             }
15758         });
15759         
15760     },
15761   
15762     onFocus : function()
15763     {
15764         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15765         this.show();
15766     },
15767     
15768     onBlur : function()
15769     {
15770         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15771         this.hide();
15772     },
15773     
15774     show : function()
15775     {
15776         this.picker().show();
15777         this.pop.show();
15778         this.update();
15779         this.place();
15780         
15781         this.fireEvent('show', this, this.date);
15782     },
15783     
15784     hide : function()
15785     {
15786         this.picker().hide();
15787         this.pop.hide();
15788         
15789         this.fireEvent('hide', this, this.date);
15790     },
15791     
15792     setTime : function()
15793     {
15794         this.hide();
15795         this.setValue(this.time.format(this.format));
15796         
15797         this.fireEvent('select', this, this.date);
15798         
15799         
15800     },
15801     
15802     onMousedown: function(e){
15803         e.stopPropagation();
15804         e.preventDefault();
15805     },
15806     
15807     onIncrementHours: function()
15808     {
15809         Roo.log('onIncrementHours');
15810         this.time = this.time.add(Date.HOUR, 1);
15811         this.update();
15812         
15813     },
15814     
15815     onDecrementHours: function()
15816     {
15817         Roo.log('onDecrementHours');
15818         this.time = this.time.add(Date.HOUR, -1);
15819         this.update();
15820     },
15821     
15822     onIncrementMinutes: function()
15823     {
15824         Roo.log('onIncrementMinutes');
15825         this.time = this.time.add(Date.MINUTE, 1);
15826         this.update();
15827     },
15828     
15829     onDecrementMinutes: function()
15830     {
15831         Roo.log('onDecrementMinutes');
15832         this.time = this.time.add(Date.MINUTE, -1);
15833         this.update();
15834     },
15835     
15836     onTogglePeriod: function()
15837     {
15838         Roo.log('onTogglePeriod');
15839         this.time = this.time.add(Date.HOUR, 12);
15840         this.update();
15841     }
15842     
15843    
15844 });
15845
15846 Roo.apply(Roo.bootstrap.TimeField,  {
15847     
15848     content : {
15849         tag: 'tbody',
15850         cn: [
15851             {
15852                 tag: 'tr',
15853                 cn: [
15854                 {
15855                     tag: 'td',
15856                     colspan: '7'
15857                 }
15858                 ]
15859             }
15860         ]
15861     },
15862     
15863     footer : {
15864         tag: 'tfoot',
15865         cn: [
15866             {
15867                 tag: 'tr',
15868                 cn: [
15869                 {
15870                     tag: 'th',
15871                     colspan: '7',
15872                     cls: '',
15873                     cn: [
15874                         {
15875                             tag: 'button',
15876                             cls: 'btn btn-info ok',
15877                             html: 'OK'
15878                         }
15879                     ]
15880                 }
15881
15882                 ]
15883             }
15884         ]
15885     }
15886 });
15887
15888 Roo.apply(Roo.bootstrap.TimeField,  {
15889   
15890     template : {
15891         tag: 'div',
15892         cls: 'datepicker dropdown-menu',
15893         cn: [
15894             {
15895                 tag: 'div',
15896                 cls: 'datepicker-time',
15897                 cn: [
15898                 {
15899                     tag: 'table',
15900                     cls: 'table-condensed',
15901                     cn:[
15902                     Roo.bootstrap.TimeField.content,
15903                     Roo.bootstrap.TimeField.footer
15904                     ]
15905                 }
15906                 ]
15907             }
15908         ]
15909     }
15910 });
15911
15912  
15913
15914  /*
15915  * - LGPL
15916  *
15917  * CheckBox
15918  * 
15919  */
15920
15921 /**
15922  * @class Roo.bootstrap.CheckBox
15923  * @extends Roo.bootstrap.Input
15924  * Bootstrap CheckBox class
15925  * 
15926  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15927  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15928  * @cfg {String} boxLabel The text that appears beside the checkbox
15929  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15930  * @cfg {Boolean} checked initnal the element
15931  * 
15932  * 
15933  * @constructor
15934  * Create a new CheckBox
15935  * @param {Object} config The config object
15936  */
15937
15938 Roo.bootstrap.CheckBox = function(config){
15939     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15940    
15941         this.addEvents({
15942             /**
15943             * @event check
15944             * Fires when the element is checked or unchecked.
15945             * @param {Roo.bootstrap.CheckBox} this This input
15946             * @param {Boolean} checked The new checked value
15947             */
15948            check : true
15949         });
15950 };
15951
15952 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15953     
15954     inputType: 'checkbox',
15955     inputValue: 1,
15956     valueOff: 0,
15957     boxLabel: false,
15958     checked: false,
15959     weight : false,
15960     
15961     getAutoCreate : function()
15962     {
15963         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15964         
15965         var id = Roo.id();
15966         
15967         var cfg = {};
15968         
15969         cfg.cls = 'form-group checkbox' //input-group
15970         
15971         
15972         
15973         
15974         var input =  {
15975             tag: 'input',
15976             id : id,
15977             type : this.inputType,
15978             value : (!this.checked) ? this.valueOff : this.inputValue,
15979             cls : 'roo-checkbox', //'form-box',
15980             placeholder : this.placeholder || ''
15981             
15982         };
15983         
15984         if (this.weight) { // Validity check?
15985             cfg.cls += " checkbox-" + this.weight;
15986         }
15987         
15988         if (this.disabled) {
15989             input.disabled=true;
15990         }
15991         
15992         if(this.checked){
15993             input.checked = this.checked;
15994         }
15995         
15996         if (this.name) {
15997             input.name = this.name;
15998         }
15999         
16000         if (this.size) {
16001             input.cls += ' input-' + this.size;
16002         }
16003         
16004         var settings=this;
16005         ['xs','sm','md','lg'].map(function(size){
16006             if (settings[size]) {
16007                 cfg.cls += ' col-' + size + '-' + settings[size];
16008             }
16009         });
16010         
16011        
16012         
16013         var inputblock = input;
16014         
16015         
16016         
16017         
16018         if (this.before || this.after) {
16019             
16020             inputblock = {
16021                 cls : 'input-group',
16022                 cn :  [] 
16023             };
16024             if (this.before) {
16025                 inputblock.cn.push({
16026                     tag :'span',
16027                     cls : 'input-group-addon',
16028                     html : this.before
16029                 });
16030             }
16031             inputblock.cn.push(input);
16032             if (this.after) {
16033                 inputblock.cn.push({
16034                     tag :'span',
16035                     cls : 'input-group-addon',
16036                     html : this.after
16037                 });
16038             }
16039             
16040         };
16041         
16042         if (align ==='left' && this.fieldLabel.length) {
16043                 Roo.log("left and has label");
16044                 cfg.cn = [
16045                     
16046                     {
16047                         tag: 'label',
16048                         'for' :  id,
16049                         cls : 'control-label col-md-' + this.labelWidth,
16050                         html : this.fieldLabel
16051                         
16052                     },
16053                     {
16054                         cls : "col-md-" + (12 - this.labelWidth), 
16055                         cn: [
16056                             inputblock
16057                         ]
16058                     }
16059                     
16060                 ];
16061         } else if ( this.fieldLabel.length) {
16062                 Roo.log(" label");
16063                 cfg.cn = [
16064                    
16065                     {
16066                         tag: this.boxLabel ? 'span' : 'label',
16067                         'for': id,
16068                         cls: 'control-label box-input-label',
16069                         //cls : 'input-group-addon',
16070                         html : this.fieldLabel
16071                         
16072                     },
16073                     
16074                     inputblock
16075                     
16076                 ];
16077
16078         } else {
16079             
16080                 Roo.log(" no label && no align");
16081                 cfg.cn = [  inputblock ] ;
16082                 
16083                 
16084         };
16085          if(this.boxLabel){
16086             cfg.cn.push( {
16087                 tag: 'label',
16088                 'for': id,
16089                 cls: 'box-label',
16090                 html: this.boxLabel
16091                 
16092             });
16093         }
16094         
16095         
16096        
16097         return cfg;
16098         
16099     },
16100     
16101     /**
16102      * return the real input element.
16103      */
16104     inputEl: function ()
16105     {
16106         return this.el.select('input.roo-checkbox',true).first();
16107     },
16108     
16109     label: function()
16110     {
16111         return this.el.select('label.control-label',true).first();
16112     },
16113     
16114     initEvents : function()
16115     {
16116 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16117         
16118         this.inputEl().on('click', this.onClick,  this);
16119         
16120     },
16121     
16122     onClick : function()
16123     {   
16124         this.setChecked(!this.checked);
16125     },
16126     
16127     setChecked : function(state,suppressEvent)
16128     {
16129         this.checked = state;
16130         
16131         this.inputEl().dom.checked = state;
16132         
16133         if(suppressEvent !== true){
16134             this.fireEvent('check', this, state);
16135         }
16136         
16137         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16138         
16139     },
16140     
16141     setValue : function(v,suppressEvent)
16142     {
16143         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16144     }
16145     
16146 });
16147
16148  
16149 /*
16150  * - LGPL
16151  *
16152  * Radio
16153  * 
16154  */
16155
16156 /**
16157  * @class Roo.bootstrap.Radio
16158  * @extends Roo.bootstrap.CheckBox
16159  * Bootstrap Radio class
16160
16161  * @constructor
16162  * Create a new Radio
16163  * @param {Object} config The config object
16164  */
16165
16166 Roo.bootstrap.Radio = function(config){
16167     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16168    
16169 };
16170
16171 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16172     
16173     inputType: 'radio',
16174     inputValue: '',
16175     valueOff: '',
16176     
16177     getAutoCreate : function()
16178     {
16179         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16180         
16181         var id = Roo.id();
16182         
16183         var cfg = {};
16184         
16185         cfg.cls = 'form-group radio' //input-group
16186         
16187         var input =  {
16188             tag: 'input',
16189             id : id,
16190             type : this.inputType,
16191             value : (!this.checked) ? this.valueOff : this.inputValue,
16192             cls : 'roo-radio',
16193             placeholder : this.placeholder || ''
16194             
16195         };
16196           if (this.weight) { // Validity check?
16197             cfg.cls += " radio-" + this.weight;
16198         }
16199         if (this.disabled) {
16200             input.disabled=true;
16201         }
16202         
16203         if(this.checked){
16204             input.checked = this.checked;
16205         }
16206         
16207         if (this.name) {
16208             input.name = this.name;
16209         }
16210         
16211         if (this.size) {
16212             input.cls += ' input-' + this.size;
16213         }
16214         
16215         var settings=this;
16216         ['xs','sm','md','lg'].map(function(size){
16217             if (settings[size]) {
16218                 cfg.cls += ' col-' + size + '-' + settings[size];
16219             }
16220         });
16221         
16222         var inputblock = input;
16223         
16224         if (this.before || this.after) {
16225             
16226             inputblock = {
16227                 cls : 'input-group',
16228                 cn :  [] 
16229             };
16230             if (this.before) {
16231                 inputblock.cn.push({
16232                     tag :'span',
16233                     cls : 'input-group-addon',
16234                     html : this.before
16235                 });
16236             }
16237             inputblock.cn.push(input);
16238             if (this.after) {
16239                 inputblock.cn.push({
16240                     tag :'span',
16241                     cls : 'input-group-addon',
16242                     html : this.after
16243                 });
16244             }
16245             
16246         };
16247         
16248         if (align ==='left' && this.fieldLabel.length) {
16249                 Roo.log("left and has label");
16250                 cfg.cn = [
16251                     
16252                     {
16253                         tag: 'label',
16254                         'for' :  id,
16255                         cls : 'control-label col-md-' + this.labelWidth,
16256                         html : this.fieldLabel
16257                         
16258                     },
16259                     {
16260                         cls : "col-md-" + (12 - this.labelWidth), 
16261                         cn: [
16262                             inputblock
16263                         ]
16264                     }
16265                     
16266                 ];
16267         } else if ( this.fieldLabel.length) {
16268                 Roo.log(" label");
16269                  cfg.cn = [
16270                    
16271                     {
16272                         tag: 'label',
16273                         'for': id,
16274                         cls: 'control-label box-input-label',
16275                         //cls : 'input-group-addon',
16276                         html : this.fieldLabel
16277                         
16278                     },
16279                     
16280                     inputblock
16281                     
16282                 ];
16283
16284         } else {
16285             
16286                    Roo.log(" no label && no align");
16287                 cfg.cn = [
16288                     
16289                         inputblock
16290                     
16291                 ];
16292                 
16293                 
16294         };
16295         
16296         if(this.boxLabel){
16297             cfg.cn.push({
16298                 tag: 'label',
16299                 'for': id,
16300                 cls: 'box-label',
16301                 html: this.boxLabel
16302             })
16303         }
16304         
16305         return cfg;
16306         
16307     },
16308     inputEl: function ()
16309     {
16310         return this.el.select('input.roo-radio',true).first();
16311     },
16312     onClick : function()
16313     {   
16314         this.setChecked(true);
16315     },
16316     
16317     setChecked : function(state,suppressEvent)
16318     {
16319         if(state){
16320             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16321                 v.dom.checked = false;
16322             });
16323         }
16324         
16325         this.checked = state;
16326         this.inputEl().dom.checked = state;
16327         
16328         if(suppressEvent !== true){
16329             this.fireEvent('check', this, state);
16330         }
16331         
16332         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16333         
16334     },
16335     
16336     getGroupValue : function()
16337     {
16338         var value = ''
16339         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16340             if(v.dom.checked == true){
16341                 value = v.dom.value;
16342             }
16343         });
16344         
16345         return value;
16346     },
16347     
16348     /**
16349      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16350      * @return {Mixed} value The field value
16351      */
16352     getValue : function(){
16353         return this.getGroupValue();
16354     }
16355     
16356 });
16357
16358  
16359 //<script type="text/javascript">
16360
16361 /*
16362  * Based  Ext JS Library 1.1.1
16363  * Copyright(c) 2006-2007, Ext JS, LLC.
16364  * LGPL
16365  *
16366  */
16367  
16368 /**
16369  * @class Roo.HtmlEditorCore
16370  * @extends Roo.Component
16371  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16372  *
16373  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16374  */
16375
16376 Roo.HtmlEditorCore = function(config){
16377     
16378     
16379     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16380     
16381     
16382     this.addEvents({
16383         /**
16384          * @event initialize
16385          * Fires when the editor is fully initialized (including the iframe)
16386          * @param {Roo.HtmlEditorCore} this
16387          */
16388         initialize: true,
16389         /**
16390          * @event activate
16391          * Fires when the editor is first receives the focus. Any insertion must wait
16392          * until after this event.
16393          * @param {Roo.HtmlEditorCore} this
16394          */
16395         activate: true,
16396          /**
16397          * @event beforesync
16398          * Fires before the textarea is updated with content from the editor iframe. Return false
16399          * to cancel the sync.
16400          * @param {Roo.HtmlEditorCore} this
16401          * @param {String} html
16402          */
16403         beforesync: true,
16404          /**
16405          * @event beforepush
16406          * Fires before the iframe editor is updated with content from the textarea. Return false
16407          * to cancel the push.
16408          * @param {Roo.HtmlEditorCore} this
16409          * @param {String} html
16410          */
16411         beforepush: true,
16412          /**
16413          * @event sync
16414          * Fires when the textarea is updated with content from the editor iframe.
16415          * @param {Roo.HtmlEditorCore} this
16416          * @param {String} html
16417          */
16418         sync: true,
16419          /**
16420          * @event push
16421          * Fires when the iframe editor is updated with content from the textarea.
16422          * @param {Roo.HtmlEditorCore} this
16423          * @param {String} html
16424          */
16425         push: true,
16426         
16427         /**
16428          * @event editorevent
16429          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16430          * @param {Roo.HtmlEditorCore} this
16431          */
16432         editorevent: true
16433     });
16434     
16435     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16436     
16437     // defaults : white / black...
16438     this.applyBlacklists();
16439     
16440     
16441     
16442 };
16443
16444
16445 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16446
16447
16448      /**
16449      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16450      */
16451     
16452     owner : false,
16453     
16454      /**
16455      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16456      *                        Roo.resizable.
16457      */
16458     resizable : false,
16459      /**
16460      * @cfg {Number} height (in pixels)
16461      */   
16462     height: 300,
16463    /**
16464      * @cfg {Number} width (in pixels)
16465      */   
16466     width: 500,
16467     
16468     /**
16469      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16470      * 
16471      */
16472     stylesheets: false,
16473     
16474     // id of frame..
16475     frameId: false,
16476     
16477     // private properties
16478     validationEvent : false,
16479     deferHeight: true,
16480     initialized : false,
16481     activated : false,
16482     sourceEditMode : false,
16483     onFocus : Roo.emptyFn,
16484     iframePad:3,
16485     hideMode:'offsets',
16486     
16487     clearUp: true,
16488     
16489     // blacklist + whitelisted elements..
16490     black: false,
16491     white: false,
16492      
16493     
16494
16495     /**
16496      * Protected method that will not generally be called directly. It
16497      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16498      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16499      */
16500     getDocMarkup : function(){
16501         // body styles..
16502         var st = '';
16503         Roo.log(this.stylesheets);
16504         
16505         // inherit styels from page...?? 
16506         if (this.stylesheets === false) {
16507             
16508             Roo.get(document.head).select('style').each(function(node) {
16509                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16510             });
16511             
16512             Roo.get(document.head).select('link').each(function(node) { 
16513                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16514             });
16515             
16516         } else if (!this.stylesheets.length) {
16517                 // simple..
16518                 st = '<style type="text/css">' +
16519                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16520                    '</style>';
16521         } else {
16522             Roo.each(this.stylesheets, function(s) {
16523                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16524             });
16525             
16526         }
16527         
16528         st +=  '<style type="text/css">' +
16529             'IMG { cursor: pointer } ' +
16530         '</style>';
16531
16532         
16533         return '<html><head>' + st  +
16534             //<style type="text/css">' +
16535             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16536             //'</style>' +
16537             ' </head><body class="roo-htmleditor-body"></body></html>';
16538     },
16539
16540     // private
16541     onRender : function(ct, position)
16542     {
16543         var _t = this;
16544         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16545         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16546         
16547         
16548         this.el.dom.style.border = '0 none';
16549         this.el.dom.setAttribute('tabIndex', -1);
16550         this.el.addClass('x-hidden hide');
16551         
16552         
16553         
16554         if(Roo.isIE){ // fix IE 1px bogus margin
16555             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16556         }
16557        
16558         
16559         this.frameId = Roo.id();
16560         
16561          
16562         
16563         var iframe = this.owner.wrap.createChild({
16564             tag: 'iframe',
16565             cls: 'form-control', // bootstrap..
16566             id: this.frameId,
16567             name: this.frameId,
16568             frameBorder : 'no',
16569             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16570         }, this.el
16571         );
16572         
16573         
16574         this.iframe = iframe.dom;
16575
16576          this.assignDocWin();
16577         
16578         this.doc.designMode = 'on';
16579        
16580         this.doc.open();
16581         this.doc.write(this.getDocMarkup());
16582         this.doc.close();
16583
16584         
16585         var task = { // must defer to wait for browser to be ready
16586             run : function(){
16587                 //console.log("run task?" + this.doc.readyState);
16588                 this.assignDocWin();
16589                 if(this.doc.body || this.doc.readyState == 'complete'){
16590                     try {
16591                         this.doc.designMode="on";
16592                     } catch (e) {
16593                         return;
16594                     }
16595                     Roo.TaskMgr.stop(task);
16596                     this.initEditor.defer(10, this);
16597                 }
16598             },
16599             interval : 10,
16600             duration: 10000,
16601             scope: this
16602         };
16603         Roo.TaskMgr.start(task);
16604
16605         
16606          
16607     },
16608
16609     // private
16610     onResize : function(w, h)
16611     {
16612          Roo.log('resize: ' +w + ',' + h );
16613         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16614         if(!this.iframe){
16615             return;
16616         }
16617         if(typeof w == 'number'){
16618             
16619             this.iframe.style.width = w + 'px';
16620         }
16621         if(typeof h == 'number'){
16622             
16623             this.iframe.style.height = h + 'px';
16624             if(this.doc){
16625                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16626             }
16627         }
16628         
16629     },
16630
16631     /**
16632      * Toggles the editor between standard and source edit mode.
16633      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16634      */
16635     toggleSourceEdit : function(sourceEditMode){
16636         
16637         this.sourceEditMode = sourceEditMode === true;
16638         
16639         if(this.sourceEditMode){
16640  
16641             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16642             
16643         }else{
16644             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16645             //this.iframe.className = '';
16646             this.deferFocus();
16647         }
16648         //this.setSize(this.owner.wrap.getSize());
16649         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16650     },
16651
16652     
16653   
16654
16655     /**
16656      * Protected method that will not generally be called directly. If you need/want
16657      * custom HTML cleanup, this is the method you should override.
16658      * @param {String} html The HTML to be cleaned
16659      * return {String} The cleaned HTML
16660      */
16661     cleanHtml : function(html){
16662         html = String(html);
16663         if(html.length > 5){
16664             if(Roo.isSafari){ // strip safari nonsense
16665                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16666             }
16667         }
16668         if(html == '&nbsp;'){
16669             html = '';
16670         }
16671         return html;
16672     },
16673
16674     /**
16675      * HTML Editor -> Textarea
16676      * Protected method that will not generally be called directly. Syncs the contents
16677      * of the editor iframe with the textarea.
16678      */
16679     syncValue : function(){
16680         if(this.initialized){
16681             var bd = (this.doc.body || this.doc.documentElement);
16682             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16683             var html = bd.innerHTML;
16684             if(Roo.isSafari){
16685                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16686                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16687                 if(m && m[1]){
16688                     html = '<div style="'+m[0]+'">' + html + '</div>';
16689                 }
16690             }
16691             html = this.cleanHtml(html);
16692             // fix up the special chars.. normaly like back quotes in word...
16693             // however we do not want to do this with chinese..
16694             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16695                 var cc = b.charCodeAt();
16696                 if (
16697                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16698                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16699                     (cc >= 0xf900 && cc < 0xfb00 )
16700                 ) {
16701                         return b;
16702                 }
16703                 return "&#"+cc+";" 
16704             });
16705             if(this.owner.fireEvent('beforesync', this, html) !== false){
16706                 this.el.dom.value = html;
16707                 this.owner.fireEvent('sync', this, html);
16708             }
16709         }
16710     },
16711
16712     /**
16713      * Protected method that will not generally be called directly. Pushes the value of the textarea
16714      * into the iframe editor.
16715      */
16716     pushValue : function(){
16717         if(this.initialized){
16718             var v = this.el.dom.value.trim();
16719             
16720 //            if(v.length < 1){
16721 //                v = '&#160;';
16722 //            }
16723             
16724             if(this.owner.fireEvent('beforepush', this, v) !== false){
16725                 var d = (this.doc.body || this.doc.documentElement);
16726                 d.innerHTML = v;
16727                 this.cleanUpPaste();
16728                 this.el.dom.value = d.innerHTML;
16729                 this.owner.fireEvent('push', this, v);
16730             }
16731         }
16732     },
16733
16734     // private
16735     deferFocus : function(){
16736         this.focus.defer(10, this);
16737     },
16738
16739     // doc'ed in Field
16740     focus : function(){
16741         if(this.win && !this.sourceEditMode){
16742             this.win.focus();
16743         }else{
16744             this.el.focus();
16745         }
16746     },
16747     
16748     assignDocWin: function()
16749     {
16750         var iframe = this.iframe;
16751         
16752          if(Roo.isIE){
16753             this.doc = iframe.contentWindow.document;
16754             this.win = iframe.contentWindow;
16755         } else {
16756 //            if (!Roo.get(this.frameId)) {
16757 //                return;
16758 //            }
16759 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16760 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16761             
16762             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16763                 return;
16764             }
16765             
16766             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16767             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16768         }
16769     },
16770     
16771     // private
16772     initEditor : function(){
16773         //console.log("INIT EDITOR");
16774         this.assignDocWin();
16775         
16776         
16777         
16778         this.doc.designMode="on";
16779         this.doc.open();
16780         this.doc.write(this.getDocMarkup());
16781         this.doc.close();
16782         
16783         var dbody = (this.doc.body || this.doc.documentElement);
16784         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16785         // this copies styles from the containing element into thsi one..
16786         // not sure why we need all of this..
16787         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16788         
16789         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16790         //ss['background-attachment'] = 'fixed'; // w3c
16791         dbody.bgProperties = 'fixed'; // ie
16792         //Roo.DomHelper.applyStyles(dbody, ss);
16793         Roo.EventManager.on(this.doc, {
16794             //'mousedown': this.onEditorEvent,
16795             'mouseup': this.onEditorEvent,
16796             'dblclick': this.onEditorEvent,
16797             'click': this.onEditorEvent,
16798             'keyup': this.onEditorEvent,
16799             buffer:100,
16800             scope: this
16801         });
16802         if(Roo.isGecko){
16803             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16804         }
16805         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16806             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16807         }
16808         this.initialized = true;
16809
16810         this.owner.fireEvent('initialize', this);
16811         this.pushValue();
16812     },
16813
16814     // private
16815     onDestroy : function(){
16816         
16817         
16818         
16819         if(this.rendered){
16820             
16821             //for (var i =0; i < this.toolbars.length;i++) {
16822             //    // fixme - ask toolbars for heights?
16823             //    this.toolbars[i].onDestroy();
16824            // }
16825             
16826             //this.wrap.dom.innerHTML = '';
16827             //this.wrap.remove();
16828         }
16829     },
16830
16831     // private
16832     onFirstFocus : function(){
16833         
16834         this.assignDocWin();
16835         
16836         
16837         this.activated = true;
16838          
16839     
16840         if(Roo.isGecko){ // prevent silly gecko errors
16841             this.win.focus();
16842             var s = this.win.getSelection();
16843             if(!s.focusNode || s.focusNode.nodeType != 3){
16844                 var r = s.getRangeAt(0);
16845                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16846                 r.collapse(true);
16847                 this.deferFocus();
16848             }
16849             try{
16850                 this.execCmd('useCSS', true);
16851                 this.execCmd('styleWithCSS', false);
16852             }catch(e){}
16853         }
16854         this.owner.fireEvent('activate', this);
16855     },
16856
16857     // private
16858     adjustFont: function(btn){
16859         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16860         //if(Roo.isSafari){ // safari
16861         //    adjust *= 2;
16862        // }
16863         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16864         if(Roo.isSafari){ // safari
16865             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16866             v =  (v < 10) ? 10 : v;
16867             v =  (v > 48) ? 48 : v;
16868             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16869             
16870         }
16871         
16872         
16873         v = Math.max(1, v+adjust);
16874         
16875         this.execCmd('FontSize', v  );
16876     },
16877
16878     onEditorEvent : function(e){
16879         this.owner.fireEvent('editorevent', this, e);
16880       //  this.updateToolbar();
16881         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16882     },
16883
16884     insertTag : function(tg)
16885     {
16886         // could be a bit smarter... -> wrap the current selected tRoo..
16887         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16888             
16889             range = this.createRange(this.getSelection());
16890             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16891             wrappingNode.appendChild(range.extractContents());
16892             range.insertNode(wrappingNode);
16893
16894             return;
16895             
16896             
16897             
16898         }
16899         this.execCmd("formatblock",   tg);
16900         
16901     },
16902     
16903     insertText : function(txt)
16904     {
16905         
16906         
16907         var range = this.createRange();
16908         range.deleteContents();
16909                //alert(Sender.getAttribute('label'));
16910                
16911         range.insertNode(this.doc.createTextNode(txt));
16912     } ,
16913     
16914      
16915
16916     /**
16917      * Executes a Midas editor command on the editor document and performs necessary focus and
16918      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16919      * @param {String} cmd The Midas command
16920      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16921      */
16922     relayCmd : function(cmd, value){
16923         this.win.focus();
16924         this.execCmd(cmd, value);
16925         this.owner.fireEvent('editorevent', this);
16926         //this.updateToolbar();
16927         this.owner.deferFocus();
16928     },
16929
16930     /**
16931      * Executes a Midas editor command directly on the editor document.
16932      * For visual commands, you should use {@link #relayCmd} instead.
16933      * <b>This should only be called after the editor is initialized.</b>
16934      * @param {String} cmd The Midas command
16935      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16936      */
16937     execCmd : function(cmd, value){
16938         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16939         this.syncValue();
16940     },
16941  
16942  
16943    
16944     /**
16945      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16946      * to insert tRoo.
16947      * @param {String} text | dom node.. 
16948      */
16949     insertAtCursor : function(text)
16950     {
16951         
16952         
16953         
16954         if(!this.activated){
16955             return;
16956         }
16957         /*
16958         if(Roo.isIE){
16959             this.win.focus();
16960             var r = this.doc.selection.createRange();
16961             if(r){
16962                 r.collapse(true);
16963                 r.pasteHTML(text);
16964                 this.syncValue();
16965                 this.deferFocus();
16966             
16967             }
16968             return;
16969         }
16970         */
16971         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16972             this.win.focus();
16973             
16974             
16975             // from jquery ui (MIT licenced)
16976             var range, node;
16977             var win = this.win;
16978             
16979             if (win.getSelection && win.getSelection().getRangeAt) {
16980                 range = win.getSelection().getRangeAt(0);
16981                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16982                 range.insertNode(node);
16983             } else if (win.document.selection && win.document.selection.createRange) {
16984                 // no firefox support
16985                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16986                 win.document.selection.createRange().pasteHTML(txt);
16987             } else {
16988                 // no firefox support
16989                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16990                 this.execCmd('InsertHTML', txt);
16991             } 
16992             
16993             this.syncValue();
16994             
16995             this.deferFocus();
16996         }
16997     },
16998  // private
16999     mozKeyPress : function(e){
17000         if(e.ctrlKey){
17001             var c = e.getCharCode(), cmd;
17002           
17003             if(c > 0){
17004                 c = String.fromCharCode(c).toLowerCase();
17005                 switch(c){
17006                     case 'b':
17007                         cmd = 'bold';
17008                         break;
17009                     case 'i':
17010                         cmd = 'italic';
17011                         break;
17012                     
17013                     case 'u':
17014                         cmd = 'underline';
17015                         break;
17016                     
17017                     case 'v':
17018                         this.cleanUpPaste.defer(100, this);
17019                         return;
17020                         
17021                 }
17022                 if(cmd){
17023                     this.win.focus();
17024                     this.execCmd(cmd);
17025                     this.deferFocus();
17026                     e.preventDefault();
17027                 }
17028                 
17029             }
17030         }
17031     },
17032
17033     // private
17034     fixKeys : function(){ // load time branching for fastest keydown performance
17035         if(Roo.isIE){
17036             return function(e){
17037                 var k = e.getKey(), r;
17038                 if(k == e.TAB){
17039                     e.stopEvent();
17040                     r = this.doc.selection.createRange();
17041                     if(r){
17042                         r.collapse(true);
17043                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17044                         this.deferFocus();
17045                     }
17046                     return;
17047                 }
17048                 
17049                 if(k == e.ENTER){
17050                     r = this.doc.selection.createRange();
17051                     if(r){
17052                         var target = r.parentElement();
17053                         if(!target || target.tagName.toLowerCase() != 'li'){
17054                             e.stopEvent();
17055                             r.pasteHTML('<br />');
17056                             r.collapse(false);
17057                             r.select();
17058                         }
17059                     }
17060                 }
17061                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17062                     this.cleanUpPaste.defer(100, this);
17063                     return;
17064                 }
17065                 
17066                 
17067             };
17068         }else if(Roo.isOpera){
17069             return function(e){
17070                 var k = e.getKey();
17071                 if(k == e.TAB){
17072                     e.stopEvent();
17073                     this.win.focus();
17074                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17075                     this.deferFocus();
17076                 }
17077                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17078                     this.cleanUpPaste.defer(100, this);
17079                     return;
17080                 }
17081                 
17082             };
17083         }else if(Roo.isSafari){
17084             return function(e){
17085                 var k = e.getKey();
17086                 
17087                 if(k == e.TAB){
17088                     e.stopEvent();
17089                     this.execCmd('InsertText','\t');
17090                     this.deferFocus();
17091                     return;
17092                 }
17093                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17094                     this.cleanUpPaste.defer(100, this);
17095                     return;
17096                 }
17097                 
17098              };
17099         }
17100     }(),
17101     
17102     getAllAncestors: function()
17103     {
17104         var p = this.getSelectedNode();
17105         var a = [];
17106         if (!p) {
17107             a.push(p); // push blank onto stack..
17108             p = this.getParentElement();
17109         }
17110         
17111         
17112         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17113             a.push(p);
17114             p = p.parentNode;
17115         }
17116         a.push(this.doc.body);
17117         return a;
17118     },
17119     lastSel : false,
17120     lastSelNode : false,
17121     
17122     
17123     getSelection : function() 
17124     {
17125         this.assignDocWin();
17126         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17127     },
17128     
17129     getSelectedNode: function() 
17130     {
17131         // this may only work on Gecko!!!
17132         
17133         // should we cache this!!!!
17134         
17135         
17136         
17137          
17138         var range = this.createRange(this.getSelection()).cloneRange();
17139         
17140         if (Roo.isIE) {
17141             var parent = range.parentElement();
17142             while (true) {
17143                 var testRange = range.duplicate();
17144                 testRange.moveToElementText(parent);
17145                 if (testRange.inRange(range)) {
17146                     break;
17147                 }
17148                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17149                     break;
17150                 }
17151                 parent = parent.parentElement;
17152             }
17153             return parent;
17154         }
17155         
17156         // is ancestor a text element.
17157         var ac =  range.commonAncestorContainer;
17158         if (ac.nodeType == 3) {
17159             ac = ac.parentNode;
17160         }
17161         
17162         var ar = ac.childNodes;
17163          
17164         var nodes = [];
17165         var other_nodes = [];
17166         var has_other_nodes = false;
17167         for (var i=0;i<ar.length;i++) {
17168             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17169                 continue;
17170             }
17171             // fullly contained node.
17172             
17173             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17174                 nodes.push(ar[i]);
17175                 continue;
17176             }
17177             
17178             // probably selected..
17179             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17180                 other_nodes.push(ar[i]);
17181                 continue;
17182             }
17183             // outer..
17184             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17185                 continue;
17186             }
17187             
17188             
17189             has_other_nodes = true;
17190         }
17191         if (!nodes.length && other_nodes.length) {
17192             nodes= other_nodes;
17193         }
17194         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17195             return false;
17196         }
17197         
17198         return nodes[0];
17199     },
17200     createRange: function(sel)
17201     {
17202         // this has strange effects when using with 
17203         // top toolbar - not sure if it's a great idea.
17204         //this.editor.contentWindow.focus();
17205         if (typeof sel != "undefined") {
17206             try {
17207                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17208             } catch(e) {
17209                 return this.doc.createRange();
17210             }
17211         } else {
17212             return this.doc.createRange();
17213         }
17214     },
17215     getParentElement: function()
17216     {
17217         
17218         this.assignDocWin();
17219         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17220         
17221         var range = this.createRange(sel);
17222          
17223         try {
17224             var p = range.commonAncestorContainer;
17225             while (p.nodeType == 3) { // text node
17226                 p = p.parentNode;
17227             }
17228             return p;
17229         } catch (e) {
17230             return null;
17231         }
17232     
17233     },
17234     /***
17235      *
17236      * Range intersection.. the hard stuff...
17237      *  '-1' = before
17238      *  '0' = hits..
17239      *  '1' = after.
17240      *         [ -- selected range --- ]
17241      *   [fail]                        [fail]
17242      *
17243      *    basically..
17244      *      if end is before start or  hits it. fail.
17245      *      if start is after end or hits it fail.
17246      *
17247      *   if either hits (but other is outside. - then it's not 
17248      *   
17249      *    
17250      **/
17251     
17252     
17253     // @see http://www.thismuchiknow.co.uk/?p=64.
17254     rangeIntersectsNode : function(range, node)
17255     {
17256         var nodeRange = node.ownerDocument.createRange();
17257         try {
17258             nodeRange.selectNode(node);
17259         } catch (e) {
17260             nodeRange.selectNodeContents(node);
17261         }
17262     
17263         var rangeStartRange = range.cloneRange();
17264         rangeStartRange.collapse(true);
17265     
17266         var rangeEndRange = range.cloneRange();
17267         rangeEndRange.collapse(false);
17268     
17269         var nodeStartRange = nodeRange.cloneRange();
17270         nodeStartRange.collapse(true);
17271     
17272         var nodeEndRange = nodeRange.cloneRange();
17273         nodeEndRange.collapse(false);
17274     
17275         return rangeStartRange.compareBoundaryPoints(
17276                  Range.START_TO_START, nodeEndRange) == -1 &&
17277                rangeEndRange.compareBoundaryPoints(
17278                  Range.START_TO_START, nodeStartRange) == 1;
17279         
17280          
17281     },
17282     rangeCompareNode : function(range, node)
17283     {
17284         var nodeRange = node.ownerDocument.createRange();
17285         try {
17286             nodeRange.selectNode(node);
17287         } catch (e) {
17288             nodeRange.selectNodeContents(node);
17289         }
17290         
17291         
17292         range.collapse(true);
17293     
17294         nodeRange.collapse(true);
17295      
17296         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17297         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17298          
17299         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17300         
17301         var nodeIsBefore   =  ss == 1;
17302         var nodeIsAfter    = ee == -1;
17303         
17304         if (nodeIsBefore && nodeIsAfter)
17305             return 0; // outer
17306         if (!nodeIsBefore && nodeIsAfter)
17307             return 1; //right trailed.
17308         
17309         if (nodeIsBefore && !nodeIsAfter)
17310             return 2;  // left trailed.
17311         // fully contined.
17312         return 3;
17313     },
17314
17315     // private? - in a new class?
17316     cleanUpPaste :  function()
17317     {
17318         // cleans up the whole document..
17319         Roo.log('cleanuppaste');
17320         
17321         this.cleanUpChildren(this.doc.body);
17322         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17323         if (clean != this.doc.body.innerHTML) {
17324             this.doc.body.innerHTML = clean;
17325         }
17326         
17327     },
17328     
17329     cleanWordChars : function(input) {// change the chars to hex code
17330         var he = Roo.HtmlEditorCore;
17331         
17332         var output = input;
17333         Roo.each(he.swapCodes, function(sw) { 
17334             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17335             
17336             output = output.replace(swapper, sw[1]);
17337         });
17338         
17339         return output;
17340     },
17341     
17342     
17343     cleanUpChildren : function (n)
17344     {
17345         if (!n.childNodes.length) {
17346             return;
17347         }
17348         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17349            this.cleanUpChild(n.childNodes[i]);
17350         }
17351     },
17352     
17353     
17354         
17355     
17356     cleanUpChild : function (node)
17357     {
17358         var ed = this;
17359         //console.log(node);
17360         if (node.nodeName == "#text") {
17361             // clean up silly Windows -- stuff?
17362             return; 
17363         }
17364         if (node.nodeName == "#comment") {
17365             node.parentNode.removeChild(node);
17366             // clean up silly Windows -- stuff?
17367             return; 
17368         }
17369         var lcname = node.tagName.toLowerCase();
17370         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17371         // whitelist of tags..
17372         
17373         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17374             // remove node.
17375             node.parentNode.removeChild(node);
17376             return;
17377             
17378         }
17379         
17380         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17381         
17382         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17383         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17384         
17385         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17386         //    remove_keep_children = true;
17387         //}
17388         
17389         if (remove_keep_children) {
17390             this.cleanUpChildren(node);
17391             // inserts everything just before this node...
17392             while (node.childNodes.length) {
17393                 var cn = node.childNodes[0];
17394                 node.removeChild(cn);
17395                 node.parentNode.insertBefore(cn, node);
17396             }
17397             node.parentNode.removeChild(node);
17398             return;
17399         }
17400         
17401         if (!node.attributes || !node.attributes.length) {
17402             this.cleanUpChildren(node);
17403             return;
17404         }
17405         
17406         function cleanAttr(n,v)
17407         {
17408             
17409             if (v.match(/^\./) || v.match(/^\//)) {
17410                 return;
17411             }
17412             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17413                 return;
17414             }
17415             if (v.match(/^#/)) {
17416                 return;
17417             }
17418 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17419             node.removeAttribute(n);
17420             
17421         }
17422         
17423         var cwhite = this.cwhite;
17424         var cblack = this.cblack;
17425             
17426         function cleanStyle(n,v)
17427         {
17428             if (v.match(/expression/)) { //XSS?? should we even bother..
17429                 node.removeAttribute(n);
17430                 return;
17431             }
17432             
17433             var parts = v.split(/;/);
17434             var clean = [];
17435             
17436             Roo.each(parts, function(p) {
17437                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17438                 if (!p.length) {
17439                     return true;
17440                 }
17441                 var l = p.split(':').shift().replace(/\s+/g,'');
17442                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17443                 
17444                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17445 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17446                     //node.removeAttribute(n);
17447                     return true;
17448                 }
17449                 //Roo.log()
17450                 // only allow 'c whitelisted system attributes'
17451                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17452 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17453                     //node.removeAttribute(n);
17454                     return true;
17455                 }
17456                 
17457                 
17458                  
17459                 
17460                 clean.push(p);
17461                 return true;
17462             });
17463             if (clean.length) { 
17464                 node.setAttribute(n, clean.join(';'));
17465             } else {
17466                 node.removeAttribute(n);
17467             }
17468             
17469         }
17470         
17471         
17472         for (var i = node.attributes.length-1; i > -1 ; i--) {
17473             var a = node.attributes[i];
17474             //console.log(a);
17475             
17476             if (a.name.toLowerCase().substr(0,2)=='on')  {
17477                 node.removeAttribute(a.name);
17478                 continue;
17479             }
17480             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17481                 node.removeAttribute(a.name);
17482                 continue;
17483             }
17484             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17485                 cleanAttr(a.name,a.value); // fixme..
17486                 continue;
17487             }
17488             if (a.name == 'style') {
17489                 cleanStyle(a.name,a.value);
17490                 continue;
17491             }
17492             /// clean up MS crap..
17493             // tecnically this should be a list of valid class'es..
17494             
17495             
17496             if (a.name == 'class') {
17497                 if (a.value.match(/^Mso/)) {
17498                     node.className = '';
17499                 }
17500                 
17501                 if (a.value.match(/body/)) {
17502                     node.className = '';
17503                 }
17504                 continue;
17505             }
17506             
17507             // style cleanup!?
17508             // class cleanup?
17509             
17510         }
17511         
17512         
17513         this.cleanUpChildren(node);
17514         
17515         
17516     },
17517     /**
17518      * Clean up MS wordisms...
17519      */
17520     cleanWord : function(node)
17521     {
17522         var _t = this;
17523         var cleanWordChildren = function()
17524         {
17525             if (!node.childNodes.length) {
17526                 return;
17527             }
17528             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17529                _t.cleanWord(node.childNodes[i]);
17530             }
17531         }
17532         
17533         
17534         if (!node) {
17535             this.cleanWord(this.doc.body);
17536             return;
17537         }
17538         if (node.nodeName == "#text") {
17539             // clean up silly Windows -- stuff?
17540             return; 
17541         }
17542         if (node.nodeName == "#comment") {
17543             node.parentNode.removeChild(node);
17544             // clean up silly Windows -- stuff?
17545             return; 
17546         }
17547         
17548         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17549             node.parentNode.removeChild(node);
17550             return;
17551         }
17552         
17553         // remove - but keep children..
17554         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17555             while (node.childNodes.length) {
17556                 var cn = node.childNodes[0];
17557                 node.removeChild(cn);
17558                 node.parentNode.insertBefore(cn, node);
17559             }
17560             node.parentNode.removeChild(node);
17561             cleanWordChildren();
17562             return;
17563         }
17564         // clean styles
17565         if (node.className.length) {
17566             
17567             var cn = node.className.split(/\W+/);
17568             var cna = [];
17569             Roo.each(cn, function(cls) {
17570                 if (cls.match(/Mso[a-zA-Z]+/)) {
17571                     return;
17572                 }
17573                 cna.push(cls);
17574             });
17575             node.className = cna.length ? cna.join(' ') : '';
17576             if (!cna.length) {
17577                 node.removeAttribute("class");
17578             }
17579         }
17580         
17581         if (node.hasAttribute("lang")) {
17582             node.removeAttribute("lang");
17583         }
17584         
17585         if (node.hasAttribute("style")) {
17586             
17587             var styles = node.getAttribute("style").split(";");
17588             var nstyle = [];
17589             Roo.each(styles, function(s) {
17590                 if (!s.match(/:/)) {
17591                     return;
17592                 }
17593                 var kv = s.split(":");
17594                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17595                     return;
17596                 }
17597                 // what ever is left... we allow.
17598                 nstyle.push(s);
17599             });
17600             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17601             if (!nstyle.length) {
17602                 node.removeAttribute('style');
17603             }
17604         }
17605         
17606         cleanWordChildren();
17607         
17608         
17609     },
17610     domToHTML : function(currentElement, depth, nopadtext) {
17611         
17612         depth = depth || 0;
17613         nopadtext = nopadtext || false;
17614     
17615         if (!currentElement) {
17616             return this.domToHTML(this.doc.body);
17617         }
17618         
17619         //Roo.log(currentElement);
17620         var j;
17621         var allText = false;
17622         var nodeName = currentElement.nodeName;
17623         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17624         
17625         if  (nodeName == '#text') {
17626             return currentElement.nodeValue;
17627         }
17628         
17629         
17630         var ret = '';
17631         if (nodeName != 'BODY') {
17632              
17633             var i = 0;
17634             // Prints the node tagName, such as <A>, <IMG>, etc
17635             if (tagName) {
17636                 var attr = [];
17637                 for(i = 0; i < currentElement.attributes.length;i++) {
17638                     // quoting?
17639                     var aname = currentElement.attributes.item(i).name;
17640                     if (!currentElement.attributes.item(i).value.length) {
17641                         continue;
17642                     }
17643                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17644                 }
17645                 
17646                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17647             } 
17648             else {
17649                 
17650                 // eack
17651             }
17652         } else {
17653             tagName = false;
17654         }
17655         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17656             return ret;
17657         }
17658         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17659             nopadtext = true;
17660         }
17661         
17662         
17663         // Traverse the tree
17664         i = 0;
17665         var currentElementChild = currentElement.childNodes.item(i);
17666         var allText = true;
17667         var innerHTML  = '';
17668         lastnode = '';
17669         while (currentElementChild) {
17670             // Formatting code (indent the tree so it looks nice on the screen)
17671             var nopad = nopadtext;
17672             if (lastnode == 'SPAN') {
17673                 nopad  = true;
17674             }
17675             // text
17676             if  (currentElementChild.nodeName == '#text') {
17677                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17678                 if (!nopad && toadd.length > 80) {
17679                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17680                 }
17681                 innerHTML  += toadd;
17682                 
17683                 i++;
17684                 currentElementChild = currentElement.childNodes.item(i);
17685                 lastNode = '';
17686                 continue;
17687             }
17688             allText = false;
17689             
17690             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17691                 
17692             // Recursively traverse the tree structure of the child node
17693             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17694             lastnode = currentElementChild.nodeName;
17695             i++;
17696             currentElementChild=currentElement.childNodes.item(i);
17697         }
17698         
17699         ret += innerHTML;
17700         
17701         if (!allText) {
17702                 // The remaining code is mostly for formatting the tree
17703             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17704         }
17705         
17706         
17707         if (tagName) {
17708             ret+= "</"+tagName+">";
17709         }
17710         return ret;
17711         
17712     },
17713         
17714     applyBlacklists : function()
17715     {
17716         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17717         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17718         
17719         this.white = [];
17720         this.black = [];
17721         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17722             if (b.indexOf(tag) > -1) {
17723                 return;
17724             }
17725             this.white.push(tag);
17726             
17727         }, this);
17728         
17729         Roo.each(w, function(tag) {
17730             if (b.indexOf(tag) > -1) {
17731                 return;
17732             }
17733             if (this.white.indexOf(tag) > -1) {
17734                 return;
17735             }
17736             this.white.push(tag);
17737             
17738         }, this);
17739         
17740         
17741         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17742             if (w.indexOf(tag) > -1) {
17743                 return;
17744             }
17745             this.black.push(tag);
17746             
17747         }, this);
17748         
17749         Roo.each(b, function(tag) {
17750             if (w.indexOf(tag) > -1) {
17751                 return;
17752             }
17753             if (this.black.indexOf(tag) > -1) {
17754                 return;
17755             }
17756             this.black.push(tag);
17757             
17758         }, this);
17759         
17760         
17761         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17762         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17763         
17764         this.cwhite = [];
17765         this.cblack = [];
17766         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17767             if (b.indexOf(tag) > -1) {
17768                 return;
17769             }
17770             this.cwhite.push(tag);
17771             
17772         }, this);
17773         
17774         Roo.each(w, function(tag) {
17775             if (b.indexOf(tag) > -1) {
17776                 return;
17777             }
17778             if (this.cwhite.indexOf(tag) > -1) {
17779                 return;
17780             }
17781             this.cwhite.push(tag);
17782             
17783         }, this);
17784         
17785         
17786         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17787             if (w.indexOf(tag) > -1) {
17788                 return;
17789             }
17790             this.cblack.push(tag);
17791             
17792         }, this);
17793         
17794         Roo.each(b, function(tag) {
17795             if (w.indexOf(tag) > -1) {
17796                 return;
17797             }
17798             if (this.cblack.indexOf(tag) > -1) {
17799                 return;
17800             }
17801             this.cblack.push(tag);
17802             
17803         }, this);
17804     }
17805     
17806     // hide stuff that is not compatible
17807     /**
17808      * @event blur
17809      * @hide
17810      */
17811     /**
17812      * @event change
17813      * @hide
17814      */
17815     /**
17816      * @event focus
17817      * @hide
17818      */
17819     /**
17820      * @event specialkey
17821      * @hide
17822      */
17823     /**
17824      * @cfg {String} fieldClass @hide
17825      */
17826     /**
17827      * @cfg {String} focusClass @hide
17828      */
17829     /**
17830      * @cfg {String} autoCreate @hide
17831      */
17832     /**
17833      * @cfg {String} inputType @hide
17834      */
17835     /**
17836      * @cfg {String} invalidClass @hide
17837      */
17838     /**
17839      * @cfg {String} invalidText @hide
17840      */
17841     /**
17842      * @cfg {String} msgFx @hide
17843      */
17844     /**
17845      * @cfg {String} validateOnBlur @hide
17846      */
17847 });
17848
17849 Roo.HtmlEditorCore.white = [
17850         'area', 'br', 'img', 'input', 'hr', 'wbr',
17851         
17852        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17853        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17854        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17855        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17856        'table',   'ul',         'xmp', 
17857        
17858        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17859       'thead',   'tr', 
17860      
17861       'dir', 'menu', 'ol', 'ul', 'dl',
17862        
17863       'embed',  'object'
17864 ];
17865
17866
17867 Roo.HtmlEditorCore.black = [
17868     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17869         'applet', // 
17870         'base',   'basefont', 'bgsound', 'blink',  'body', 
17871         'frame',  'frameset', 'head',    'html',   'ilayer', 
17872         'iframe', 'layer',  'link',     'meta',    'object',   
17873         'script', 'style' ,'title',  'xml' // clean later..
17874 ];
17875 Roo.HtmlEditorCore.clean = [
17876     'script', 'style', 'title', 'xml'
17877 ];
17878 Roo.HtmlEditorCore.remove = [
17879     'font'
17880 ];
17881 // attributes..
17882
17883 Roo.HtmlEditorCore.ablack = [
17884     'on'
17885 ];
17886     
17887 Roo.HtmlEditorCore.aclean = [ 
17888     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17889 ];
17890
17891 // protocols..
17892 Roo.HtmlEditorCore.pwhite= [
17893         'http',  'https',  'mailto'
17894 ];
17895
17896 // white listed style attributes.
17897 Roo.HtmlEditorCore.cwhite= [
17898       //  'text-align', /// default is to allow most things..
17899       
17900          
17901 //        'font-size'//??
17902 ];
17903
17904 // black listed style attributes.
17905 Roo.HtmlEditorCore.cblack= [
17906       //  'font-size' -- this can be set by the project 
17907 ];
17908
17909
17910 Roo.HtmlEditorCore.swapCodes   =[ 
17911     [    8211, "--" ], 
17912     [    8212, "--" ], 
17913     [    8216,  "'" ],  
17914     [    8217, "'" ],  
17915     [    8220, '"' ],  
17916     [    8221, '"' ],  
17917     [    8226, "*" ],  
17918     [    8230, "..." ]
17919 ]; 
17920
17921     /*
17922  * - LGPL
17923  *
17924  * HtmlEditor
17925  * 
17926  */
17927
17928 /**
17929  * @class Roo.bootstrap.HtmlEditor
17930  * @extends Roo.bootstrap.TextArea
17931  * Bootstrap HtmlEditor class
17932
17933  * @constructor
17934  * Create a new HtmlEditor
17935  * @param {Object} config The config object
17936  */
17937
17938 Roo.bootstrap.HtmlEditor = function(config){
17939     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17940     if (!this.toolbars) {
17941         this.toolbars = [];
17942     }
17943     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17944     this.addEvents({
17945             /**
17946              * @event initialize
17947              * Fires when the editor is fully initialized (including the iframe)
17948              * @param {HtmlEditor} this
17949              */
17950             initialize: true,
17951             /**
17952              * @event activate
17953              * Fires when the editor is first receives the focus. Any insertion must wait
17954              * until after this event.
17955              * @param {HtmlEditor} this
17956              */
17957             activate: true,
17958              /**
17959              * @event beforesync
17960              * Fires before the textarea is updated with content from the editor iframe. Return false
17961              * to cancel the sync.
17962              * @param {HtmlEditor} this
17963              * @param {String} html
17964              */
17965             beforesync: true,
17966              /**
17967              * @event beforepush
17968              * Fires before the iframe editor is updated with content from the textarea. Return false
17969              * to cancel the push.
17970              * @param {HtmlEditor} this
17971              * @param {String} html
17972              */
17973             beforepush: true,
17974              /**
17975              * @event sync
17976              * Fires when the textarea is updated with content from the editor iframe.
17977              * @param {HtmlEditor} this
17978              * @param {String} html
17979              */
17980             sync: true,
17981              /**
17982              * @event push
17983              * Fires when the iframe editor is updated with content from the textarea.
17984              * @param {HtmlEditor} this
17985              * @param {String} html
17986              */
17987             push: true,
17988              /**
17989              * @event editmodechange
17990              * Fires when the editor switches edit modes
17991              * @param {HtmlEditor} this
17992              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17993              */
17994             editmodechange: true,
17995             /**
17996              * @event editorevent
17997              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17998              * @param {HtmlEditor} this
17999              */
18000             editorevent: true,
18001             /**
18002              * @event firstfocus
18003              * Fires when on first focus - needed by toolbars..
18004              * @param {HtmlEditor} this
18005              */
18006             firstfocus: true,
18007             /**
18008              * @event autosave
18009              * Auto save the htmlEditor value as a file into Events
18010              * @param {HtmlEditor} this
18011              */
18012             autosave: true,
18013             /**
18014              * @event savedpreview
18015              * preview the saved version of htmlEditor
18016              * @param {HtmlEditor} this
18017              */
18018             savedpreview: true
18019         });
18020 };
18021
18022
18023 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18024     
18025     
18026       /**
18027      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18028      */
18029     toolbars : false,
18030    
18031      /**
18032      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18033      *                        Roo.resizable.
18034      */
18035     resizable : false,
18036      /**
18037      * @cfg {Number} height (in pixels)
18038      */   
18039     height: 300,
18040    /**
18041      * @cfg {Number} width (in pixels)
18042      */   
18043     width: false,
18044     
18045     /**
18046      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18047      * 
18048      */
18049     stylesheets: false,
18050     
18051     // id of frame..
18052     frameId: false,
18053     
18054     // private properties
18055     validationEvent : false,
18056     deferHeight: true,
18057     initialized : false,
18058     activated : false,
18059     
18060     onFocus : Roo.emptyFn,
18061     iframePad:3,
18062     hideMode:'offsets',
18063     
18064     
18065     tbContainer : false,
18066     
18067     toolbarContainer :function() {
18068         return this.wrap.select('.x-html-editor-tb',true).first();
18069     },
18070
18071     /**
18072      * Protected method that will not generally be called directly. It
18073      * is called when the editor creates its toolbar. Override this method if you need to
18074      * add custom toolbar buttons.
18075      * @param {HtmlEditor} editor
18076      */
18077     createToolbar : function(){
18078         
18079         Roo.log("create toolbars");
18080         
18081         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18082         this.toolbars[0].render(this.toolbarContainer());
18083         
18084         return;
18085         
18086 //        if (!editor.toolbars || !editor.toolbars.length) {
18087 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18088 //        }
18089 //        
18090 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18091 //            editor.toolbars[i] = Roo.factory(
18092 //                    typeof(editor.toolbars[i]) == 'string' ?
18093 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18094 //                Roo.bootstrap.HtmlEditor);
18095 //            editor.toolbars[i].init(editor);
18096 //        }
18097     },
18098
18099      
18100     // private
18101     onRender : function(ct, position)
18102     {
18103        // Roo.log("Call onRender: " + this.xtype);
18104         var _t = this;
18105         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18106       
18107         this.wrap = this.inputEl().wrap({
18108             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18109         });
18110         
18111         this.editorcore.onRender(ct, position);
18112          
18113         if (this.resizable) {
18114             this.resizeEl = new Roo.Resizable(this.wrap, {
18115                 pinned : true,
18116                 wrap: true,
18117                 dynamic : true,
18118                 minHeight : this.height,
18119                 height: this.height,
18120                 handles : this.resizable,
18121                 width: this.width,
18122                 listeners : {
18123                     resize : function(r, w, h) {
18124                         _t.onResize(w,h); // -something
18125                     }
18126                 }
18127             });
18128             
18129         }
18130         this.createToolbar(this);
18131        
18132         
18133         if(!this.width && this.resizable){
18134             this.setSize(this.wrap.getSize());
18135         }
18136         if (this.resizeEl) {
18137             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18138             // should trigger onReize..
18139         }
18140         
18141     },
18142
18143     // private
18144     onResize : function(w, h)
18145     {
18146         Roo.log('resize: ' +w + ',' + h );
18147         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18148         var ew = false;
18149         var eh = false;
18150         
18151         if(this.inputEl() ){
18152             if(typeof w == 'number'){
18153                 var aw = w - this.wrap.getFrameWidth('lr');
18154                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18155                 ew = aw;
18156             }
18157             if(typeof h == 'number'){
18158                  var tbh = -11;  // fixme it needs to tool bar size!
18159                 for (var i =0; i < this.toolbars.length;i++) {
18160                     // fixme - ask toolbars for heights?
18161                     tbh += this.toolbars[i].el.getHeight();
18162                     //if (this.toolbars[i].footer) {
18163                     //    tbh += this.toolbars[i].footer.el.getHeight();
18164                     //}
18165                 }
18166               
18167                 
18168                 
18169                 
18170                 
18171                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18172                 ah -= 5; // knock a few pixes off for look..
18173                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18174                 var eh = ah;
18175             }
18176         }
18177         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18178         this.editorcore.onResize(ew,eh);
18179         
18180     },
18181
18182     /**
18183      * Toggles the editor between standard and source edit mode.
18184      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18185      */
18186     toggleSourceEdit : function(sourceEditMode)
18187     {
18188         this.editorcore.toggleSourceEdit(sourceEditMode);
18189         
18190         if(this.editorcore.sourceEditMode){
18191             Roo.log('editor - showing textarea');
18192             
18193 //            Roo.log('in');
18194 //            Roo.log(this.syncValue());
18195             this.syncValue();
18196             this.inputEl().removeClass(['hide', 'x-hidden']);
18197             this.inputEl().dom.removeAttribute('tabIndex');
18198             this.inputEl().focus();
18199         }else{
18200             Roo.log('editor - hiding textarea');
18201 //            Roo.log('out')
18202 //            Roo.log(this.pushValue()); 
18203             this.pushValue();
18204             
18205             this.inputEl().addClass(['hide', 'x-hidden']);
18206             this.inputEl().dom.setAttribute('tabIndex', -1);
18207             //this.deferFocus();
18208         }
18209          
18210         if(this.resizable){
18211             this.setSize(this.wrap.getSize());
18212         }
18213         
18214         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18215     },
18216  
18217     // private (for BoxComponent)
18218     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18219
18220     // private (for BoxComponent)
18221     getResizeEl : function(){
18222         return this.wrap;
18223     },
18224
18225     // private (for BoxComponent)
18226     getPositionEl : function(){
18227         return this.wrap;
18228     },
18229
18230     // private
18231     initEvents : function(){
18232         this.originalValue = this.getValue();
18233     },
18234
18235 //    /**
18236 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18237 //     * @method
18238 //     */
18239 //    markInvalid : Roo.emptyFn,
18240 //    /**
18241 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18242 //     * @method
18243 //     */
18244 //    clearInvalid : Roo.emptyFn,
18245
18246     setValue : function(v){
18247         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18248         this.editorcore.pushValue();
18249     },
18250
18251      
18252     // private
18253     deferFocus : function(){
18254         this.focus.defer(10, this);
18255     },
18256
18257     // doc'ed in Field
18258     focus : function(){
18259         this.editorcore.focus();
18260         
18261     },
18262       
18263
18264     // private
18265     onDestroy : function(){
18266         
18267         
18268         
18269         if(this.rendered){
18270             
18271             for (var i =0; i < this.toolbars.length;i++) {
18272                 // fixme - ask toolbars for heights?
18273                 this.toolbars[i].onDestroy();
18274             }
18275             
18276             this.wrap.dom.innerHTML = '';
18277             this.wrap.remove();
18278         }
18279     },
18280
18281     // private
18282     onFirstFocus : function(){
18283         //Roo.log("onFirstFocus");
18284         this.editorcore.onFirstFocus();
18285          for (var i =0; i < this.toolbars.length;i++) {
18286             this.toolbars[i].onFirstFocus();
18287         }
18288         
18289     },
18290     
18291     // private
18292     syncValue : function()
18293     {   
18294         this.editorcore.syncValue();
18295     },
18296     
18297     pushValue : function()
18298     {   
18299         this.editorcore.pushValue();
18300     }
18301      
18302     
18303     // hide stuff that is not compatible
18304     /**
18305      * @event blur
18306      * @hide
18307      */
18308     /**
18309      * @event change
18310      * @hide
18311      */
18312     /**
18313      * @event focus
18314      * @hide
18315      */
18316     /**
18317      * @event specialkey
18318      * @hide
18319      */
18320     /**
18321      * @cfg {String} fieldClass @hide
18322      */
18323     /**
18324      * @cfg {String} focusClass @hide
18325      */
18326     /**
18327      * @cfg {String} autoCreate @hide
18328      */
18329     /**
18330      * @cfg {String} inputType @hide
18331      */
18332     /**
18333      * @cfg {String} invalidClass @hide
18334      */
18335     /**
18336      * @cfg {String} invalidText @hide
18337      */
18338     /**
18339      * @cfg {String} msgFx @hide
18340      */
18341     /**
18342      * @cfg {String} validateOnBlur @hide
18343      */
18344 });
18345  
18346     
18347    
18348    
18349    
18350       
18351 Roo.namespace('Roo.bootstrap.htmleditor');
18352 /**
18353  * @class Roo.bootstrap.HtmlEditorToolbar1
18354  * Basic Toolbar
18355  * 
18356  * Usage:
18357  *
18358  new Roo.bootstrap.HtmlEditor({
18359     ....
18360     toolbars : [
18361         new Roo.bootstrap.HtmlEditorToolbar1({
18362             disable : { fonts: 1 , format: 1, ..., ... , ...],
18363             btns : [ .... ]
18364         })
18365     }
18366      
18367  * 
18368  * @cfg {Object} disable List of elements to disable..
18369  * @cfg {Array} btns List of additional buttons.
18370  * 
18371  * 
18372  * NEEDS Extra CSS? 
18373  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18374  */
18375  
18376 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18377 {
18378     
18379     Roo.apply(this, config);
18380     
18381     // default disabled, based on 'good practice'..
18382     this.disable = this.disable || {};
18383     Roo.applyIf(this.disable, {
18384         fontSize : true,
18385         colors : true,
18386         specialElements : true
18387     });
18388     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18389     
18390     this.editor = config.editor;
18391     this.editorcore = config.editor.editorcore;
18392     
18393     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18394     
18395     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18396     // dont call parent... till later.
18397 }
18398 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18399      
18400     bar : true,
18401     
18402     editor : false,
18403     editorcore : false,
18404     
18405     
18406     formats : [
18407         "p" ,  
18408         "h1","h2","h3","h4","h5","h6", 
18409         "pre", "code", 
18410         "abbr", "acronym", "address", "cite", "samp", "var",
18411         'div','span'
18412     ],
18413     
18414     onRender : function(ct, position)
18415     {
18416        // Roo.log("Call onRender: " + this.xtype);
18417         
18418        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18419        Roo.log(this.el);
18420        this.el.dom.style.marginBottom = '0';
18421        var _this = this;
18422        var editorcore = this.editorcore;
18423        var editor= this.editor;
18424        
18425        var children = [];
18426        var btn = function(id,cmd , toggle, handler){
18427        
18428             var  event = toggle ? 'toggle' : 'click';
18429        
18430             var a = {
18431                 size : 'sm',
18432                 xtype: 'Button',
18433                 xns: Roo.bootstrap,
18434                 glyphicon : id,
18435                 cmd : id || cmd,
18436                 enableToggle:toggle !== false,
18437                 //html : 'submit'
18438                 pressed : toggle ? false : null,
18439                 listeners : {}
18440             }
18441             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18442                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18443             }
18444             children.push(a);
18445             return a;
18446        }
18447         
18448         var style = {
18449                 xtype: 'Button',
18450                 size : 'sm',
18451                 xns: Roo.bootstrap,
18452                 glyphicon : 'font',
18453                 //html : 'submit'
18454                 menu : {
18455                     xtype: 'Menu',
18456                     xns: Roo.bootstrap,
18457                     items:  []
18458                 }
18459         };
18460         Roo.each(this.formats, function(f) {
18461             style.menu.items.push({
18462                 xtype :'MenuItem',
18463                 xns: Roo.bootstrap,
18464                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18465                 tagname : f,
18466                 listeners : {
18467                     click : function()
18468                     {
18469                         editorcore.insertTag(this.tagname);
18470                         editor.focus();
18471                     }
18472                 }
18473                 
18474             });
18475         });
18476          children.push(style);   
18477             
18478             
18479         btn('bold',false,true);
18480         btn('italic',false,true);
18481         btn('align-left', 'justifyleft',true);
18482         btn('align-center', 'justifycenter',true);
18483         btn('align-right' , 'justifyright',true);
18484         btn('link', false, false, function(btn) {
18485             //Roo.log("create link?");
18486             var url = prompt(this.createLinkText, this.defaultLinkValue);
18487             if(url && url != 'http:/'+'/'){
18488                 this.editorcore.relayCmd('createlink', url);
18489             }
18490         }),
18491         btn('list','insertunorderedlist',true);
18492         btn('pencil', false,true, function(btn){
18493                 Roo.log(this);
18494                 
18495                 this.toggleSourceEdit(btn.pressed);
18496         });
18497         /*
18498         var cog = {
18499                 xtype: 'Button',
18500                 size : 'sm',
18501                 xns: Roo.bootstrap,
18502                 glyphicon : 'cog',
18503                 //html : 'submit'
18504                 menu : {
18505                     xtype: 'Menu',
18506                     xns: Roo.bootstrap,
18507                     items:  []
18508                 }
18509         };
18510         
18511         cog.menu.items.push({
18512             xtype :'MenuItem',
18513             xns: Roo.bootstrap,
18514             html : Clean styles,
18515             tagname : f,
18516             listeners : {
18517                 click : function()
18518                 {
18519                     editorcore.insertTag(this.tagname);
18520                     editor.focus();
18521                 }
18522             }
18523             
18524         });
18525        */
18526         
18527          
18528        this.xtype = 'NavSimplebar';
18529         
18530         for(var i=0;i< children.length;i++) {
18531             
18532             this.buttons.add(this.addxtypeChild(children[i]));
18533             
18534         }
18535         
18536         editor.on('editorevent', this.updateToolbar, this);
18537     },
18538     onBtnClick : function(id)
18539     {
18540        this.editorcore.relayCmd(id);
18541        this.editorcore.focus();
18542     },
18543     
18544     /**
18545      * Protected method that will not generally be called directly. It triggers
18546      * a toolbar update by reading the markup state of the current selection in the editor.
18547      */
18548     updateToolbar: function(){
18549
18550         if(!this.editorcore.activated){
18551             this.editor.onFirstFocus(); // is this neeed?
18552             return;
18553         }
18554
18555         var btns = this.buttons; 
18556         var doc = this.editorcore.doc;
18557         btns.get('bold').setActive(doc.queryCommandState('bold'));
18558         btns.get('italic').setActive(doc.queryCommandState('italic'));
18559         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18560         
18561         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18562         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18563         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18564         
18565         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18566         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18567          /*
18568         
18569         var ans = this.editorcore.getAllAncestors();
18570         if (this.formatCombo) {
18571             
18572             
18573             var store = this.formatCombo.store;
18574             this.formatCombo.setValue("");
18575             for (var i =0; i < ans.length;i++) {
18576                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18577                     // select it..
18578                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18579                     break;
18580                 }
18581             }
18582         }
18583         
18584         
18585         
18586         // hides menus... - so this cant be on a menu...
18587         Roo.bootstrap.MenuMgr.hideAll();
18588         */
18589         Roo.bootstrap.MenuMgr.hideAll();
18590         //this.editorsyncValue();
18591     },
18592     onFirstFocus: function() {
18593         this.buttons.each(function(item){
18594            item.enable();
18595         });
18596     },
18597     toggleSourceEdit : function(sourceEditMode){
18598         
18599           
18600         if(sourceEditMode){
18601             Roo.log("disabling buttons");
18602            this.buttons.each( function(item){
18603                 if(item.cmd != 'pencil'){
18604                     item.disable();
18605                 }
18606             });
18607           
18608         }else{
18609             Roo.log("enabling buttons");
18610             if(this.editorcore.initialized){
18611                 this.buttons.each( function(item){
18612                     item.enable();
18613                 });
18614             }
18615             
18616         }
18617         Roo.log("calling toggole on editor");
18618         // tell the editor that it's been pressed..
18619         this.editor.toggleSourceEdit(sourceEditMode);
18620        
18621     }
18622 });
18623
18624
18625
18626
18627
18628 /**
18629  * @class Roo.bootstrap.Table.AbstractSelectionModel
18630  * @extends Roo.util.Observable
18631  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18632  * implemented by descendant classes.  This class should not be directly instantiated.
18633  * @constructor
18634  */
18635 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18636     this.locked = false;
18637     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18638 };
18639
18640
18641 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18642     /** @ignore Called by the grid automatically. Do not call directly. */
18643     init : function(grid){
18644         this.grid = grid;
18645         this.initEvents();
18646     },
18647
18648     /**
18649      * Locks the selections.
18650      */
18651     lock : function(){
18652         this.locked = true;
18653     },
18654
18655     /**
18656      * Unlocks the selections.
18657      */
18658     unlock : function(){
18659         this.locked = false;
18660     },
18661
18662     /**
18663      * Returns true if the selections are locked.
18664      * @return {Boolean}
18665      */
18666     isLocked : function(){
18667         return this.locked;
18668     }
18669 });
18670 /**
18671  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18672  * @class Roo.bootstrap.Table.RowSelectionModel
18673  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18674  * It supports multiple selections and keyboard selection/navigation. 
18675  * @constructor
18676  * @param {Object} config
18677  */
18678
18679 Roo.bootstrap.Table.RowSelectionModel = function(config){
18680     Roo.apply(this, config);
18681     this.selections = new Roo.util.MixedCollection(false, function(o){
18682         return o.id;
18683     });
18684
18685     this.last = false;
18686     this.lastActive = false;
18687
18688     this.addEvents({
18689         /**
18690              * @event selectionchange
18691              * Fires when the selection changes
18692              * @param {SelectionModel} this
18693              */
18694             "selectionchange" : true,
18695         /**
18696              * @event afterselectionchange
18697              * Fires after the selection changes (eg. by key press or clicking)
18698              * @param {SelectionModel} this
18699              */
18700             "afterselectionchange" : true,
18701         /**
18702              * @event beforerowselect
18703              * Fires when a row is selected being selected, return false to cancel.
18704              * @param {SelectionModel} this
18705              * @param {Number} rowIndex The selected index
18706              * @param {Boolean} keepExisting False if other selections will be cleared
18707              */
18708             "beforerowselect" : true,
18709         /**
18710              * @event rowselect
18711              * Fires when a row is selected.
18712              * @param {SelectionModel} this
18713              * @param {Number} rowIndex The selected index
18714              * @param {Roo.data.Record} r The record
18715              */
18716             "rowselect" : true,
18717         /**
18718              * @event rowdeselect
18719              * Fires when a row is deselected.
18720              * @param {SelectionModel} this
18721              * @param {Number} rowIndex The selected index
18722              */
18723         "rowdeselect" : true
18724     });
18725     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18726     this.locked = false;
18727 };
18728
18729 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18730     /**
18731      * @cfg {Boolean} singleSelect
18732      * True to allow selection of only one row at a time (defaults to false)
18733      */
18734     singleSelect : false,
18735
18736     // private
18737     initEvents : function(){
18738
18739         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18740             this.grid.on("mousedown", this.handleMouseDown, this);
18741         }else{ // allow click to work like normal
18742             this.grid.on("rowclick", this.handleDragableRowClick, this);
18743         }
18744
18745         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18746             "up" : function(e){
18747                 if(!e.shiftKey){
18748                     this.selectPrevious(e.shiftKey);
18749                 }else if(this.last !== false && this.lastActive !== false){
18750                     var last = this.last;
18751                     this.selectRange(this.last,  this.lastActive-1);
18752                     this.grid.getView().focusRow(this.lastActive);
18753                     if(last !== false){
18754                         this.last = last;
18755                     }
18756                 }else{
18757                     this.selectFirstRow();
18758                 }
18759                 this.fireEvent("afterselectionchange", this);
18760             },
18761             "down" : function(e){
18762                 if(!e.shiftKey){
18763                     this.selectNext(e.shiftKey);
18764                 }else if(this.last !== false && this.lastActive !== false){
18765                     var last = this.last;
18766                     this.selectRange(this.last,  this.lastActive+1);
18767                     this.grid.getView().focusRow(this.lastActive);
18768                     if(last !== false){
18769                         this.last = last;
18770                     }
18771                 }else{
18772                     this.selectFirstRow();
18773                 }
18774                 this.fireEvent("afterselectionchange", this);
18775             },
18776             scope: this
18777         });
18778
18779         var view = this.grid.view;
18780         view.on("refresh", this.onRefresh, this);
18781         view.on("rowupdated", this.onRowUpdated, this);
18782         view.on("rowremoved", this.onRemove, this);
18783     },
18784
18785     // private
18786     onRefresh : function(){
18787         var ds = this.grid.dataSource, i, v = this.grid.view;
18788         var s = this.selections;
18789         s.each(function(r){
18790             if((i = ds.indexOfId(r.id)) != -1){
18791                 v.onRowSelect(i);
18792             }else{
18793                 s.remove(r);
18794             }
18795         });
18796     },
18797
18798     // private
18799     onRemove : function(v, index, r){
18800         this.selections.remove(r);
18801     },
18802
18803     // private
18804     onRowUpdated : function(v, index, r){
18805         if(this.isSelected(r)){
18806             v.onRowSelect(index);
18807         }
18808     },
18809
18810     /**
18811      * Select records.
18812      * @param {Array} records The records to select
18813      * @param {Boolean} keepExisting (optional) True to keep existing selections
18814      */
18815     selectRecords : function(records, keepExisting){
18816         if(!keepExisting){
18817             this.clearSelections();
18818         }
18819         var ds = this.grid.dataSource;
18820         for(var i = 0, len = records.length; i < len; i++){
18821             this.selectRow(ds.indexOf(records[i]), true);
18822         }
18823     },
18824
18825     /**
18826      * Gets the number of selected rows.
18827      * @return {Number}
18828      */
18829     getCount : function(){
18830         return this.selections.length;
18831     },
18832
18833     /**
18834      * Selects the first row in the grid.
18835      */
18836     selectFirstRow : function(){
18837         this.selectRow(0);
18838     },
18839
18840     /**
18841      * Select the last row.
18842      * @param {Boolean} keepExisting (optional) True to keep existing selections
18843      */
18844     selectLastRow : function(keepExisting){
18845         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18846     },
18847
18848     /**
18849      * Selects the row immediately following the last selected row.
18850      * @param {Boolean} keepExisting (optional) True to keep existing selections
18851      */
18852     selectNext : function(keepExisting){
18853         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18854             this.selectRow(this.last+1, keepExisting);
18855             this.grid.getView().focusRow(this.last);
18856         }
18857     },
18858
18859     /**
18860      * Selects the row that precedes the last selected row.
18861      * @param {Boolean} keepExisting (optional) True to keep existing selections
18862      */
18863     selectPrevious : function(keepExisting){
18864         if(this.last){
18865             this.selectRow(this.last-1, keepExisting);
18866             this.grid.getView().focusRow(this.last);
18867         }
18868     },
18869
18870     /**
18871      * Returns the selected records
18872      * @return {Array} Array of selected records
18873      */
18874     getSelections : function(){
18875         return [].concat(this.selections.items);
18876     },
18877
18878     /**
18879      * Returns the first selected record.
18880      * @return {Record}
18881      */
18882     getSelected : function(){
18883         return this.selections.itemAt(0);
18884     },
18885
18886
18887     /**
18888      * Clears all selections.
18889      */
18890     clearSelections : function(fast){
18891         if(this.locked) return;
18892         if(fast !== true){
18893             var ds = this.grid.dataSource;
18894             var s = this.selections;
18895             s.each(function(r){
18896                 this.deselectRow(ds.indexOfId(r.id));
18897             }, this);
18898             s.clear();
18899         }else{
18900             this.selections.clear();
18901         }
18902         this.last = false;
18903     },
18904
18905
18906     /**
18907      * Selects all rows.
18908      */
18909     selectAll : function(){
18910         if(this.locked) return;
18911         this.selections.clear();
18912         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18913             this.selectRow(i, true);
18914         }
18915     },
18916
18917     /**
18918      * Returns True if there is a selection.
18919      * @return {Boolean}
18920      */
18921     hasSelection : function(){
18922         return this.selections.length > 0;
18923     },
18924
18925     /**
18926      * Returns True if the specified row is selected.
18927      * @param {Number/Record} record The record or index of the record to check
18928      * @return {Boolean}
18929      */
18930     isSelected : function(index){
18931         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18932         return (r && this.selections.key(r.id) ? true : false);
18933     },
18934
18935     /**
18936      * Returns True if the specified record id is selected.
18937      * @param {String} id The id of record to check
18938      * @return {Boolean}
18939      */
18940     isIdSelected : function(id){
18941         return (this.selections.key(id) ? true : false);
18942     },
18943
18944     // private
18945     handleMouseDown : function(e, t){
18946         var view = this.grid.getView(), rowIndex;
18947         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18948             return;
18949         };
18950         if(e.shiftKey && this.last !== false){
18951             var last = this.last;
18952             this.selectRange(last, rowIndex, e.ctrlKey);
18953             this.last = last; // reset the last
18954             view.focusRow(rowIndex);
18955         }else{
18956             var isSelected = this.isSelected(rowIndex);
18957             if(e.button !== 0 && isSelected){
18958                 view.focusRow(rowIndex);
18959             }else if(e.ctrlKey && isSelected){
18960                 this.deselectRow(rowIndex);
18961             }else if(!isSelected){
18962                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18963                 view.focusRow(rowIndex);
18964             }
18965         }
18966         this.fireEvent("afterselectionchange", this);
18967     },
18968     // private
18969     handleDragableRowClick :  function(grid, rowIndex, e) 
18970     {
18971         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18972             this.selectRow(rowIndex, false);
18973             grid.view.focusRow(rowIndex);
18974              this.fireEvent("afterselectionchange", this);
18975         }
18976     },
18977     
18978     /**
18979      * Selects multiple rows.
18980      * @param {Array} rows Array of the indexes of the row to select
18981      * @param {Boolean} keepExisting (optional) True to keep existing selections
18982      */
18983     selectRows : function(rows, keepExisting){
18984         if(!keepExisting){
18985             this.clearSelections();
18986         }
18987         for(var i = 0, len = rows.length; i < len; i++){
18988             this.selectRow(rows[i], true);
18989         }
18990     },
18991
18992     /**
18993      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18994      * @param {Number} startRow The index of the first row in the range
18995      * @param {Number} endRow The index of the last row in the range
18996      * @param {Boolean} keepExisting (optional) True to retain existing selections
18997      */
18998     selectRange : function(startRow, endRow, keepExisting){
18999         if(this.locked) return;
19000         if(!keepExisting){
19001             this.clearSelections();
19002         }
19003         if(startRow <= endRow){
19004             for(var i = startRow; i <= endRow; i++){
19005                 this.selectRow(i, true);
19006             }
19007         }else{
19008             for(var i = startRow; i >= endRow; i--){
19009                 this.selectRow(i, true);
19010             }
19011         }
19012     },
19013
19014     /**
19015      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19016      * @param {Number} startRow The index of the first row in the range
19017      * @param {Number} endRow The index of the last row in the range
19018      */
19019     deselectRange : function(startRow, endRow, preventViewNotify){
19020         if(this.locked) return;
19021         for(var i = startRow; i <= endRow; i++){
19022             this.deselectRow(i, preventViewNotify);
19023         }
19024     },
19025
19026     /**
19027      * Selects a row.
19028      * @param {Number} row The index of the row to select
19029      * @param {Boolean} keepExisting (optional) True to keep existing selections
19030      */
19031     selectRow : function(index, keepExisting, preventViewNotify){
19032         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19033         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19034             if(!keepExisting || this.singleSelect){
19035                 this.clearSelections();
19036             }
19037             var r = this.grid.dataSource.getAt(index);
19038             this.selections.add(r);
19039             this.last = this.lastActive = index;
19040             if(!preventViewNotify){
19041                 this.grid.getView().onRowSelect(index);
19042             }
19043             this.fireEvent("rowselect", this, index, r);
19044             this.fireEvent("selectionchange", this);
19045         }
19046     },
19047
19048     /**
19049      * Deselects a row.
19050      * @param {Number} row The index of the row to deselect
19051      */
19052     deselectRow : function(index, preventViewNotify){
19053         if(this.locked) return;
19054         if(this.last == index){
19055             this.last = false;
19056         }
19057         if(this.lastActive == index){
19058             this.lastActive = false;
19059         }
19060         var r = this.grid.dataSource.getAt(index);
19061         this.selections.remove(r);
19062         if(!preventViewNotify){
19063             this.grid.getView().onRowDeselect(index);
19064         }
19065         this.fireEvent("rowdeselect", this, index);
19066         this.fireEvent("selectionchange", this);
19067     },
19068
19069     // private
19070     restoreLast : function(){
19071         if(this._last){
19072             this.last = this._last;
19073         }
19074     },
19075
19076     // private
19077     acceptsNav : function(row, col, cm){
19078         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19079     },
19080
19081     // private
19082     onEditorKey : function(field, e){
19083         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19084         if(k == e.TAB){
19085             e.stopEvent();
19086             ed.completeEdit();
19087             if(e.shiftKey){
19088                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19089             }else{
19090                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19091             }
19092         }else if(k == e.ENTER && !e.ctrlKey){
19093             e.stopEvent();
19094             ed.completeEdit();
19095             if(e.shiftKey){
19096                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19097             }else{
19098                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19099             }
19100         }else if(k == e.ESC){
19101             ed.cancelEdit();
19102         }
19103         if(newCell){
19104             g.startEditing(newCell[0], newCell[1]);
19105         }
19106     }
19107 });/*
19108  * Based on:
19109  * Ext JS Library 1.1.1
19110  * Copyright(c) 2006-2007, Ext JS, LLC.
19111  *
19112  * Originally Released Under LGPL - original licence link has changed is not relivant.
19113  *
19114  * Fork - LGPL
19115  * <script type="text/javascript">
19116  */
19117  
19118 /**
19119  * @class Roo.bootstrap.PagingToolbar
19120  * @extends Roo.Row
19121  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19122  * @constructor
19123  * Create a new PagingToolbar
19124  * @param {Object} config The config object
19125  */
19126 Roo.bootstrap.PagingToolbar = function(config)
19127 {
19128     // old args format still supported... - xtype is prefered..
19129         // created from xtype...
19130     var ds = config.dataSource;
19131     this.toolbarItems = [];
19132     if (config.items) {
19133         this.toolbarItems = config.items;
19134 //        config.items = [];
19135     }
19136     
19137     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19138     this.ds = ds;
19139     this.cursor = 0;
19140     if (ds) { 
19141         this.bind(ds);
19142     }
19143     
19144     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19145     
19146 };
19147
19148 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19149     /**
19150      * @cfg {Roo.data.Store} dataSource
19151      * The underlying data store providing the paged data
19152      */
19153     /**
19154      * @cfg {String/HTMLElement/Element} container
19155      * container The id or element that will contain the toolbar
19156      */
19157     /**
19158      * @cfg {Boolean} displayInfo
19159      * True to display the displayMsg (defaults to false)
19160      */
19161     /**
19162      * @cfg {Number} pageSize
19163      * The number of records to display per page (defaults to 20)
19164      */
19165     pageSize: 20,
19166     /**
19167      * @cfg {String} displayMsg
19168      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19169      */
19170     displayMsg : 'Displaying {0} - {1} of {2}',
19171     /**
19172      * @cfg {String} emptyMsg
19173      * The message to display when no records are found (defaults to "No data to display")
19174      */
19175     emptyMsg : 'No data to display',
19176     /**
19177      * Customizable piece of the default paging text (defaults to "Page")
19178      * @type String
19179      */
19180     beforePageText : "Page",
19181     /**
19182      * Customizable piece of the default paging text (defaults to "of %0")
19183      * @type String
19184      */
19185     afterPageText : "of {0}",
19186     /**
19187      * Customizable piece of the default paging text (defaults to "First Page")
19188      * @type String
19189      */
19190     firstText : "First Page",
19191     /**
19192      * Customizable piece of the default paging text (defaults to "Previous Page")
19193      * @type String
19194      */
19195     prevText : "Previous Page",
19196     /**
19197      * Customizable piece of the default paging text (defaults to "Next Page")
19198      * @type String
19199      */
19200     nextText : "Next Page",
19201     /**
19202      * Customizable piece of the default paging text (defaults to "Last Page")
19203      * @type String
19204      */
19205     lastText : "Last Page",
19206     /**
19207      * Customizable piece of the default paging text (defaults to "Refresh")
19208      * @type String
19209      */
19210     refreshText : "Refresh",
19211
19212     buttons : false,
19213     // private
19214     onRender : function(ct, position) 
19215     {
19216         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19217         this.navgroup.parentId = this.id;
19218         this.navgroup.onRender(this.el, null);
19219         // add the buttons to the navgroup
19220         
19221         if(this.displayInfo){
19222             Roo.log(this.el.select('ul.navbar-nav',true).first());
19223             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19224             this.displayEl = this.el.select('.x-paging-info', true).first();
19225 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19226 //            this.displayEl = navel.el.select('span',true).first();
19227         }
19228         
19229         var _this = this;
19230         
19231         if(this.buttons){
19232             Roo.each(_this.buttons, function(e){
19233                Roo.factory(e).onRender(_this.el, null);
19234             });
19235         }
19236             
19237         Roo.each(_this.toolbarItems, function(e) {
19238             _this.navgroup.addItem(e);
19239         });
19240         
19241         this.first = this.navgroup.addItem({
19242             tooltip: this.firstText,
19243             cls: "prev",
19244             icon : 'fa fa-backward',
19245             disabled: true,
19246             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19247         });
19248         
19249         this.prev =  this.navgroup.addItem({
19250             tooltip: this.prevText,
19251             cls: "prev",
19252             icon : 'fa fa-step-backward',
19253             disabled: true,
19254             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19255         });
19256     //this.addSeparator();
19257         
19258         
19259         var field = this.navgroup.addItem( {
19260             tagtype : 'span',
19261             cls : 'x-paging-position',
19262             
19263             html : this.beforePageText  +
19264                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19265                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19266          } ); //?? escaped?
19267         
19268         this.field = field.el.select('input', true).first();
19269         this.field.on("keydown", this.onPagingKeydown, this);
19270         this.field.on("focus", function(){this.dom.select();});
19271     
19272     
19273         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19274         //this.field.setHeight(18);
19275         //this.addSeparator();
19276         this.next = this.navgroup.addItem({
19277             tooltip: this.nextText,
19278             cls: "next",
19279             html : ' <i class="fa fa-step-forward">',
19280             disabled: true,
19281             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19282         });
19283         this.last = this.navgroup.addItem({
19284             tooltip: this.lastText,
19285             icon : 'fa fa-forward',
19286             cls: "next",
19287             disabled: true,
19288             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19289         });
19290     //this.addSeparator();
19291         this.loading = this.navgroup.addItem({
19292             tooltip: this.refreshText,
19293             icon: 'fa fa-refresh',
19294             
19295             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19296         });
19297
19298     },
19299
19300     // private
19301     updateInfo : function(){
19302         if(this.displayEl){
19303             var count = this.ds.getCount();
19304             var msg = count == 0 ?
19305                 this.emptyMsg :
19306                 String.format(
19307                     this.displayMsg,
19308                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19309                 );
19310             this.displayEl.update(msg);
19311         }
19312     },
19313
19314     // private
19315     onLoad : function(ds, r, o){
19316        this.cursor = o.params ? o.params.start : 0;
19317        var d = this.getPageData(),
19318             ap = d.activePage,
19319             ps = d.pages;
19320         
19321        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19322        this.field.dom.value = ap;
19323        this.first.setDisabled(ap == 1);
19324        this.prev.setDisabled(ap == 1);
19325        this.next.setDisabled(ap == ps);
19326        this.last.setDisabled(ap == ps);
19327        this.loading.enable();
19328        this.updateInfo();
19329     },
19330
19331     // private
19332     getPageData : function(){
19333         var total = this.ds.getTotalCount();
19334         return {
19335             total : total,
19336             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19337             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19338         };
19339     },
19340
19341     // private
19342     onLoadError : function(){
19343         this.loading.enable();
19344     },
19345
19346     // private
19347     onPagingKeydown : function(e){
19348         var k = e.getKey();
19349         var d = this.getPageData();
19350         if(k == e.RETURN){
19351             var v = this.field.dom.value, pageNum;
19352             if(!v || isNaN(pageNum = parseInt(v, 10))){
19353                 this.field.dom.value = d.activePage;
19354                 return;
19355             }
19356             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19357             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19358             e.stopEvent();
19359         }
19360         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))
19361         {
19362           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19363           this.field.dom.value = pageNum;
19364           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19365           e.stopEvent();
19366         }
19367         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19368         {
19369           var v = this.field.dom.value, pageNum; 
19370           var increment = (e.shiftKey) ? 10 : 1;
19371           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19372             increment *= -1;
19373           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19374             this.field.dom.value = d.activePage;
19375             return;
19376           }
19377           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19378           {
19379             this.field.dom.value = parseInt(v, 10) + increment;
19380             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19381             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19382           }
19383           e.stopEvent();
19384         }
19385     },
19386
19387     // private
19388     beforeLoad : function(){
19389         if(this.loading){
19390             this.loading.disable();
19391         }
19392     },
19393
19394     // private
19395     onClick : function(which){
19396         var ds = this.ds;
19397         if (!ds) {
19398             return;
19399         }
19400         switch(which){
19401             case "first":
19402                 ds.load({params:{start: 0, limit: this.pageSize}});
19403             break;
19404             case "prev":
19405                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19406             break;
19407             case "next":
19408                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19409             break;
19410             case "last":
19411                 var total = ds.getTotalCount();
19412                 var extra = total % this.pageSize;
19413                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19414                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19415             break;
19416             case "refresh":
19417                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19418             break;
19419         }
19420     },
19421
19422     /**
19423      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19424      * @param {Roo.data.Store} store The data store to unbind
19425      */
19426     unbind : function(ds){
19427         ds.un("beforeload", this.beforeLoad, this);
19428         ds.un("load", this.onLoad, this);
19429         ds.un("loadexception", this.onLoadError, this);
19430         ds.un("remove", this.updateInfo, this);
19431         ds.un("add", this.updateInfo, this);
19432         this.ds = undefined;
19433     },
19434
19435     /**
19436      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19437      * @param {Roo.data.Store} store The data store to bind
19438      */
19439     bind : function(ds){
19440         ds.on("beforeload", this.beforeLoad, this);
19441         ds.on("load", this.onLoad, this);
19442         ds.on("loadexception", this.onLoadError, this);
19443         ds.on("remove", this.updateInfo, this);
19444         ds.on("add", this.updateInfo, this);
19445         this.ds = ds;
19446     }
19447 });/*
19448  * - LGPL
19449  *
19450  * element
19451  * 
19452  */
19453
19454 /**
19455  * @class Roo.bootstrap.MessageBar
19456  * @extends Roo.bootstrap.Component
19457  * Bootstrap MessageBar class
19458  * @cfg {String} html contents of the MessageBar
19459  * @cfg {String} weight (info | success | warning | danger) default info
19460  * @cfg {String} beforeClass insert the bar before the given class
19461  * @cfg {Boolean} closable (true | false) default false
19462  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19463  * 
19464  * @constructor
19465  * Create a new Element
19466  * @param {Object} config The config object
19467  */
19468
19469 Roo.bootstrap.MessageBar = function(config){
19470     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19471 };
19472
19473 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19474     
19475     html: '',
19476     weight: 'info',
19477     closable: false,
19478     fixed: false,
19479     beforeClass: 'bootstrap-sticky-wrap',
19480     
19481     getAutoCreate : function(){
19482         
19483         var cfg = {
19484             tag: 'div',
19485             cls: 'alert alert-dismissable alert-' + this.weight,
19486             cn: [
19487                 {
19488                     tag: 'span',
19489                     cls: 'message',
19490                     html: this.html || ''
19491                 }
19492             ]
19493         }
19494         
19495         if(this.fixed){
19496             cfg.cls += ' alert-messages-fixed';
19497         }
19498         
19499         if(this.closable){
19500             cfg.cn.push({
19501                 tag: 'button',
19502                 cls: 'close',
19503                 html: 'x'
19504             });
19505         }
19506         
19507         return cfg;
19508     },
19509     
19510     onRender : function(ct, position)
19511     {
19512         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19513         
19514         if(!this.el){
19515             var cfg = Roo.apply({},  this.getAutoCreate());
19516             cfg.id = Roo.id();
19517             
19518             if (this.cls) {
19519                 cfg.cls += ' ' + this.cls;
19520             }
19521             if (this.style) {
19522                 cfg.style = this.style;
19523             }
19524             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19525             
19526             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19527         }
19528         
19529         this.el.select('>button.close').on('click', this.hide, this);
19530         
19531     },
19532     
19533     show : function()
19534     {
19535         if (!this.rendered) {
19536             this.render();
19537         }
19538         
19539         this.el.show();
19540         
19541         this.fireEvent('show', this);
19542         
19543     },
19544     
19545     hide : function()
19546     {
19547         if (!this.rendered) {
19548             this.render();
19549         }
19550         
19551         this.el.hide();
19552         
19553         this.fireEvent('hide', this);
19554     },
19555     
19556     update : function()
19557     {
19558 //        var e = this.el.dom.firstChild;
19559 //        
19560 //        if(this.closable){
19561 //            e = e.nextSibling;
19562 //        }
19563 //        
19564 //        e.data = this.html || '';
19565
19566         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19567     }
19568    
19569 });
19570
19571  
19572
19573      /*
19574  * - LGPL
19575  *
19576  * Graph
19577  * 
19578  */
19579
19580
19581 /**
19582  * @class Roo.bootstrap.Graph
19583  * @extends Roo.bootstrap.Component
19584  * Bootstrap Graph class
19585 > Prameters
19586  -sm {number} sm 4
19587  -md {number} md 5
19588  @cfg {String} graphtype  bar | vbar | pie
19589  @cfg {number} g_x coodinator | centre x (pie)
19590  @cfg {number} g_y coodinator | centre y (pie)
19591  @cfg {number} g_r radius (pie)
19592  @cfg {number} g_height height of the chart (respected by all elements in the set)
19593  @cfg {number} g_width width of the chart (respected by all elements in the set)
19594  @cfg {Object} title The title of the chart
19595     
19596  -{Array}  values
19597  -opts (object) options for the chart 
19598      o {
19599      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19600      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19601      o vgutter (number)
19602      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.
19603      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19604      o to
19605      o stretch (boolean)
19606      o }
19607  -opts (object) options for the pie
19608      o{
19609      o cut
19610      o startAngle (number)
19611      o endAngle (number)
19612      } 
19613  *
19614  * @constructor
19615  * Create a new Input
19616  * @param {Object} config The config object
19617  */
19618
19619 Roo.bootstrap.Graph = function(config){
19620     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19621     
19622     this.addEvents({
19623         // img events
19624         /**
19625          * @event click
19626          * The img click event for the img.
19627          * @param {Roo.EventObject} e
19628          */
19629         "click" : true
19630     });
19631 };
19632
19633 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19634     
19635     sm: 4,
19636     md: 5,
19637     graphtype: 'bar',
19638     g_height: 250,
19639     g_width: 400,
19640     g_x: 50,
19641     g_y: 50,
19642     g_r: 30,
19643     opts:{
19644         //g_colors: this.colors,
19645         g_type: 'soft',
19646         g_gutter: '20%'
19647
19648     },
19649     title : false,
19650
19651     getAutoCreate : function(){
19652         
19653         var cfg = {
19654             tag: 'div',
19655             html : null
19656         }
19657         
19658         
19659         return  cfg;
19660     },
19661
19662     onRender : function(ct,position){
19663         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19664         this.raphael = Raphael(this.el.dom);
19665         
19666                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19667                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19668                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19669                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19670                 /*
19671                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19672                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19673                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19674                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19675                 
19676                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19677                 r.barchart(330, 10, 300, 220, data1);
19678                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19679                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19680                 */
19681                 
19682                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19683                 // r.barchart(30, 30, 560, 250,  xdata, {
19684                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19685                 //     axis : "0 0 1 1",
19686                 //     axisxlabels :  xdata
19687                 //     //yvalues : cols,
19688                    
19689                 // });
19690 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19691 //        
19692 //        this.load(null,xdata,{
19693 //                axis : "0 0 1 1",
19694 //                axisxlabels :  xdata
19695 //                });
19696
19697     },
19698
19699     load : function(graphtype,xdata,opts){
19700         this.raphael.clear();
19701         if(!graphtype) {
19702             graphtype = this.graphtype;
19703         }
19704         if(!opts){
19705             opts = this.opts;
19706         }
19707         var r = this.raphael,
19708             fin = function () {
19709                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19710             },
19711             fout = function () {
19712                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19713             },
19714             pfin = function() {
19715                 this.sector.stop();
19716                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19717
19718                 if (this.label) {
19719                     this.label[0].stop();
19720                     this.label[0].attr({ r: 7.5 });
19721                     this.label[1].attr({ "font-weight": 800 });
19722                 }
19723             },
19724             pfout = function() {
19725                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19726
19727                 if (this.label) {
19728                     this.label[0].animate({ r: 5 }, 500, "bounce");
19729                     this.label[1].attr({ "font-weight": 400 });
19730                 }
19731             };
19732
19733         switch(graphtype){
19734             case 'bar':
19735                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19736                 break;
19737             case 'hbar':
19738                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19739                 break;
19740             case 'pie':
19741 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19742 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19743 //            
19744                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19745                 
19746                 break;
19747
19748         }
19749         
19750         if(this.title){
19751             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19752         }
19753         
19754     },
19755     
19756     setTitle: function(o)
19757     {
19758         this.title = o;
19759     },
19760     
19761     initEvents: function() {
19762         
19763         if(!this.href){
19764             this.el.on('click', this.onClick, this);
19765         }
19766     },
19767     
19768     onClick : function(e)
19769     {
19770         Roo.log('img onclick');
19771         this.fireEvent('click', this, e);
19772     }
19773    
19774 });
19775
19776  
19777 /*
19778  * - LGPL
19779  *
19780  * numberBox
19781  * 
19782  */
19783 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19784
19785 /**
19786  * @class Roo.bootstrap.dash.NumberBox
19787  * @extends Roo.bootstrap.Component
19788  * Bootstrap NumberBox class
19789  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19790  * @cfg {String} headline Box headline
19791  * @cfg {String} content Box content
19792  * @cfg {String} icon Box icon
19793  * @cfg {String} footer Footer text
19794  * @cfg {String} fhref Footer href
19795  * 
19796  * @constructor
19797  * Create a new NumberBox
19798  * @param {Object} config The config object
19799  */
19800
19801
19802 Roo.bootstrap.dash.NumberBox = function(config){
19803     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19804     
19805 };
19806
19807 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19808     
19809     bgcolor : 'aqua',
19810     headline : '',
19811     content : '',
19812     icon : '',
19813     footer : '',
19814     fhref : '',
19815     ficon : '',
19816     
19817     getAutoCreate : function(){
19818         
19819         var cfg = {
19820             tag : 'div',
19821             cls : 'small-box bg-' + this.bgcolor,
19822             cn : [
19823                 {
19824                     tag : 'div',
19825                     cls : 'inner',
19826                     cn :[
19827                         {
19828                             tag : 'h3',
19829                             cls : 'roo-headline',
19830                             html : this.headline
19831                         },
19832                         {
19833                             tag : 'p',
19834                             cls : 'roo-content',
19835                             html : this.content
19836                         }
19837                     ]
19838                 }
19839             ]
19840         }
19841         
19842         if(this.icon){
19843             cfg.cn.push({
19844                 tag : 'div',
19845                 cls : 'icon',
19846                 cn :[
19847                     {
19848                         tag : 'i',
19849                         cls : 'ion ' + this.icon
19850                     }
19851                 ]
19852             });
19853         }
19854         
19855         if(this.footer){
19856             var footer = {
19857                 tag : 'a',
19858                 cls : 'small-box-footer',
19859                 href : this.fhref || '#',
19860                 html : this.footer
19861             };
19862             
19863             cfg.cn.push(footer);
19864             
19865         }
19866         
19867         return  cfg;
19868     },
19869
19870     onRender : function(ct,position){
19871         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19872
19873
19874        
19875                 
19876     },
19877
19878     setHeadline: function (value)
19879     {
19880         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19881     },
19882     
19883     setFooter: function (value, href)
19884     {
19885         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19886         
19887         if(href){
19888             this.el.select('a.small-box-footer',true).first().attr('href', href);
19889         }
19890         
19891     },
19892
19893     setContent: function (value)
19894     {
19895         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19896     },
19897
19898     initEvents: function() 
19899     {   
19900         
19901     }
19902     
19903 });
19904
19905  
19906 /*
19907  * - LGPL
19908  *
19909  * TabBox
19910  * 
19911  */
19912 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19913
19914 /**
19915  * @class Roo.bootstrap.dash.TabBox
19916  * @extends Roo.bootstrap.Component
19917  * Bootstrap TabBox class
19918  * @cfg {String} title Title of the TabBox
19919  * @cfg {String} icon Icon of the TabBox
19920  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19921  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19922  * 
19923  * @constructor
19924  * Create a new TabBox
19925  * @param {Object} config The config object
19926  */
19927
19928
19929 Roo.bootstrap.dash.TabBox = function(config){
19930     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19931     this.addEvents({
19932         // raw events
19933         /**
19934          * @event addpane
19935          * When a pane is added
19936          * @param {Roo.bootstrap.dash.TabPane} pane
19937          */
19938         "addpane" : true,
19939         /**
19940          * @event activatepane
19941          * When a pane is activated
19942          * @param {Roo.bootstrap.dash.TabPane} pane
19943          */
19944         "activatepane" : true
19945         
19946          
19947     });
19948     
19949     this.panes = [];
19950 };
19951
19952 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19953
19954     title : '',
19955     icon : false,
19956     showtabs : true,
19957     tabScrollable : false,
19958     
19959     getChildContainer : function()
19960     {
19961         return this.el.select('.tab-content', true).first();
19962     },
19963     
19964     getAutoCreate : function(){
19965         
19966         var header = {
19967             tag: 'li',
19968             cls: 'pull-left header',
19969             html: this.title,
19970             cn : []
19971         };
19972         
19973         if(this.icon){
19974             header.cn.push({
19975                 tag: 'i',
19976                 cls: 'fa ' + this.icon
19977             });
19978         }
19979         
19980         var h = {
19981             tag: 'ul',
19982             cls: 'nav nav-tabs pull-right',
19983             cn: [
19984                 header
19985             ]
19986         };
19987         
19988         if(this.tabScrollable){
19989             h = {
19990                 tag: 'div',
19991                 cls: 'tab-header',
19992                 cn: [
19993                     {
19994                         tag: 'ul',
19995                         cls: 'nav nav-tabs pull-right',
19996                         cn: [
19997                             header
19998                         ]
19999                     }
20000                 ]
20001             }
20002         }
20003         
20004         var cfg = {
20005             tag: 'div',
20006             cls: 'nav-tabs-custom',
20007             cn: [
20008                 h,
20009                 {
20010                     tag: 'div',
20011                     cls: 'tab-content no-padding',
20012                     cn: []
20013                 }
20014             ]
20015         }
20016
20017         return  cfg;
20018     },
20019     initEvents : function()
20020     {
20021         //Roo.log('add add pane handler');
20022         this.on('addpane', this.onAddPane, this);
20023     },
20024      /**
20025      * Updates the box title
20026      * @param {String} html to set the title to.
20027      */
20028     setTitle : function(value)
20029     {
20030         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20031     },
20032     onAddPane : function(pane)
20033     {
20034         this.panes.push(pane);
20035         //Roo.log('addpane');
20036         //Roo.log(pane);
20037         // tabs are rendere left to right..
20038         if(!this.showtabs){
20039             return;
20040         }
20041         
20042         var ctr = this.el.select('.nav-tabs', true).first();
20043          
20044          
20045         var existing = ctr.select('.nav-tab',true);
20046         var qty = existing.getCount();;
20047         
20048         
20049         var tab = ctr.createChild({
20050             tag : 'li',
20051             cls : 'nav-tab' + (qty ? '' : ' active'),
20052             cn : [
20053                 {
20054                     tag : 'a',
20055                     href:'#',
20056                     html : pane.title
20057                 }
20058             ]
20059         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20060         pane.tab = tab;
20061         
20062         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20063         if (!qty) {
20064             pane.el.addClass('active');
20065         }
20066         
20067                 
20068     },
20069     onTabClick : function(ev,un,ob,pane)
20070     {
20071         //Roo.log('tab - prev default');
20072         ev.preventDefault();
20073         
20074         
20075         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20076         pane.tab.addClass('active');
20077         //Roo.log(pane.title);
20078         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20079         // technically we should have a deactivate event.. but maybe add later.
20080         // and it should not de-activate the selected tab...
20081         this.fireEvent('activatepane', pane);
20082         pane.el.addClass('active');
20083         pane.fireEvent('activate');
20084         
20085         
20086     },
20087     
20088     getActivePane : function()
20089     {
20090         var r = false;
20091         Roo.each(this.panes, function(p) {
20092             if(p.el.hasClass('active')){
20093                 r = p;
20094                 return false;
20095             }
20096             
20097             return;
20098         });
20099         
20100         return r;
20101     }
20102     
20103     
20104 });
20105
20106  
20107 /*
20108  * - LGPL
20109  *
20110  * Tab pane
20111  * 
20112  */
20113 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20114 /**
20115  * @class Roo.bootstrap.TabPane
20116  * @extends Roo.bootstrap.Component
20117  * Bootstrap TabPane class
20118  * @cfg {Boolean} active (false | true) Default false
20119  * @cfg {String} title title of panel
20120
20121  * 
20122  * @constructor
20123  * Create a new TabPane
20124  * @param {Object} config The config object
20125  */
20126
20127 Roo.bootstrap.dash.TabPane = function(config){
20128     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20129     
20130     this.addEvents({
20131         // raw events
20132         /**
20133          * @event activate
20134          * When a pane is activated
20135          * @param {Roo.bootstrap.dash.TabPane} pane
20136          */
20137         "activate" : true
20138          
20139     });
20140 };
20141
20142 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20143     
20144     active : false,
20145     title : '',
20146     
20147     // the tabBox that this is attached to.
20148     tab : false,
20149      
20150     getAutoCreate : function() 
20151     {
20152         var cfg = {
20153             tag: 'div',
20154             cls: 'tab-pane'
20155         }
20156         
20157         if(this.active){
20158             cfg.cls += ' active';
20159         }
20160         
20161         return cfg;
20162     },
20163     initEvents  : function()
20164     {
20165         //Roo.log('trigger add pane handler');
20166         this.parent().fireEvent('addpane', this)
20167     },
20168     
20169      /**
20170      * Updates the tab title 
20171      * @param {String} html to set the title to.
20172      */
20173     setTitle: function(str)
20174     {
20175         if (!this.tab) {
20176             return;
20177         }
20178         this.title = str;
20179         this.tab.select('a', true).first().dom.innerHTML = str;
20180         
20181     }
20182     
20183     
20184     
20185 });
20186
20187  
20188
20189
20190  /*
20191  * - LGPL
20192  *
20193  * menu
20194  * 
20195  */
20196 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20197
20198 /**
20199  * @class Roo.bootstrap.menu.Menu
20200  * @extends Roo.bootstrap.Component
20201  * Bootstrap Menu class - container for Menu
20202  * @cfg {String} html Text of the menu
20203  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20204  * @cfg {String} icon Font awesome icon
20205  * @cfg {String} pos Menu align to (top | bottom) default bottom
20206  * 
20207  * 
20208  * @constructor
20209  * Create a new Menu
20210  * @param {Object} config The config object
20211  */
20212
20213
20214 Roo.bootstrap.menu.Menu = function(config){
20215     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20216     
20217     this.addEvents({
20218         /**
20219          * @event beforeshow
20220          * Fires before this menu is displayed
20221          * @param {Roo.bootstrap.menu.Menu} this
20222          */
20223         beforeshow : true,
20224         /**
20225          * @event beforehide
20226          * Fires before this menu is hidden
20227          * @param {Roo.bootstrap.menu.Menu} this
20228          */
20229         beforehide : true,
20230         /**
20231          * @event show
20232          * Fires after this menu is displayed
20233          * @param {Roo.bootstrap.menu.Menu} this
20234          */
20235         show : true,
20236         /**
20237          * @event hide
20238          * Fires after this menu is hidden
20239          * @param {Roo.bootstrap.menu.Menu} this
20240          */
20241         hide : true,
20242         /**
20243          * @event click
20244          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20245          * @param {Roo.bootstrap.menu.Menu} this
20246          * @param {Roo.EventObject} e
20247          */
20248         click : true
20249     });
20250     
20251 };
20252
20253 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20254     
20255     submenu : false,
20256     html : '',
20257     weight : 'default',
20258     icon : false,
20259     pos : 'bottom',
20260     
20261     
20262     getChildContainer : function() {
20263         if(this.isSubMenu){
20264             return this.el;
20265         }
20266         
20267         return this.el.select('ul.dropdown-menu', true).first();  
20268     },
20269     
20270     getAutoCreate : function()
20271     {
20272         var text = [
20273             {
20274                 tag : 'span',
20275                 cls : 'roo-menu-text',
20276                 html : this.html
20277             }
20278         ];
20279         
20280         if(this.icon){
20281             text.unshift({
20282                 tag : 'i',
20283                 cls : 'fa ' + this.icon
20284             })
20285         }
20286         
20287         
20288         var cfg = {
20289             tag : 'div',
20290             cls : 'btn-group',
20291             cn : [
20292                 {
20293                     tag : 'button',
20294                     cls : 'dropdown-button btn btn-' + this.weight,
20295                     cn : text
20296                 },
20297                 {
20298                     tag : 'button',
20299                     cls : 'dropdown-toggle btn btn-' + this.weight,
20300                     cn : [
20301                         {
20302                             tag : 'span',
20303                             cls : 'caret'
20304                         }
20305                     ]
20306                 },
20307                 {
20308                     tag : 'ul',
20309                     cls : 'dropdown-menu'
20310                 }
20311             ]
20312             
20313         };
20314         
20315         if(this.pos == 'top'){
20316             cfg.cls += ' dropup';
20317         }
20318         
20319         if(this.isSubMenu){
20320             cfg = {
20321                 tag : 'ul',
20322                 cls : 'dropdown-menu'
20323             }
20324         }
20325         
20326         return cfg;
20327     },
20328     
20329     onRender : function(ct, position)
20330     {
20331         this.isSubMenu = ct.hasClass('dropdown-submenu');
20332         
20333         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20334     },
20335     
20336     initEvents : function() 
20337     {
20338         if(this.isSubMenu){
20339             return;
20340         }
20341         
20342         this.hidden = true;
20343         
20344         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20345         this.triggerEl.on('click', this.onTriggerPress, this);
20346         
20347         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20348         this.buttonEl.on('click', this.onClick, this);
20349         
20350     },
20351     
20352     list : function()
20353     {
20354         if(this.isSubMenu){
20355             return this.el;
20356         }
20357         
20358         return this.el.select('ul.dropdown-menu', true).first();
20359     },
20360     
20361     onClick : function(e)
20362     {
20363         this.fireEvent("click", this, e);
20364     },
20365     
20366     onTriggerPress  : function(e)
20367     {   
20368         if (this.isVisible()) {
20369             this.hide();
20370         } else {
20371             this.show();
20372         }
20373     },
20374     
20375     isVisible : function(){
20376         return !this.hidden;
20377     },
20378     
20379     show : function()
20380     {
20381         this.fireEvent("beforeshow", this);
20382         
20383         this.hidden = false;
20384         this.el.addClass('open');
20385         
20386         Roo.get(document).on("mouseup", this.onMouseUp, this);
20387         
20388         this.fireEvent("show", this);
20389         
20390         
20391     },
20392     
20393     hide : function()
20394     {
20395         this.fireEvent("beforehide", this);
20396         
20397         this.hidden = true;
20398         this.el.removeClass('open');
20399         
20400         Roo.get(document).un("mouseup", this.onMouseUp);
20401         
20402         this.fireEvent("hide", this);
20403     },
20404     
20405     onMouseUp : function()
20406     {
20407         this.hide();
20408     }
20409     
20410 });
20411
20412  
20413  /*
20414  * - LGPL
20415  *
20416  * menu item
20417  * 
20418  */
20419 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20420
20421 /**
20422  * @class Roo.bootstrap.menu.Item
20423  * @extends Roo.bootstrap.Component
20424  * Bootstrap MenuItem class
20425  * @cfg {Boolean} submenu (true | false) default false
20426  * @cfg {String} html text of the item
20427  * @cfg {String} href the link
20428  * @cfg {Boolean} disable (true | false) default false
20429  * @cfg {Boolean} preventDefault (true | false) default true
20430  * @cfg {String} icon Font awesome icon
20431  * @cfg {String} pos Submenu align to (left | right) default right 
20432  * 
20433  * 
20434  * @constructor
20435  * Create a new Item
20436  * @param {Object} config The config object
20437  */
20438
20439
20440 Roo.bootstrap.menu.Item = function(config){
20441     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20442     this.addEvents({
20443         /**
20444          * @event mouseover
20445          * Fires when the mouse is hovering over this menu
20446          * @param {Roo.bootstrap.menu.Item} this
20447          * @param {Roo.EventObject} e
20448          */
20449         mouseover : true,
20450         /**
20451          * @event mouseout
20452          * Fires when the mouse exits this menu
20453          * @param {Roo.bootstrap.menu.Item} this
20454          * @param {Roo.EventObject} e
20455          */
20456         mouseout : true,
20457         // raw events
20458         /**
20459          * @event click
20460          * The raw click event for the entire grid.
20461          * @param {Roo.EventObject} e
20462          */
20463         click : true
20464     });
20465 };
20466
20467 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20468     
20469     submenu : false,
20470     href : '',
20471     html : '',
20472     preventDefault: true,
20473     disable : false,
20474     icon : false,
20475     pos : 'right',
20476     
20477     getAutoCreate : function()
20478     {
20479         var text = [
20480             {
20481                 tag : 'span',
20482                 cls : 'roo-menu-item-text',
20483                 html : this.html
20484             }
20485         ];
20486         
20487         if(this.icon){
20488             text.unshift({
20489                 tag : 'i',
20490                 cls : 'fa ' + this.icon
20491             })
20492         }
20493         
20494         var cfg = {
20495             tag : 'li',
20496             cn : [
20497                 {
20498                     tag : 'a',
20499                     href : this.href || '#',
20500                     cn : text
20501                 }
20502             ]
20503         };
20504         
20505         if(this.disable){
20506             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20507         }
20508         
20509         if(this.submenu){
20510             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20511             
20512             if(this.pos == 'left'){
20513                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20514             }
20515         }
20516         
20517         return cfg;
20518     },
20519     
20520     initEvents : function() 
20521     {
20522         this.el.on('mouseover', this.onMouseOver, this);
20523         this.el.on('mouseout', this.onMouseOut, this);
20524         
20525         this.el.select('a', true).first().on('click', this.onClick, this);
20526         
20527     },
20528     
20529     onClick : function(e)
20530     {
20531         if(this.preventDefault){
20532             e.preventDefault();
20533         }
20534         
20535         this.fireEvent("click", this, e);
20536     },
20537     
20538     onMouseOver : function(e)
20539     {
20540         if(this.submenu && this.pos == 'left'){
20541             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20542         }
20543         
20544         this.fireEvent("mouseover", this, e);
20545     },
20546     
20547     onMouseOut : function(e)
20548     {
20549         this.fireEvent("mouseout", this, e);
20550     }
20551 });
20552
20553  
20554
20555  /*
20556  * - LGPL
20557  *
20558  * menu separator
20559  * 
20560  */
20561 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20562
20563 /**
20564  * @class Roo.bootstrap.menu.Separator
20565  * @extends Roo.bootstrap.Component
20566  * Bootstrap Separator class
20567  * 
20568  * @constructor
20569  * Create a new Separator
20570  * @param {Object} config The config object
20571  */
20572
20573
20574 Roo.bootstrap.menu.Separator = function(config){
20575     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20576 };
20577
20578 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20579     
20580     getAutoCreate : function(){
20581         var cfg = {
20582             tag : 'li',
20583             cls: 'divider'
20584         };
20585         
20586         return cfg;
20587     }
20588    
20589 });
20590
20591  
20592
20593