roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * 
21  * @constructor
22  * Do not use directly - it does not do anything..
23  * @param {Object} config The config object
24  */
25
26
27
28 Roo.bootstrap.Component = function(config){
29     Roo.bootstrap.Component.superclass.constructor.call(this, config);
30 };
31
32 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
33     
34     
35     allowDomMove : false, // to stop relocations in parent onRender...
36     
37     cls : false,
38     
39     style : false,
40     
41     autoCreate : false,
42     
43     tooltip : null,
44     /**
45      * Initialize Events for the element
46      */
47     initEvents : function() { },
48     
49     xattr : false,
50     
51     parentId : false,
52     
53     can_build_overlaid : true,
54     
55     dataId : false,
56     
57     name : false,
58     
59     parent: function() {
60         // returns the parent component..
61         return Roo.ComponentMgr.get(this.parentId)
62         
63         
64     },
65     
66     // private
67     onRender : function(ct, position)
68     {
69        // Roo.log("Call onRender: " + this.xtype);
70         
71         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
72         
73         if(this.el){
74             if (this.el.attr('xtype')) {
75                 this.el.attr('xtypex', this.el.attr('xtype'));
76                 this.el.dom.removeAttribute('xtype');
77                 
78                 this.initEvents();
79             }
80             
81             return;
82         }
83         
84          
85         
86         var cfg = Roo.apply({},  this.getAutoCreate());
87         cfg.id = Roo.id();
88         
89         // fill in the extra attributes 
90         if (this.xattr && typeof(this.xattr) =='object') {
91             for (var i in this.xattr) {
92                 cfg[i] = this.xattr[i];
93             }
94         }
95         
96         if(this.dataId){
97             cfg.dataId = this.dataId;
98         }
99         
100         if (this.cls) {
101             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
102         }
103         
104         if (this.style) { // fixme needs to support more complex style data.
105             cfg.style = this.style;
106         }
107         
108         if(this.name){
109             cfg.name = this.name;
110         }
111         
112        
113         
114         this.el = ct.createChild(cfg, position);
115         
116         if (this.tooltip) {
117             this.tooltipEl().attr('tooltip', this.tooltip);
118         }
119         
120         if(this.tabIndex !== undefined){
121             this.el.dom.setAttribute('tabIndex', this.tabIndex);
122         }
123         this.initEvents();
124         
125         
126     },
127     /**
128      * Fetch the element to add children to
129      * @return {Roo.Element} defaults to this.el
130      */
131     getChildContainer : function()
132     {
133         return this.el;
134     },
135     /**
136      * Fetch the element to display the tooltip on.
137      * @return {Roo.Element} defaults to this.el
138      */
139     tooltipEl : function()
140     {
141         return this.el;
142     },
143         
144     addxtype  : function(tree,cntr)
145     {
146         var cn = this;
147         
148         cn = Roo.factory(tree);
149            
150         cn.parentType = this.xtype; //??
151         cn.parentId = this.id;
152         
153         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
154         
155         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
156         
157         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
158         
159         var build_from_html =  Roo.XComponent.build_from_html;
160           
161         var is_body  = (tree.xtype == 'Body') ;
162           
163         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
164           
165         var self_cntr_el = Roo.get(this[cntr](false));
166         
167         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
168             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
169                 return this.addxtypeChild(tree,cntr);
170             }
171             
172             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
173                 
174             if(echild){
175                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
176             }
177             
178             Roo.log('skipping render');
179             return cn;
180             
181         }
182         
183         var ret = false;
184         
185         while (true) {
186             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
187             
188             if (!echild) {
189                 break;
190             }
191             
192             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
193                 break;
194             }
195             
196             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
197         }
198         return ret;
199     },
200     
201     addxtypeChild : function (tree, cntr)
202     {
203         Roo.debug && Roo.log('addxtypeChild:' + cntr);
204         var cn = this;
205         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
206         
207         
208         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
209                     (typeof(tree['flexy:foreach']) != 'undefined');
210           
211         
212         
213          skip_children = false;
214         // render the element if it's not BODY.
215         if (tree.xtype != 'Body') {
216            
217             cn = Roo.factory(tree);
218            
219             cn.parentType = this.xtype; //??
220             cn.parentId = this.id;
221             
222             var build_from_html =  Roo.XComponent.build_from_html;
223             
224             
225             // does the container contain child eleemnts with 'xtype' attributes.
226             // that match this xtype..
227             // note - when we render we create these as well..
228             // so we should check to see if body has xtype set.
229             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
230                
231                 var self_cntr_el = Roo.get(this[cntr](false));
232                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233                 
234                 
235                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
236                 // and are not displayed -this causes this to use up the wrong element when matching.
237                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
238                 
239                 
240                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
241                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
242                   
243                   
244                   
245                     cn.el = echild;
246                   //  Roo.log("GOT");
247                     //echild.dom.removeAttribute('xtype');
248                 } else {
249                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
250                     Roo.debug && Roo.log(self_cntr_el);
251                     Roo.debug && Roo.log(echild);
252                     Roo.debug && Roo.log(cn);
253                 }
254             }
255            
256             
257            
258             // if object has flexy:if - then it may or may not be rendered.
259             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
260                 // skip a flexy if element.
261                 Roo.debug && Roo.log('skipping render');
262                 Roo.debug && Roo.log(tree);
263                 if (!cn.el) {
264                     Roo.debug && Roo.log('skipping all children');
265                     skip_children = true;
266                 }
267                 
268              } else {
269                  
270                 // actually if flexy:foreach is found, we really want to create 
271                 // multiple copies here...
272                 //Roo.log('render');
273                 //Roo.log(this[cntr]());
274                 cn.render(this[cntr](true));
275              }
276             // then add the element..
277         }
278         
279         
280         // handle the kids..
281         
282         var nitems = [];
283         /*
284         if (typeof (tree.menu) != 'undefined') {
285             tree.menu.parentType = cn.xtype;
286             tree.menu.triggerEl = cn.el;
287             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
288             
289         }
290         */
291         if (!tree.items || !tree.items.length) {
292             cn.items = nitems;
293             return cn;
294         }
295         var items = tree.items;
296         delete tree.items;
297         
298         //Roo.log(items.length);
299             // add the items..
300         if (!skip_children) {    
301             for(var i =0;i < items.length;i++) {
302                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
303             }
304         }
305         
306         cn.items = nitems;
307         
308         return cn;
309     }
310     
311     
312     
313     
314 });
315
316  /*
317  * - LGPL
318  *
319  * Body
320  * 
321  */
322
323 /**
324  * @class Roo.bootstrap.Body
325  * @extends Roo.bootstrap.Component
326  * Bootstrap Body class
327  * 
328  * @constructor
329  * Create a new body
330  * @param {Object} config The config object
331  */
332
333 Roo.bootstrap.Body = function(config){
334     Roo.bootstrap.Body.superclass.constructor.call(this, config);
335     this.el = Roo.get(document.body);
336     if (this.cls && this.cls.length) {
337         Roo.get(document.body).addClass(this.cls);
338     }
339 };
340
341 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
342       
343         autoCreate : {
344         cls: 'container'
345     },
346     onRender : function(ct, position)
347     {
348        /* Roo.log("Roo.bootstrap.Body - onRender");
349         if (this.cls && this.cls.length) {
350             Roo.get(document.body).addClass(this.cls);
351         }
352         // style??? xttr???
353         */
354     }
355     
356     
357  
358    
359 });
360
361  /*
362  * - LGPL
363  *
364  * button group
365  * 
366  */
367
368
369 /**
370  * @class Roo.bootstrap.ButtonGroup
371  * @extends Roo.bootstrap.Component
372  * Bootstrap ButtonGroup class
373  * @cfg {String} size lg | sm | xs (default empty normal)
374  * @cfg {String} align vertical | justified  (default none)
375  * @cfg {String} direction up | down (default down)
376  * @cfg {Boolean} toolbar false | true
377  * @cfg {Boolean} btn true | false
378  * 
379  * 
380  * @constructor
381  * Create a new Input
382  * @param {Object} config The config object
383  */
384
385 Roo.bootstrap.ButtonGroup = function(config){
386     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
387 };
388
389 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
390     
391     size: '',
392     align: '',
393     direction: '',
394     toolbar: false,
395     btn: true,
396
397     getAutoCreate : function(){
398         var cfg = {
399             cls: 'btn-group',
400             html : null
401         }
402         
403         cfg.html = this.html || cfg.html;
404         
405         if (this.toolbar) {
406             cfg = {
407                 cls: 'btn-toolbar',
408                 html: null
409             }
410             
411             return cfg;
412         }
413         
414         if (['vertical','justified'].indexOf(this.align)!==-1) {
415             cfg.cls = 'btn-group-' + this.align;
416             
417             if (this.align == 'justified') {
418                 console.log(this.items);
419             }
420         }
421         
422         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
423             cfg.cls += ' btn-group-' + this.size;
424         }
425         
426         if (this.direction == 'up') {
427             cfg.cls += ' dropup' ;
428         }
429         
430         return cfg;
431     }
432    
433 });
434
435  /*
436  * - LGPL
437  *
438  * button
439  * 
440  */
441
442 /**
443  * @class Roo.bootstrap.Button
444  * @extends Roo.bootstrap.Component
445  * Bootstrap Button class
446  * @cfg {String} html The button content
447  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
448  * @cfg {String} size empty | lg | sm | xs
449  * @cfg {String} tag empty | a | input | submit
450  * @cfg {String} href empty or href
451  * @cfg {Boolean} disabled false | true
452  * @cfg {Boolean} isClose false | true
453  * @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
454  * @cfg {String} badge text for badge
455  * @cfg {String} theme default (or empty) | glow
456  * @cfg {Boolean} inverse false | true
457  * @cfg {Boolean} toggle false | true
458  * @cfg {String} ontext text for on toggle state
459  * @cfg {String} offtext text for off toggle state
460  * @cfg {Boolean} defaulton true | false
461  * @cfg {Boolean} preventDefault (true | false) default true
462  * @cfg {Boolean} removeClass true | false remove the standard class..
463  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
464  * 
465  * @constructor
466  * Create a new button
467  * @param {Object} config The config object
468  */
469
470
471 Roo.bootstrap.Button = function(config){
472     Roo.bootstrap.Button.superclass.constructor.call(this, config);
473     this.addEvents({
474         // raw events
475         /**
476          * @event click
477          * When a butotn is pressed
478          * @param {Roo.EventObject} e
479          */
480         "click" : true,
481          /**
482          * @event toggle
483          * After the button has been toggles
484          * @param {Roo.EventObject} e
485          * @param {boolean} pressed (also available as button.pressed)
486          */
487         "toggle" : true
488     });
489 };
490
491 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
492     html: false,
493     active: false,
494     weight: '',
495     size: '',
496     tag: 'button',
497     href: '',
498     disabled: false,
499     isClose: false,
500     glyphicon: '',
501     badge: '',
502     theme: 'default',
503     inverse: false,
504     
505     toggle: false,
506     ontext: 'ON',
507     offtext: 'OFF',
508     defaulton: true,
509     preventDefault: true,
510     removeClass: false,
511     name: false,
512     target: false,
513     
514     
515     pressed : null,
516      
517     
518     getAutoCreate : function(){
519         
520         var cfg = {
521             tag : 'button',
522             cls : 'roo-button',
523             html: ''
524         };
525         
526         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
527             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
528             this.tag = 'button';
529         } else {
530             cfg.tag = this.tag;
531         }
532         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
533         
534         if (this.toggle == true) {
535             cfg={
536                 tag: 'div',
537                 cls: 'slider-frame roo-button',
538                 cn: [
539                     {
540                         tag: 'span',
541                         'data-on-text':'ON',
542                         'data-off-text':'OFF',
543                         cls: 'slider-button',
544                         html: this.offtext
545                     }
546                 ]
547             };
548             
549             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
550                 cfg.cls += ' '+this.weight;
551             }
552             
553             return cfg;
554         }
555         
556         if (this.isClose) {
557             cfg.cls += ' close';
558             
559             cfg["aria-hidden"] = true;
560             
561             cfg.html = "&times;";
562             
563             return cfg;
564         }
565         
566          
567         if (this.theme==='default') {
568             cfg.cls = 'btn roo-button';
569             
570             //if (this.parentType != 'Navbar') {
571             this.weight = this.weight.length ?  this.weight : 'default';
572             //}
573             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
574                 
575                 cfg.cls += ' btn-' + this.weight;
576             }
577         } else if (this.theme==='glow') {
578             
579             cfg.tag = 'a';
580             cfg.cls = 'btn-glow roo-button';
581             
582             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
583                 
584                 cfg.cls += ' ' + this.weight;
585             }
586         }
587    
588         
589         if (this.inverse) {
590             this.cls += ' inverse';
591         }
592         
593         
594         if (this.active) {
595             cfg.cls += ' active';
596         }
597         
598         if (this.disabled) {
599             cfg.disabled = 'disabled';
600         }
601         
602         if (this.items) {
603             Roo.log('changing to ul' );
604             cfg.tag = 'ul';
605             this.glyphicon = 'caret';
606         }
607         
608         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
609          
610         //gsRoo.log(this.parentType);
611         if (this.parentType === 'Navbar' && !this.parent().bar) {
612             Roo.log('changing to li?');
613             
614             cfg.tag = 'li';
615             
616             cfg.cls = '';
617             cfg.cn =  [{
618                 tag : 'a',
619                 cls : 'roo-button',
620                 html : this.html,
621                 href : this.href || '#'
622             }];
623             if (this.menu) {
624                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
625                 cfg.cls += ' dropdown';
626             }   
627             
628             delete cfg.html;
629             
630         }
631         
632        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
633         
634         if (this.glyphicon) {
635             cfg.html = ' ' + cfg.html;
636             
637             cfg.cn = [
638                 {
639                     tag: 'span',
640                     cls: 'glyphicon glyphicon-' + this.glyphicon
641                 }
642             ];
643         }
644         
645         if (this.badge) {
646             cfg.html += ' ';
647             
648             cfg.tag = 'a';
649             
650 //            cfg.cls='btn roo-button';
651             
652             cfg.href=this.href;
653             
654             var value = cfg.html;
655             
656             if(this.glyphicon){
657                 value = {
658                             tag: 'span',
659                             cls: 'glyphicon glyphicon-' + this.glyphicon,
660                             html: this.html
661                         };
662                 
663             }
664             
665             cfg.cn = [
666                 value,
667                 {
668                     tag: 'span',
669                     cls: 'badge',
670                     html: this.badge
671                 }
672             ];
673             
674             cfg.html='';
675         }
676         
677         if (this.menu) {
678             cfg.cls += ' dropdown';
679             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
680         }
681         
682         if (cfg.tag !== 'a' && this.href !== '') {
683             throw "Tag must be a to set href.";
684         } else if (this.href.length > 0) {
685             cfg.href = this.href;
686         }
687         
688         if(this.removeClass){
689             cfg.cls = '';
690         }
691         
692         if(this.target){
693             cfg.target = this.target;
694         }
695         
696         return cfg;
697     },
698     initEvents: function() {
699        // Roo.log('init events?');
700 //        Roo.log(this.el.dom);
701         // add the menu...
702         
703         if (typeof (this.menu) != 'undefined') {
704             this.menu.parentType = this.xtype;
705             this.menu.triggerEl = this.el;
706             this.addxtype(Roo.apply({}, this.menu));
707         }
708
709
710        if (this.el.hasClass('roo-button')) {
711             this.el.on('click', this.onClick, this);
712        } else {
713             this.el.select('.roo-button').on('click', this.onClick, this);
714        }
715        
716        if(this.removeClass){
717            this.el.on('click', this.onClick, this);
718        }
719        
720        this.el.enableDisplayMode();
721         
722     },
723     onClick : function(e)
724     {
725         if (this.disabled) {
726             return;
727         }
728         
729         Roo.log('button on click ');
730         if(this.preventDefault){
731             e.preventDefault();
732         }
733         if (this.pressed === true || this.pressed === false) {
734             this.pressed = !this.pressed;
735             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
736             this.fireEvent('toggle', this, e, this.pressed);
737         }
738         
739         
740         this.fireEvent('click', this, e);
741     },
742     
743     /**
744      * Enables this button
745      */
746     enable : function()
747     {
748         this.disabled = false;
749         this.el.removeClass('disabled');
750     },
751     
752     /**
753      * Disable this button
754      */
755     disable : function()
756     {
757         this.disabled = true;
758         this.el.addClass('disabled');
759     },
760      /**
761      * sets the active state on/off, 
762      * @param {Boolean} state (optional) Force a particular state
763      */
764     setActive : function(v) {
765         
766         this.el[v ? 'addClass' : 'removeClass']('active');
767     },
768      /**
769      * toggles the current active state 
770      */
771     toggleActive : function()
772     {
773        var active = this.el.hasClass('active');
774        this.setActive(!active);
775        
776         
777     },
778     setText : function(str)
779     {
780         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
781     },
782     getText : function()
783     {
784         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
785     },
786     hide: function() {
787        
788      
789         this.el.hide();   
790     },
791     show: function() {
792        
793         this.el.show();   
794     }
795     
796     
797 });
798
799  /*
800  * - LGPL
801  *
802  * column
803  * 
804  */
805
806 /**
807  * @class Roo.bootstrap.Column
808  * @extends Roo.bootstrap.Component
809  * Bootstrap Column class
810  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
811  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
812  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
813  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
814  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
815  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
816  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
817  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
818  *
819  * 
820  * @cfg {Boolean} hidden (true|false) hide the element
821  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
822  * @cfg {String} fa (ban|check|...) font awesome icon
823  * @cfg {Number} fasize (1|2|....) font awsome size
824
825  * @cfg {String} icon (info-sign|check|...) glyphicon name
826
827  * @cfg {String} html content of column.
828  * 
829  * @constructor
830  * Create a new Column
831  * @param {Object} config The config object
832  */
833
834 Roo.bootstrap.Column = function(config){
835     Roo.bootstrap.Column.superclass.constructor.call(this, config);
836 };
837
838 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
839     
840     xs: false,
841     sm: false,
842     md: false,
843     lg: false,
844     xsoff: false,
845     smoff: false,
846     mdoff: false,
847     lgoff: false,
848     html: '',
849     offset: 0,
850     alert: false,
851     fa: false,
852     icon : false,
853     hidden : false,
854     fasize : 1,
855     
856     getAutoCreate : function(){
857         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
858         
859         cfg = {
860             tag: 'div',
861             cls: 'column'
862         };
863         
864         var settings=this;
865         ['xs','sm','md','lg'].map(function(size){
866             //Roo.log( size + ':' + settings[size]);
867             
868             if (settings[size+'off'] !== false) {
869                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
870             }
871             
872             if (settings[size] === false) {
873                 return;
874             }
875             Roo.log(settings[size]);
876             if (!settings[size]) { // 0 = hidden
877                 cfg.cls += ' hidden-' + size;
878                 return;
879             }
880             cfg.cls += ' col-' + size + '-' + settings[size];
881             
882         });
883         
884         if (this.hidden) {
885             cfg.cls += ' hidden';
886         }
887         
888         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
889             cfg.cls +=' alert alert-' + this.alert;
890         }
891         
892         
893         if (this.html.length) {
894             cfg.html = this.html;
895         }
896         if (this.fa) {
897             var fasize = '';
898             if (this.fasize > 1) {
899                 fasize = ' fa-' + this.fasize + 'x';
900             }
901             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
902             
903             
904         }
905         if (this.icon) {
906             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
907         }
908         
909         return cfg;
910     }
911    
912 });
913
914  
915
916  /*
917  * - LGPL
918  *
919  * page container.
920  * 
921  */
922
923
924 /**
925  * @class Roo.bootstrap.Container
926  * @extends Roo.bootstrap.Component
927  * Bootstrap Container class
928  * @cfg {Boolean} jumbotron is it a jumbotron element
929  * @cfg {String} html content of element
930  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
931  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
932  * @cfg {String} header content of header (for panel)
933  * @cfg {String} footer content of footer (for panel)
934  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
935  * @cfg {String} tag (header|aside|section) type of HTML tag.
936  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
937  * @cfg {String} fa (ban|check|...) font awesome icon
938  * @cfg {String} icon (info-sign|check|...) glyphicon name
939  * @cfg {Boolean} hidden (true|false) hide the element
940
941  *     
942  * @constructor
943  * Create a new Container
944  * @param {Object} config The config object
945  */
946
947 Roo.bootstrap.Container = function(config){
948     Roo.bootstrap.Container.superclass.constructor.call(this, config);
949 };
950
951 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
952     
953     jumbotron : false,
954     well: '',
955     panel : '',
956     header: '',
957     footer : '',
958     sticky: '',
959     tag : false,
960     alert : false,
961     fa: false,
962     icon : false,
963   
964      
965     getChildContainer : function() {
966         
967         if(!this.el){
968             return false;
969         }
970         
971         if (this.panel.length) {
972             return this.el.select('.panel-body',true).first();
973         }
974         
975         return this.el;
976     },
977     
978     
979     getAutoCreate : function(){
980         
981         var cfg = {
982             tag : this.tag || 'div',
983             html : '',
984             cls : ''
985         };
986         if (this.jumbotron) {
987             cfg.cls = 'jumbotron';
988         }
989         
990         
991         
992         // - this is applied by the parent..
993         //if (this.cls) {
994         //    cfg.cls = this.cls + '';
995         //}
996         
997         if (this.sticky.length) {
998             
999             var bd = Roo.get(document.body);
1000             if (!bd.hasClass('bootstrap-sticky')) {
1001                 bd.addClass('bootstrap-sticky');
1002                 Roo.select('html',true).setStyle('height', '100%');
1003             }
1004              
1005             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1006         }
1007         
1008         
1009         if (this.well.length) {
1010             switch (this.well) {
1011                 case 'lg':
1012                 case 'sm':
1013                     cfg.cls +=' well well-' +this.well;
1014                     break;
1015                 default:
1016                     cfg.cls +=' well';
1017                     break;
1018             }
1019         }
1020         
1021         if (this.hidden) {
1022             cfg.cls += ' hidden';
1023         }
1024         
1025         
1026         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1027             cfg.cls +=' alert alert-' + this.alert;
1028         }
1029         
1030         var body = cfg;
1031         
1032         if (this.panel.length) {
1033             cfg.cls += ' panel panel-' + this.panel;
1034             cfg.cn = [];
1035             if (this.header.length) {
1036                 cfg.cn.push({
1037                     
1038                     cls : 'panel-heading',
1039                     cn : [{
1040                         tag: 'h3',
1041                         cls : 'panel-title',
1042                         html : this.header
1043                     }]
1044                     
1045                 });
1046             }
1047             body = false;
1048             cfg.cn.push({
1049                 cls : 'panel-body',
1050                 html : this.html
1051             });
1052             
1053             
1054             if (this.footer.length) {
1055                 cfg.cn.push({
1056                     cls : 'panel-footer',
1057                     html : this.footer
1058                     
1059                 });
1060             }
1061             
1062         }
1063         
1064         if (body) {
1065             body.html = this.html || cfg.html;
1066             // prefix with the icons..
1067             if (this.fa) {
1068                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1069             }
1070             if (this.icon) {
1071                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1072             }
1073             
1074             
1075         }
1076         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1077             cfg.cls =  'container';
1078         }
1079         
1080         return cfg;
1081     },
1082     
1083     titleEl : function()
1084     {
1085         if(!this.el || !this.panel.length || !this.header.length){
1086             return;
1087         }
1088         
1089         return this.el.select('.panel-title',true).first();
1090     },
1091     
1092     setTitle : function(v)
1093     {
1094         var titleEl = this.titleEl();
1095         
1096         if(!titleEl){
1097             return;
1098         }
1099         
1100         titleEl.dom.innerHTML = v;
1101     },
1102     
1103     getTitle : function()
1104     {
1105         
1106         var titleEl = this.titleEl();
1107         
1108         if(!titleEl){
1109             return '';
1110         }
1111         
1112         return titleEl.dom.innerHTML;
1113     }
1114    
1115 });
1116
1117  /*
1118  * - LGPL
1119  *
1120  * image
1121  * 
1122  */
1123
1124
1125 /**
1126  * @class Roo.bootstrap.Img
1127  * @extends Roo.bootstrap.Component
1128  * Bootstrap Img class
1129  * @cfg {Boolean} imgResponsive false | true
1130  * @cfg {String} border rounded | circle | thumbnail
1131  * @cfg {String} src image source
1132  * @cfg {String} alt image alternative text
1133  * @cfg {String} href a tag href
1134  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1135  * 
1136  * @constructor
1137  * Create a new Input
1138  * @param {Object} config The config object
1139  */
1140
1141 Roo.bootstrap.Img = function(config){
1142     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1143     
1144     this.addEvents({
1145         // img events
1146         /**
1147          * @event click
1148          * The img click event for the img.
1149          * @param {Roo.EventObject} e
1150          */
1151         "click" : true
1152     });
1153 };
1154
1155 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1156     
1157     imgResponsive: true,
1158     border: '',
1159     src: '',
1160     href: false,
1161     target: false,
1162
1163     getAutoCreate : function(){
1164         
1165         var cfg = {
1166             tag: 'img',
1167             cls: (this.imgResponsive) ? 'img-responsive' : '',
1168             html : null
1169         }
1170         
1171         cfg.html = this.html || cfg.html;
1172         
1173         cfg.src = this.src || cfg.src;
1174         
1175         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1176             cfg.cls += ' img-' + this.border;
1177         }
1178         
1179         if(this.alt){
1180             cfg.alt = this.alt;
1181         }
1182         
1183         if(this.href){
1184             var a = {
1185                 tag: 'a',
1186                 href: this.href,
1187                 cn: [
1188                     cfg
1189                 ]
1190             }
1191             
1192             if(this.target){
1193                 a.target = this.target;
1194             }
1195             
1196         }
1197         
1198         
1199         return (this.href) ? a : cfg;
1200     },
1201     
1202     initEvents: function() {
1203         
1204         if(!this.href){
1205             this.el.on('click', this.onClick, this);
1206         }
1207     },
1208     
1209     onClick : function(e)
1210     {
1211         Roo.log('img onclick');
1212         this.fireEvent('click', this, e);
1213     }
1214    
1215 });
1216
1217  /*
1218  * - LGPL
1219  *
1220  * image
1221  * 
1222  */
1223
1224
1225 /**
1226  * @class Roo.bootstrap.Link
1227  * @extends Roo.bootstrap.Component
1228  * Bootstrap Link Class
1229  * @cfg {String} alt image alternative text
1230  * @cfg {String} href a tag href
1231  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1232  * @cfg {String} html the content of the link.
1233  * @cfg {String} anchor name for the anchor link
1234
1235  * @cfg {Boolean} preventDefault (true | false) default false
1236
1237  * 
1238  * @constructor
1239  * Create a new Input
1240  * @param {Object} config The config object
1241  */
1242
1243 Roo.bootstrap.Link = function(config){
1244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1245     
1246     this.addEvents({
1247         // img events
1248         /**
1249          * @event click
1250          * The img click event for the img.
1251          * @param {Roo.EventObject} e
1252          */
1253         "click" : true
1254     });
1255 };
1256
1257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1258     
1259     href: false,
1260     target: false,
1261     preventDefault: false,
1262     anchor : false,
1263     alt : false,
1264
1265     getAutoCreate : function()
1266     {
1267         
1268         var cfg = {
1269             tag: 'a'
1270         };
1271         // anchor's do not require html/href...
1272         if (this.anchor === false) {
1273             cfg.html = this.html || 'html-missing';
1274             cfg.href = this.href || '#';
1275         } else {
1276             cfg.name = this.anchor;
1277             if (this.html !== false) {
1278                 cfg.html = this.html;
1279             }
1280             if (this.href !== false) {
1281                 cfg.href = this.href;
1282             }
1283         }
1284         
1285         if(this.alt !== false){
1286             cfg.alt = this.alt;
1287         }
1288         
1289         
1290         if(this.target !== false) {
1291             cfg.target = this.target;
1292         }
1293         
1294         return cfg;
1295     },
1296     
1297     initEvents: function() {
1298         
1299         if(!this.href || this.preventDefault){
1300             this.el.on('click', this.onClick, this);
1301         }
1302     },
1303     
1304     onClick : function(e)
1305     {
1306         if(this.preventDefault){
1307             e.preventDefault();
1308         }
1309         //Roo.log('img onclick');
1310         this.fireEvent('click', this, e);
1311     }
1312    
1313 });
1314
1315  /*
1316  * - LGPL
1317  *
1318  * header
1319  * 
1320  */
1321
1322 /**
1323  * @class Roo.bootstrap.Header
1324  * @extends Roo.bootstrap.Component
1325  * Bootstrap Header class
1326  * @cfg {String} html content of header
1327  * @cfg {Number} level (1|2|3|4|5|6) default 1
1328  * 
1329  * @constructor
1330  * Create a new Header
1331  * @param {Object} config The config object
1332  */
1333
1334
1335 Roo.bootstrap.Header  = function(config){
1336     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1337 };
1338
1339 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1340     
1341     //href : false,
1342     html : false,
1343     level : 1,
1344     
1345     
1346     
1347     getAutoCreate : function(){
1348         
1349         var cfg = {
1350             tag: 'h' + (1 *this.level),
1351             html: this.html || 'fill in html'
1352         } ;
1353         
1354         return cfg;
1355     }
1356    
1357 });
1358
1359  
1360
1361  /*
1362  * Based on:
1363  * Ext JS Library 1.1.1
1364  * Copyright(c) 2006-2007, Ext JS, LLC.
1365  *
1366  * Originally Released Under LGPL - original licence link has changed is not relivant.
1367  *
1368  * Fork - LGPL
1369  * <script type="text/javascript">
1370  */
1371  
1372 /**
1373  * @class Roo.bootstrap.MenuMgr
1374  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1375  * @singleton
1376  */
1377 Roo.bootstrap.MenuMgr = function(){
1378    var menus, active, groups = {}, attached = false, lastShow = new Date();
1379
1380    // private - called when first menu is created
1381    function init(){
1382        menus = {};
1383        active = new Roo.util.MixedCollection();
1384        Roo.get(document).addKeyListener(27, function(){
1385            if(active.length > 0){
1386                hideAll();
1387            }
1388        });
1389    }
1390
1391    // private
1392    function hideAll(){
1393        if(active && active.length > 0){
1394            var c = active.clone();
1395            c.each(function(m){
1396                m.hide();
1397            });
1398        }
1399    }
1400
1401    // private
1402    function onHide(m){
1403        active.remove(m);
1404        if(active.length < 1){
1405            Roo.get(document).un("mouseup", onMouseDown);
1406             
1407            attached = false;
1408        }
1409    }
1410
1411    // private
1412    function onShow(m){
1413        var last = active.last();
1414        lastShow = new Date();
1415        active.add(m);
1416        if(!attached){
1417           Roo.get(document).on("mouseup", onMouseDown);
1418            
1419            attached = true;
1420        }
1421        if(m.parentMenu){
1422           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1423           m.parentMenu.activeChild = m;
1424        }else if(last && last.isVisible()){
1425           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1426        }
1427    }
1428
1429    // private
1430    function onBeforeHide(m){
1431        if(m.activeChild){
1432            m.activeChild.hide();
1433        }
1434        if(m.autoHideTimer){
1435            clearTimeout(m.autoHideTimer);
1436            delete m.autoHideTimer;
1437        }
1438    }
1439
1440    // private
1441    function onBeforeShow(m){
1442        var pm = m.parentMenu;
1443        if(!pm && !m.allowOtherMenus){
1444            hideAll();
1445        }else if(pm && pm.activeChild && active != m){
1446            pm.activeChild.hide();
1447        }
1448    }
1449
1450    // private
1451    function onMouseDown(e){
1452         Roo.log("on MouseDown");
1453         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1454            hideAll();
1455         }
1456         
1457         
1458    }
1459
1460    // private
1461    function onBeforeCheck(mi, state){
1462        if(state){
1463            var g = groups[mi.group];
1464            for(var i = 0, l = g.length; i < l; i++){
1465                if(g[i] != mi){
1466                    g[i].setChecked(false);
1467                }
1468            }
1469        }
1470    }
1471
1472    return {
1473
1474        /**
1475         * Hides all menus that are currently visible
1476         */
1477        hideAll : function(){
1478             hideAll();  
1479        },
1480
1481        // private
1482        register : function(menu){
1483            if(!menus){
1484                init();
1485            }
1486            menus[menu.id] = menu;
1487            menu.on("beforehide", onBeforeHide);
1488            menu.on("hide", onHide);
1489            menu.on("beforeshow", onBeforeShow);
1490            menu.on("show", onShow);
1491            var g = menu.group;
1492            if(g && menu.events["checkchange"]){
1493                if(!groups[g]){
1494                    groups[g] = [];
1495                }
1496                groups[g].push(menu);
1497                menu.on("checkchange", onCheck);
1498            }
1499        },
1500
1501         /**
1502          * Returns a {@link Roo.menu.Menu} object
1503          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1504          * be used to generate and return a new Menu instance.
1505          */
1506        get : function(menu){
1507            if(typeof menu == "string"){ // menu id
1508                return menus[menu];
1509            }else if(menu.events){  // menu instance
1510                return menu;
1511            }
1512            /*else if(typeof menu.length == 'number'){ // array of menu items?
1513                return new Roo.bootstrap.Menu({items:menu});
1514            }else{ // otherwise, must be a config
1515                return new Roo.bootstrap.Menu(menu);
1516            }
1517            */
1518            return false;
1519        },
1520
1521        // private
1522        unregister : function(menu){
1523            delete menus[menu.id];
1524            menu.un("beforehide", onBeforeHide);
1525            menu.un("hide", onHide);
1526            menu.un("beforeshow", onBeforeShow);
1527            menu.un("show", onShow);
1528            var g = menu.group;
1529            if(g && menu.events["checkchange"]){
1530                groups[g].remove(menu);
1531                menu.un("checkchange", onCheck);
1532            }
1533        },
1534
1535        // private
1536        registerCheckable : function(menuItem){
1537            var g = menuItem.group;
1538            if(g){
1539                if(!groups[g]){
1540                    groups[g] = [];
1541                }
1542                groups[g].push(menuItem);
1543                menuItem.on("beforecheckchange", onBeforeCheck);
1544            }
1545        },
1546
1547        // private
1548        unregisterCheckable : function(menuItem){
1549            var g = menuItem.group;
1550            if(g){
1551                groups[g].remove(menuItem);
1552                menuItem.un("beforecheckchange", onBeforeCheck);
1553            }
1554        }
1555    };
1556 }();/*
1557  * - LGPL
1558  *
1559  * menu
1560  * 
1561  */
1562
1563 /**
1564  * @class Roo.bootstrap.Menu
1565  * @extends Roo.bootstrap.Component
1566  * Bootstrap Menu class - container for MenuItems
1567  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1568  * 
1569  * @constructor
1570  * Create a new Menu
1571  * @param {Object} config The config object
1572  */
1573
1574
1575 Roo.bootstrap.Menu = function(config){
1576     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1577     if (this.registerMenu) {
1578         Roo.bootstrap.MenuMgr.register(this);
1579     }
1580     this.addEvents({
1581         /**
1582          * @event beforeshow
1583          * Fires before this menu is displayed
1584          * @param {Roo.menu.Menu} this
1585          */
1586         beforeshow : true,
1587         /**
1588          * @event beforehide
1589          * Fires before this menu is hidden
1590          * @param {Roo.menu.Menu} this
1591          */
1592         beforehide : true,
1593         /**
1594          * @event show
1595          * Fires after this menu is displayed
1596          * @param {Roo.menu.Menu} this
1597          */
1598         show : true,
1599         /**
1600          * @event hide
1601          * Fires after this menu is hidden
1602          * @param {Roo.menu.Menu} this
1603          */
1604         hide : true,
1605         /**
1606          * @event click
1607          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1608          * @param {Roo.menu.Menu} this
1609          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1610          * @param {Roo.EventObject} e
1611          */
1612         click : true,
1613         /**
1614          * @event mouseover
1615          * Fires when the mouse is hovering over this menu
1616          * @param {Roo.menu.Menu} this
1617          * @param {Roo.EventObject} e
1618          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1619          */
1620         mouseover : true,
1621         /**
1622          * @event mouseout
1623          * Fires when the mouse exits this menu
1624          * @param {Roo.menu.Menu} this
1625          * @param {Roo.EventObject} e
1626          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1627          */
1628         mouseout : true,
1629         /**
1630          * @event itemclick
1631          * Fires when a menu item contained in this menu is clicked
1632          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1633          * @param {Roo.EventObject} e
1634          */
1635         itemclick: true
1636     });
1637     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1638 };
1639
1640 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1641     
1642    /// html : false,
1643     //align : '',
1644     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1645     type: false,
1646     /**
1647      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1648      */
1649     registerMenu : true,
1650     
1651     menuItems :false, // stores the menu items..
1652     
1653     hidden:true,
1654     
1655     parentMenu : false,
1656     
1657     getChildContainer : function() {
1658         return this.el;  
1659     },
1660     
1661     getAutoCreate : function(){
1662          
1663         //if (['right'].indexOf(this.align)!==-1) {
1664         //    cfg.cn[1].cls += ' pull-right'
1665         //}
1666         
1667         
1668         var cfg = {
1669             tag : 'ul',
1670             cls : 'dropdown-menu' ,
1671             style : 'z-index:1000'
1672             
1673         }
1674         
1675         if (this.type === 'submenu') {
1676             cfg.cls = 'submenu active';
1677         }
1678         if (this.type === 'treeview') {
1679             cfg.cls = 'treeview-menu';
1680         }
1681         
1682         return cfg;
1683     },
1684     initEvents : function() {
1685         
1686        // Roo.log("ADD event");
1687        // Roo.log(this.triggerEl.dom);
1688         this.triggerEl.on('click', this.onTriggerPress, this);
1689         this.triggerEl.addClass('dropdown-toggle');
1690         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1691
1692         this.el.on("mouseover", this.onMouseOver, this);
1693         this.el.on("mouseout", this.onMouseOut, this);
1694         
1695         
1696     },
1697     findTargetItem : function(e){
1698         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1699         if(!t){
1700             return false;
1701         }
1702         //Roo.log(t);         Roo.log(t.id);
1703         if(t && t.id){
1704             //Roo.log(this.menuitems);
1705             return this.menuitems.get(t.id);
1706             
1707             //return this.items.get(t.menuItemId);
1708         }
1709         
1710         return false;
1711     },
1712     onClick : function(e){
1713         Roo.log("menu.onClick");
1714         var t = this.findTargetItem(e);
1715         if(!t || t.isContainer){
1716             return;
1717         }
1718         Roo.log(e);
1719         /*
1720         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1721             if(t == this.activeItem && t.shouldDeactivate(e)){
1722                 this.activeItem.deactivate();
1723                 delete this.activeItem;
1724                 return;
1725             }
1726             if(t.canActivate){
1727                 this.setActiveItem(t, true);
1728             }
1729             return;
1730             
1731             
1732         }
1733         */
1734        
1735         Roo.log('pass click event');
1736         
1737         t.onClick(e);
1738         
1739         this.fireEvent("click", this, t, e);
1740         
1741         this.hide();
1742     },
1743      onMouseOver : function(e){
1744         var t  = this.findTargetItem(e);
1745         //Roo.log(t);
1746         //if(t){
1747         //    if(t.canActivate && !t.disabled){
1748         //        this.setActiveItem(t, true);
1749         //    }
1750         //}
1751         
1752         this.fireEvent("mouseover", this, e, t);
1753     },
1754     isVisible : function(){
1755         return !this.hidden;
1756     },
1757      onMouseOut : function(e){
1758         var t  = this.findTargetItem(e);
1759         
1760         //if(t ){
1761         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1762         //        this.activeItem.deactivate();
1763         //        delete this.activeItem;
1764         //    }
1765         //}
1766         this.fireEvent("mouseout", this, e, t);
1767     },
1768     
1769     
1770     /**
1771      * Displays this menu relative to another element
1772      * @param {String/HTMLElement/Roo.Element} element The element to align to
1773      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1774      * the element (defaults to this.defaultAlign)
1775      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1776      */
1777     show : function(el, pos, parentMenu){
1778         this.parentMenu = parentMenu;
1779         if(!this.el){
1780             this.render();
1781         }
1782         this.fireEvent("beforeshow", this);
1783         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1784     },
1785      /**
1786      * Displays this menu at a specific xy position
1787      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1788      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1789      */
1790     showAt : function(xy, parentMenu, /* private: */_e){
1791         this.parentMenu = parentMenu;
1792         if(!this.el){
1793             this.render();
1794         }
1795         if(_e !== false){
1796             this.fireEvent("beforeshow", this);
1797             
1798             //xy = this.el.adjustForConstraints(xy);
1799         }
1800         //this.el.setXY(xy);
1801         //this.el.show();
1802         this.hideMenuItems();
1803         this.hidden = false;
1804         this.triggerEl.addClass('open');
1805         this.focus();
1806         this.fireEvent("show", this);
1807     },
1808     
1809     focus : function(){
1810         return;
1811         if(!this.hidden){
1812             this.doFocus.defer(50, this);
1813         }
1814     },
1815
1816     doFocus : function(){
1817         if(!this.hidden){
1818             this.focusEl.focus();
1819         }
1820     },
1821
1822     /**
1823      * Hides this menu and optionally all parent menus
1824      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1825      */
1826     hide : function(deep){
1827         
1828         this.hideMenuItems();
1829         if(this.el && this.isVisible()){
1830             this.fireEvent("beforehide", this);
1831             if(this.activeItem){
1832                 this.activeItem.deactivate();
1833                 this.activeItem = null;
1834             }
1835             this.triggerEl.removeClass('open');;
1836             this.hidden = true;
1837             this.fireEvent("hide", this);
1838         }
1839         if(deep === true && this.parentMenu){
1840             this.parentMenu.hide(true);
1841         }
1842     },
1843     
1844     onTriggerPress  : function(e)
1845     {
1846         
1847         Roo.log('trigger press');
1848         //Roo.log(e.getTarget());
1849        // Roo.log(this.triggerEl.dom);
1850         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1851             return;
1852         }
1853         if (this.isVisible()) {
1854             Roo.log('hide');
1855             this.hide();
1856         } else {
1857             this.show(this.triggerEl, false, false);
1858         }
1859         
1860         
1861     },
1862     
1863          
1864        
1865     
1866     hideMenuItems : function()
1867     {
1868         //$(backdrop).remove()
1869         Roo.select('.open',true).each(function(aa) {
1870             
1871             aa.removeClass('open');
1872           //var parent = getParent($(this))
1873           //var relatedTarget = { relatedTarget: this }
1874           
1875            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1876           //if (e.isDefaultPrevented()) return
1877            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1878         })
1879     },
1880     addxtypeChild : function (tree, cntr) {
1881         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1882           
1883         this.menuitems.add(comp);
1884         return comp;
1885
1886     },
1887     getEl : function()
1888     {
1889         Roo.log(this.el);
1890         return this.el;
1891     }
1892 });
1893
1894  
1895  /*
1896  * - LGPL
1897  *
1898  * menu item
1899  * 
1900  */
1901
1902
1903 /**
1904  * @class Roo.bootstrap.MenuItem
1905  * @extends Roo.bootstrap.Component
1906  * Bootstrap MenuItem class
1907  * @cfg {String} html the menu label
1908  * @cfg {String} href the link
1909  * @cfg {Boolean} preventDefault (true | false) default true
1910  * @cfg {Boolean} isContainer (true | false) default false
1911  * 
1912  * 
1913  * @constructor
1914  * Create a new MenuItem
1915  * @param {Object} config The config object
1916  */
1917
1918
1919 Roo.bootstrap.MenuItem = function(config){
1920     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1921     this.addEvents({
1922         // raw events
1923         /**
1924          * @event click
1925          * The raw click event for the entire grid.
1926          * @param {Roo.EventObject} e
1927          */
1928         "click" : true
1929     });
1930 };
1931
1932 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1933     
1934     href : false,
1935     html : false,
1936     preventDefault: true,
1937     isContainer : false,
1938     
1939     getAutoCreate : function(){
1940         
1941         if(this.isContainer){
1942             return {
1943                 tag: 'li',
1944                 cls: 'dropdown-menu-item'
1945             };
1946         }
1947         
1948         var cfg= {
1949             tag: 'li',
1950             cls: 'dropdown-menu-item',
1951             cn: [
1952                     {
1953                         tag : 'a',
1954                         href : '#',
1955                         html : 'Link'
1956                     }
1957                 ]
1958         };
1959         if (this.parent().type == 'treeview') {
1960             cfg.cls = 'treeview-menu';
1961         }
1962         
1963         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1964         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1965         return cfg;
1966     },
1967     
1968     initEvents: function() {
1969         
1970         //this.el.select('a').on('click', this.onClick, this);
1971         
1972     },
1973     onClick : function(e)
1974     {
1975         Roo.log('item on click ');
1976         //if(this.preventDefault){
1977         //    e.preventDefault();
1978         //}
1979         //this.parent().hideMenuItems();
1980         
1981         this.fireEvent('click', this, e);
1982     },
1983     getEl : function()
1984     {
1985         return this.el;
1986     }
1987 });
1988
1989  
1990
1991  /*
1992  * - LGPL
1993  *
1994  * menu separator
1995  * 
1996  */
1997
1998
1999 /**
2000  * @class Roo.bootstrap.MenuSeparator
2001  * @extends Roo.bootstrap.Component
2002  * Bootstrap MenuSeparator class
2003  * 
2004  * @constructor
2005  * Create a new MenuItem
2006  * @param {Object} config The config object
2007  */
2008
2009
2010 Roo.bootstrap.MenuSeparator = function(config){
2011     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2012 };
2013
2014 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2015     
2016     getAutoCreate : function(){
2017         var cfg = {
2018             cls: 'divider',
2019             tag : 'li'
2020         };
2021         
2022         return cfg;
2023     }
2024    
2025 });
2026
2027  
2028
2029  
2030 /*
2031 <div class="modal fade">
2032   <div class="modal-dialog">
2033     <div class="modal-content">
2034       <div class="modal-header">
2035         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2036         <h4 class="modal-title">Modal title</h4>
2037       </div>
2038       <div class="modal-body">
2039         <p>One fine body&hellip;</p>
2040       </div>
2041       <div class="modal-footer">
2042         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2043         <button type="button" class="btn btn-primary">Save changes</button>
2044       </div>
2045     </div><!-- /.modal-content -->
2046   </div><!-- /.modal-dialog -->
2047 </div><!-- /.modal -->
2048 */
2049 /*
2050  * - LGPL
2051  *
2052  * page contgainer.
2053  * 
2054  */
2055
2056 /**
2057  * @class Roo.bootstrap.Modal
2058  * @extends Roo.bootstrap.Component
2059  * Bootstrap Modal class
2060  * @cfg {String} title Title of dialog
2061  * @cfg {Boolean} specificTitle (true|false) default false
2062  * @cfg {Array} buttons Array of buttons or standard button set..
2063  * @cfg {String} buttonPosition (left|right|center) default right
2064  * @cfg {Boolean} animate (true | false) default true
2065  * 
2066  * @constructor
2067  * Create a new Modal Dialog
2068  * @param {Object} config The config object
2069  */
2070
2071 Roo.bootstrap.Modal = function(config){
2072     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2073     this.addEvents({
2074         // raw events
2075         /**
2076          * @event btnclick
2077          * The raw btnclick event for the button
2078          * @param {Roo.EventObject} e
2079          */
2080         "btnclick" : true
2081     });
2082     this.buttons = this.buttons || [];
2083 };
2084
2085 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2086     
2087     title : 'test dialog',
2088    
2089     buttons : false,
2090     
2091     // set on load...
2092     body:  false,
2093     
2094     specificTitle: false,
2095     
2096     buttonPosition: 'right',
2097     
2098     animate : true,
2099     
2100     onRender : function(ct, position)
2101     {
2102         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2103      
2104         if(!this.el){
2105             var cfg = Roo.apply({},  this.getAutoCreate());
2106             cfg.id = Roo.id();
2107             //if(!cfg.name){
2108             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2109             //}
2110             //if (!cfg.name.length) {
2111             //    delete cfg.name;
2112            // }
2113             if (this.cls) {
2114                 cfg.cls += ' ' + this.cls;
2115             }
2116             if (this.style) {
2117                 cfg.style = this.style;
2118             }
2119             this.el = Roo.get(document.body).createChild(cfg, position);
2120         }
2121         //var type = this.el.dom.type;
2122         
2123         if(this.tabIndex !== undefined){
2124             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2125         }
2126         
2127         
2128         
2129         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2130         this.maskEl.enableDisplayMode("block");
2131         this.maskEl.hide();
2132         //this.el.addClass("x-dlg-modal");
2133     
2134         if (this.buttons.length) {
2135             Roo.each(this.buttons, function(bb) {
2136                 b = Roo.apply({}, bb);
2137                 b.xns = b.xns || Roo.bootstrap;
2138                 b.xtype = b.xtype || 'Button';
2139                 if (typeof(b.listeners) == 'undefined') {
2140                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2141                 }
2142                 
2143                 var btn = Roo.factory(b);
2144                 
2145                 btn.onRender(this.el.select('.modal-footer div').first());
2146                 
2147             },this);
2148         }
2149         // render the children.
2150         var nitems = [];
2151         
2152         if(typeof(this.items) != 'undefined'){
2153             var items = this.items;
2154             delete this.items;
2155
2156             for(var i =0;i < items.length;i++) {
2157                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2158             }
2159         }
2160         
2161         this.items = nitems;
2162         
2163         this.body = this.el.select('.modal-body',true).first();
2164         this.close = this.el.select('.modal-header .close', true).first();
2165         this.footer = this.el.select('.modal-footer',true).first();
2166         this.initEvents();
2167         //this.el.addClass([this.fieldClass, this.cls]);
2168         
2169     },
2170     getAutoCreate : function(){
2171         
2172         
2173         var bdy = {
2174                 cls : 'modal-body',
2175                 html : this.html || ''
2176         };
2177         
2178         var title = {
2179             tag: 'h4',
2180             cls : 'modal-title',
2181             html : this.title
2182         };
2183         
2184         if(this.specificTitle){
2185             title = this.title;
2186         };
2187         
2188         var modal = {
2189             cls: "modal",
2190             style : 'display: none',
2191             cn : [
2192                 {
2193                     cls: "modal-dialog",
2194                     cn : [
2195                         {
2196                             cls : "modal-content",
2197                             cn : [
2198                                 {
2199                                     cls : 'modal-header',
2200                                     cn : [
2201                                         {
2202                                             tag: 'button',
2203                                             cls : 'close',
2204                                             html : '&times'
2205                                         },
2206                                         title
2207                                     ]
2208                                 },
2209                                 bdy,
2210                                 {
2211                                     cls : 'modal-footer',
2212                                     cn : [
2213                                         {
2214                                             tag: 'div',
2215                                             cls: 'btn-' + this.buttonPosition
2216                                         }
2217                                     ]
2218                                     
2219                                 }
2220                                 
2221                                 
2222                             ]
2223                             
2224                         }
2225                     ]
2226                         
2227                 }
2228             ]
2229         };
2230         
2231         if(this.animate){
2232             modal.cls += ' fade';
2233         }
2234         
2235         return modal;
2236           
2237     },
2238     getChildContainer : function() {
2239          
2240          return this.el.select('.modal-body',true).first();
2241         
2242     },
2243     getButtonContainer : function() {
2244          return this.el.select('.modal-footer div',true).first();
2245         
2246     },
2247     initEvents : function()
2248     {
2249         this.el.select('.modal-header .close').on('click', this.hide, this);
2250 //        
2251 //        this.addxtype(this);
2252     },
2253     show : function() {
2254         
2255         if (!this.rendered) {
2256             this.render();
2257         }
2258         
2259         this.el.setStyle('display', 'block');
2260         
2261         if(this.animate){
2262             var _this = this;
2263             (function(){ _this.el.addClass('in'); }).defer(50);
2264         }else{
2265             this.el.addClass('in');
2266         }
2267         
2268         Roo.get(document.body).addClass("x-body-masked");
2269         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2270         this.maskEl.show();
2271         this.el.setStyle('zIndex', '10001');
2272         this.fireEvent('show', this);
2273         
2274         
2275     },
2276     hide : function()
2277     {
2278         this.maskEl.hide();
2279         Roo.get(document.body).removeClass("x-body-masked");
2280         this.el.removeClass('in');
2281         
2282         if(this.animate){
2283             var _this = this;
2284             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2285         }else{
2286             this.el.setStyle('display', 'none');
2287         }
2288         
2289         this.fireEvent('hide', this);
2290     },
2291     
2292     addButton : function(str, cb)
2293     {
2294          
2295         
2296         var b = Roo.apply({}, { html : str } );
2297         b.xns = b.xns || Roo.bootstrap;
2298         b.xtype = b.xtype || 'Button';
2299         if (typeof(b.listeners) == 'undefined') {
2300             b.listeners = { click : cb.createDelegate(this)  };
2301         }
2302         
2303         var btn = Roo.factory(b);
2304            
2305         btn.onRender(this.el.select('.modal-footer div').first());
2306         
2307         return btn;   
2308        
2309     },
2310     
2311     setDefaultButton : function(btn)
2312     {
2313         //this.el.select('.modal-footer').()
2314     },
2315     resizeTo: function(w,h)
2316     {
2317         // skip..
2318     },
2319     setContentSize  : function(w, h)
2320     {
2321         
2322     },
2323     onButtonClick: function(btn,e)
2324     {
2325         //Roo.log([a,b,c]);
2326         this.fireEvent('btnclick', btn.name, e);
2327     },
2328     setTitle: function(str) {
2329         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2330         
2331     }
2332 });
2333
2334
2335 Roo.apply(Roo.bootstrap.Modal,  {
2336     /**
2337          * Button config that displays a single OK button
2338          * @type Object
2339          */
2340         OK :  [{
2341             name : 'ok',
2342             weight : 'primary',
2343             html : 'OK'
2344         }], 
2345         /**
2346          * Button config that displays Yes and No buttons
2347          * @type Object
2348          */
2349         YESNO : [
2350             {
2351                 name  : 'no',
2352                 html : 'No'
2353             },
2354             {
2355                 name  :'yes',
2356                 weight : 'primary',
2357                 html : 'Yes'
2358             }
2359         ],
2360         
2361         /**
2362          * Button config that displays OK and Cancel buttons
2363          * @type Object
2364          */
2365         OKCANCEL : [
2366             {
2367                name : 'cancel',
2368                 html : 'Cancel'
2369             },
2370             {
2371                 name : 'ok',
2372                 weight : 'primary',
2373                 html : 'OK'
2374             }
2375         ],
2376         /**
2377          * Button config that displays Yes, No and Cancel buttons
2378          * @type Object
2379          */
2380         YESNOCANCEL : [
2381             {
2382                 name : 'yes',
2383                 weight : 'primary',
2384                 html : 'Yes'
2385             },
2386             {
2387                 name : 'no',
2388                 html : 'No'
2389             },
2390             {
2391                 name : 'cancel',
2392                 html : 'Cancel'
2393             }
2394         ]
2395 });
2396  /*
2397  * - LGPL
2398  *
2399  * messagebox - can be used as a replace
2400  * 
2401  */
2402 /**
2403  * @class Roo.MessageBox
2404  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2405  * Example usage:
2406  *<pre><code>
2407 // Basic alert:
2408 Roo.Msg.alert('Status', 'Changes saved successfully.');
2409
2410 // Prompt for user data:
2411 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2412     if (btn == 'ok'){
2413         // process text value...
2414     }
2415 });
2416
2417 // Show a dialog using config options:
2418 Roo.Msg.show({
2419    title:'Save Changes?',
2420    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2421    buttons: Roo.Msg.YESNOCANCEL,
2422    fn: processResult,
2423    animEl: 'elId'
2424 });
2425 </code></pre>
2426  * @singleton
2427  */
2428 Roo.bootstrap.MessageBox = function(){
2429     var dlg, opt, mask, waitTimer;
2430     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2431     var buttons, activeTextEl, bwidth;
2432
2433     
2434     // private
2435     var handleButton = function(button){
2436         dlg.hide();
2437         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2438     };
2439
2440     // private
2441     var handleHide = function(){
2442         if(opt && opt.cls){
2443             dlg.el.removeClass(opt.cls);
2444         }
2445         //if(waitTimer){
2446         //    Roo.TaskMgr.stop(waitTimer);
2447         //    waitTimer = null;
2448         //}
2449     };
2450
2451     // private
2452     var updateButtons = function(b){
2453         var width = 0;
2454         if(!b){
2455             buttons["ok"].hide();
2456             buttons["cancel"].hide();
2457             buttons["yes"].hide();
2458             buttons["no"].hide();
2459             //dlg.footer.dom.style.display = 'none';
2460             return width;
2461         }
2462         dlg.footer.dom.style.display = '';
2463         for(var k in buttons){
2464             if(typeof buttons[k] != "function"){
2465                 if(b[k]){
2466                     buttons[k].show();
2467                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2468                     width += buttons[k].el.getWidth()+15;
2469                 }else{
2470                     buttons[k].hide();
2471                 }
2472             }
2473         }
2474         return width;
2475     };
2476
2477     // private
2478     var handleEsc = function(d, k, e){
2479         if(opt && opt.closable !== false){
2480             dlg.hide();
2481         }
2482         if(e){
2483             e.stopEvent();
2484         }
2485     };
2486
2487     return {
2488         /**
2489          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2490          * @return {Roo.BasicDialog} The BasicDialog element
2491          */
2492         getDialog : function(){
2493            if(!dlg){
2494                 dlg = new Roo.bootstrap.Modal( {
2495                     //draggable: true,
2496                     //resizable:false,
2497                     //constraintoviewport:false,
2498                     //fixedcenter:true,
2499                     //collapsible : false,
2500                     //shim:true,
2501                     //modal: true,
2502                   //  width:400,
2503                   //  height:100,
2504                     //buttonAlign:"center",
2505                     closeClick : function(){
2506                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2507                             handleButton("no");
2508                         }else{
2509                             handleButton("cancel");
2510                         }
2511                     }
2512                 });
2513                 dlg.render();
2514                 dlg.on("hide", handleHide);
2515                 mask = dlg.mask;
2516                 //dlg.addKeyListener(27, handleEsc);
2517                 buttons = {};
2518                 this.buttons = buttons;
2519                 var bt = this.buttonText;
2520                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2521                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2522                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2523                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2524                 Roo.log(buttons)
2525                 bodyEl = dlg.body.createChild({
2526
2527                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2528                         '<textarea class="roo-mb-textarea"></textarea>' +
2529                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2530                 });
2531                 msgEl = bodyEl.dom.firstChild;
2532                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2533                 textboxEl.enableDisplayMode();
2534                 textboxEl.addKeyListener([10,13], function(){
2535                     if(dlg.isVisible() && opt && opt.buttons){
2536                         if(opt.buttons.ok){
2537                             handleButton("ok");
2538                         }else if(opt.buttons.yes){
2539                             handleButton("yes");
2540                         }
2541                     }
2542                 });
2543                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2544                 textareaEl.enableDisplayMode();
2545                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2546                 progressEl.enableDisplayMode();
2547                 var pf = progressEl.dom.firstChild;
2548                 if (pf) {
2549                     pp = Roo.get(pf.firstChild);
2550                     pp.setHeight(pf.offsetHeight);
2551                 }
2552                 
2553             }
2554             return dlg;
2555         },
2556
2557         /**
2558          * Updates the message box body text
2559          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2560          * the XHTML-compliant non-breaking space character '&amp;#160;')
2561          * @return {Roo.MessageBox} This message box
2562          */
2563         updateText : function(text){
2564             if(!dlg.isVisible() && !opt.width){
2565                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2566             }
2567             msgEl.innerHTML = text || '&#160;';
2568       
2569             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2570             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2571             var w = Math.max(
2572                     Math.min(opt.width || cw , this.maxWidth), 
2573                     Math.max(opt.minWidth || this.minWidth, bwidth)
2574             );
2575             if(opt.prompt){
2576                 activeTextEl.setWidth(w);
2577             }
2578             if(dlg.isVisible()){
2579                 dlg.fixedcenter = false;
2580             }
2581             // to big, make it scroll. = But as usual stupid IE does not support
2582             // !important..
2583             
2584             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2585                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2586                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2587             } else {
2588                 bodyEl.dom.style.height = '';
2589                 bodyEl.dom.style.overflowY = '';
2590             }
2591             if (cw > w) {
2592                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2593             } else {
2594                 bodyEl.dom.style.overflowX = '';
2595             }
2596             
2597             dlg.setContentSize(w, bodyEl.getHeight());
2598             if(dlg.isVisible()){
2599                 dlg.fixedcenter = true;
2600             }
2601             return this;
2602         },
2603
2604         /**
2605          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2606          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2607          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2608          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2609          * @return {Roo.MessageBox} This message box
2610          */
2611         updateProgress : function(value, text){
2612             if(text){
2613                 this.updateText(text);
2614             }
2615             if (pp) { // weird bug on my firefox - for some reason this is not defined
2616                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2617             }
2618             return this;
2619         },        
2620
2621         /**
2622          * Returns true if the message box is currently displayed
2623          * @return {Boolean} True if the message box is visible, else false
2624          */
2625         isVisible : function(){
2626             return dlg && dlg.isVisible();  
2627         },
2628
2629         /**
2630          * Hides the message box if it is displayed
2631          */
2632         hide : function(){
2633             if(this.isVisible()){
2634                 dlg.hide();
2635             }  
2636         },
2637
2638         /**
2639          * Displays a new message box, or reinitializes an existing message box, based on the config options
2640          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2641          * The following config object properties are supported:
2642          * <pre>
2643 Property    Type             Description
2644 ----------  ---------------  ------------------------------------------------------------------------------------
2645 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2646                                    closes (defaults to undefined)
2647 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2648                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2649 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2650                                    progress and wait dialogs will ignore this property and always hide the
2651                                    close button as they can only be closed programmatically.
2652 cls               String           A custom CSS class to apply to the message box element
2653 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2654                                    displayed (defaults to 75)
2655 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2656                                    function will be btn (the name of the button that was clicked, if applicable,
2657                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2658                                    Progress and wait dialogs will ignore this option since they do not respond to
2659                                    user actions and can only be closed programmatically, so any required function
2660                                    should be called by the same code after it closes the dialog.
2661 icon              String           A CSS class that provides a background image to be used as an icon for
2662                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2663 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2664 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2665 modal             Boolean          False to allow user interaction with the page while the message box is
2666                                    displayed (defaults to true)
2667 msg               String           A string that will replace the existing message box body text (defaults
2668                                    to the XHTML-compliant non-breaking space character '&#160;')
2669 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2670 progress          Boolean          True to display a progress bar (defaults to false)
2671 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2672 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2673 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2674 title             String           The title text
2675 value             String           The string value to set into the active textbox element if displayed
2676 wait              Boolean          True to display a progress bar (defaults to false)
2677 width             Number           The width of the dialog in pixels
2678 </pre>
2679          *
2680          * Example usage:
2681          * <pre><code>
2682 Roo.Msg.show({
2683    title: 'Address',
2684    msg: 'Please enter your address:',
2685    width: 300,
2686    buttons: Roo.MessageBox.OKCANCEL,
2687    multiline: true,
2688    fn: saveAddress,
2689    animEl: 'addAddressBtn'
2690 });
2691 </code></pre>
2692          * @param {Object} config Configuration options
2693          * @return {Roo.MessageBox} This message box
2694          */
2695         show : function(options)
2696         {
2697             
2698             // this causes nightmares if you show one dialog after another
2699             // especially on callbacks..
2700              
2701             if(this.isVisible()){
2702                 
2703                 this.hide();
2704                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2705                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2706                 Roo.log("New Dialog Message:" +  options.msg )
2707                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2708                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2709                 
2710             }
2711             var d = this.getDialog();
2712             opt = options;
2713             d.setTitle(opt.title || "&#160;");
2714             d.close.setDisplayed(opt.closable !== false);
2715             activeTextEl = textboxEl;
2716             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2717             if(opt.prompt){
2718                 if(opt.multiline){
2719                     textboxEl.hide();
2720                     textareaEl.show();
2721                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2722                         opt.multiline : this.defaultTextHeight);
2723                     activeTextEl = textareaEl;
2724                 }else{
2725                     textboxEl.show();
2726                     textareaEl.hide();
2727                 }
2728             }else{
2729                 textboxEl.hide();
2730                 textareaEl.hide();
2731             }
2732             progressEl.setDisplayed(opt.progress === true);
2733             this.updateProgress(0);
2734             activeTextEl.dom.value = opt.value || "";
2735             if(opt.prompt){
2736                 dlg.setDefaultButton(activeTextEl);
2737             }else{
2738                 var bs = opt.buttons;
2739                 var db = null;
2740                 if(bs && bs.ok){
2741                     db = buttons["ok"];
2742                 }else if(bs && bs.yes){
2743                     db = buttons["yes"];
2744                 }
2745                 dlg.setDefaultButton(db);
2746             }
2747             bwidth = updateButtons(opt.buttons);
2748             this.updateText(opt.msg);
2749             if(opt.cls){
2750                 d.el.addClass(opt.cls);
2751             }
2752             d.proxyDrag = opt.proxyDrag === true;
2753             d.modal = opt.modal !== false;
2754             d.mask = opt.modal !== false ? mask : false;
2755             if(!d.isVisible()){
2756                 // force it to the end of the z-index stack so it gets a cursor in FF
2757                 document.body.appendChild(dlg.el.dom);
2758                 d.animateTarget = null;
2759                 d.show(options.animEl);
2760             }
2761             return this;
2762         },
2763
2764         /**
2765          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2766          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2767          * and closing the message box when the process is complete.
2768          * @param {String} title The title bar text
2769          * @param {String} msg The message box body text
2770          * @return {Roo.MessageBox} This message box
2771          */
2772         progress : function(title, msg){
2773             this.show({
2774                 title : title,
2775                 msg : msg,
2776                 buttons: false,
2777                 progress:true,
2778                 closable:false,
2779                 minWidth: this.minProgressWidth,
2780                 modal : true
2781             });
2782             return this;
2783         },
2784
2785         /**
2786          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2787          * If a callback function is passed it will be called after the user clicks the button, and the
2788          * id of the button that was clicked will be passed as the only parameter to the callback
2789          * (could also be the top-right close button).
2790          * @param {String} title The title bar text
2791          * @param {String} msg The message box body text
2792          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2793          * @param {Object} scope (optional) The scope of the callback function
2794          * @return {Roo.MessageBox} This message box
2795          */
2796         alert : function(title, msg, fn, scope){
2797             this.show({
2798                 title : title,
2799                 msg : msg,
2800                 buttons: this.OK,
2801                 fn: fn,
2802                 scope : scope,
2803                 modal : true
2804             });
2805             return this;
2806         },
2807
2808         /**
2809          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2810          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2811          * You are responsible for closing the message box when the process is complete.
2812          * @param {String} msg The message box body text
2813          * @param {String} title (optional) The title bar text
2814          * @return {Roo.MessageBox} This message box
2815          */
2816         wait : function(msg, title){
2817             this.show({
2818                 title : title,
2819                 msg : msg,
2820                 buttons: false,
2821                 closable:false,
2822                 progress:true,
2823                 modal:true,
2824                 width:300,
2825                 wait:true
2826             });
2827             waitTimer = Roo.TaskMgr.start({
2828                 run: function(i){
2829                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2830                 },
2831                 interval: 1000
2832             });
2833             return this;
2834         },
2835
2836         /**
2837          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2838          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2839          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2840          * @param {String} title The title bar text
2841          * @param {String} msg The message box body text
2842          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2843          * @param {Object} scope (optional) The scope of the callback function
2844          * @return {Roo.MessageBox} This message box
2845          */
2846         confirm : function(title, msg, fn, scope){
2847             this.show({
2848                 title : title,
2849                 msg : msg,
2850                 buttons: this.YESNO,
2851                 fn: fn,
2852                 scope : scope,
2853                 modal : true
2854             });
2855             return this;
2856         },
2857
2858         /**
2859          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2860          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2861          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2862          * (could also be the top-right close button) and the text that was entered will be passed as the two
2863          * parameters to the callback.
2864          * @param {String} title The title bar text
2865          * @param {String} msg The message box body text
2866          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2867          * @param {Object} scope (optional) The scope of the callback function
2868          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2869          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2870          * @return {Roo.MessageBox} This message box
2871          */
2872         prompt : function(title, msg, fn, scope, multiline){
2873             this.show({
2874                 title : title,
2875                 msg : msg,
2876                 buttons: this.OKCANCEL,
2877                 fn: fn,
2878                 minWidth:250,
2879                 scope : scope,
2880                 prompt:true,
2881                 multiline: multiline,
2882                 modal : true
2883             });
2884             return this;
2885         },
2886
2887         /**
2888          * Button config that displays a single OK button
2889          * @type Object
2890          */
2891         OK : {ok:true},
2892         /**
2893          * Button config that displays Yes and No buttons
2894          * @type Object
2895          */
2896         YESNO : {yes:true, no:true},
2897         /**
2898          * Button config that displays OK and Cancel buttons
2899          * @type Object
2900          */
2901         OKCANCEL : {ok:true, cancel:true},
2902         /**
2903          * Button config that displays Yes, No and Cancel buttons
2904          * @type Object
2905          */
2906         YESNOCANCEL : {yes:true, no:true, cancel:true},
2907
2908         /**
2909          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2910          * @type Number
2911          */
2912         defaultTextHeight : 75,
2913         /**
2914          * The maximum width in pixels of the message box (defaults to 600)
2915          * @type Number
2916          */
2917         maxWidth : 600,
2918         /**
2919          * The minimum width in pixels of the message box (defaults to 100)
2920          * @type Number
2921          */
2922         minWidth : 100,
2923         /**
2924          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2925          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2926          * @type Number
2927          */
2928         minProgressWidth : 250,
2929         /**
2930          * An object containing the default button text strings that can be overriden for localized language support.
2931          * Supported properties are: ok, cancel, yes and no.
2932          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2933          * @type Object
2934          */
2935         buttonText : {
2936             ok : "OK",
2937             cancel : "Cancel",
2938             yes : "Yes",
2939             no : "No"
2940         }
2941     };
2942 }();
2943
2944 /**
2945  * Shorthand for {@link Roo.MessageBox}
2946  */
2947 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2948 Roo.Msg = Roo.Msg || Roo.MessageBox;
2949 /*
2950  * - LGPL
2951  *
2952  * navbar
2953  * 
2954  */
2955
2956 /**
2957  * @class Roo.bootstrap.Navbar
2958  * @extends Roo.bootstrap.Component
2959  * Bootstrap Navbar class
2960
2961  * @constructor
2962  * Create a new Navbar
2963  * @param {Object} config The config object
2964  */
2965
2966
2967 Roo.bootstrap.Navbar = function(config){
2968     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2969     
2970 };
2971
2972 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2973     
2974     
2975    
2976     // private
2977     navItems : false,
2978     loadMask : false,
2979     
2980     
2981     getAutoCreate : function(){
2982         
2983         
2984         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2985         
2986     },
2987     
2988     initEvents :function ()
2989     {
2990         //Roo.log(this.el.select('.navbar-toggle',true));
2991         this.el.select('.navbar-toggle',true).on('click', function() {
2992            // Roo.log('click');
2993             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2994         }, this);
2995         
2996         var mark = {
2997             tag: "div",
2998             cls:"x-dlg-mask"
2999         }
3000         
3001         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3002         
3003         var size = this.el.getSize();
3004         this.maskEl.setSize(size.width, size.height);
3005         this.maskEl.enableDisplayMode("block");
3006         this.maskEl.hide();
3007         
3008         if(this.loadMask){
3009             this.maskEl.show();
3010         }
3011     },
3012     
3013     
3014     getChildContainer : function()
3015     {
3016         if (this.el.select('.collapse').getCount()) {
3017             return this.el.select('.collapse',true).first();
3018         }
3019         
3020         return this.el;
3021     },
3022     
3023     mask : function()
3024     {
3025         this.maskEl.show();
3026     },
3027     
3028     unmask : function()
3029     {
3030         this.maskEl.hide();
3031     } 
3032     
3033     
3034     
3035     
3036 });
3037
3038
3039
3040  
3041
3042  /*
3043  * - LGPL
3044  *
3045  * navbar
3046  * 
3047  */
3048
3049 /**
3050  * @class Roo.bootstrap.NavSimplebar
3051  * @extends Roo.bootstrap.Navbar
3052  * Bootstrap Sidebar class
3053  *
3054  * @cfg {Boolean} inverse is inverted color
3055  * 
3056  * @cfg {String} type (nav | pills | tabs)
3057  * @cfg {Boolean} arrangement stacked | justified
3058  * @cfg {String} align (left | right) alignment
3059  * 
3060  * @cfg {Boolean} main (true|false) main nav bar? default false
3061  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3062  * 
3063  * @cfg {String} tag (header|footer|nav|div) default is nav 
3064
3065  * 
3066  * 
3067  * 
3068  * @constructor
3069  * Create a new Sidebar
3070  * @param {Object} config The config object
3071  */
3072
3073
3074 Roo.bootstrap.NavSimplebar = function(config){
3075     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3076 };
3077
3078 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3079     
3080     inverse: false,
3081     
3082     type: false,
3083     arrangement: '',
3084     align : false,
3085     
3086     
3087     
3088     main : false,
3089     
3090     
3091     tag : false,
3092     
3093     
3094     getAutoCreate : function(){
3095         
3096         
3097         var cfg = {
3098             tag : this.tag || 'div',
3099             cls : 'navbar'
3100         };
3101           
3102         
3103         cfg.cn = [
3104             {
3105                 cls: 'nav',
3106                 tag : 'ul'
3107             }
3108         ];
3109         
3110          
3111         this.type = this.type || 'nav';
3112         if (['tabs','pills'].indexOf(this.type)!==-1) {
3113             cfg.cn[0].cls += ' nav-' + this.type
3114         
3115         
3116         } else {
3117             if (this.type!=='nav') {
3118                 Roo.log('nav type must be nav/tabs/pills')
3119             }
3120             cfg.cn[0].cls += ' navbar-nav'
3121         }
3122         
3123         
3124         
3125         
3126         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3127             cfg.cn[0].cls += ' nav-' + this.arrangement;
3128         }
3129         
3130         
3131         if (this.align === 'right') {
3132             cfg.cn[0].cls += ' navbar-right';
3133         }
3134         
3135         if (this.inverse) {
3136             cfg.cls += ' navbar-inverse';
3137             
3138         }
3139         
3140         
3141         return cfg;
3142     
3143         
3144     }
3145     
3146     
3147     
3148 });
3149
3150
3151
3152  
3153
3154  
3155        /*
3156  * - LGPL
3157  *
3158  * navbar
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavHeaderbar
3164  * @extends Roo.bootstrap.NavSimplebar
3165  * Bootstrap Sidebar class
3166  *
3167  * @cfg {String} brand what is brand
3168  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3169  * @cfg {String} brand_href href of the brand
3170  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3171  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3172  * 
3173  * @constructor
3174  * Create a new Sidebar
3175  * @param {Object} config The config object
3176  */
3177
3178
3179 Roo.bootstrap.NavHeaderbar = function(config){
3180     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3181 };
3182
3183 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3184     
3185     position: '',
3186     brand: '',
3187     brand_href: false,
3188     srButton : true,
3189     autohide : false,
3190     
3191     getAutoCreate : function(){
3192         
3193         var   cfg = {
3194             tag: this.nav || 'nav',
3195             cls: 'navbar',
3196             role: 'navigation',
3197             cn: []
3198         };
3199         
3200         if(this.srButton){
3201             cfg.cn.push({
3202                 tag: 'div',
3203                 cls: 'navbar-header',
3204                 cn: [
3205                     {
3206                         tag: 'button',
3207                         type: 'button',
3208                         cls: 'navbar-toggle',
3209                         'data-toggle': 'collapse',
3210                         cn: [
3211                             {
3212                                 tag: 'span',
3213                                 cls: 'sr-only',
3214                                 html: 'Toggle navigation'
3215                             },
3216                             {
3217                                 tag: 'span',
3218                                 cls: 'icon-bar'
3219                             },
3220                             {
3221                                 tag: 'span',
3222                                 cls: 'icon-bar'
3223                             },
3224                             {
3225                                 tag: 'span',
3226                                 cls: 'icon-bar'
3227                             }
3228                         ]
3229                     }
3230                 ]
3231             });
3232         }
3233         
3234         cfg.cn.push({
3235             tag: 'div',
3236             cls: 'collapse navbar-collapse',
3237             cn : []
3238         });
3239         
3240         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3241         
3242         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3243             cfg.cls += ' navbar-' + this.position;
3244             
3245             // tag can override this..
3246             
3247             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3248         }
3249         
3250         if (this.brand !== '') {
3251             cfg.cn[0].cn.push({
3252                 tag: 'a',
3253                 href: this.brand_href ? this.brand_href : '#',
3254                 cls: 'navbar-brand',
3255                 cn: [
3256                 this.brand
3257                 ]
3258             });
3259         }
3260         
3261         if(this.main){
3262             cfg.cls += ' main-nav';
3263         }
3264         
3265         
3266         return cfg;
3267
3268         
3269     },
3270     initEvents : function()
3271     {
3272         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3273         
3274         if (this.autohide) {
3275             
3276             var prevScroll = 0;
3277             var ft = this.el;
3278             
3279             Roo.get(document).on('scroll',function(e) {
3280                 var ns = Roo.get(document).getScroll().top;
3281                 var os = prevScroll;
3282                 prevScroll = ns;
3283                 
3284                 if(ns > os){
3285                     ft.removeClass('slideDown');
3286                     ft.addClass('slideUp');
3287                     return;
3288                 }
3289                 ft.removeClass('slideUp');
3290                 ft.addClass('slideDown');
3291                  
3292               
3293           },this);
3294         }
3295     }    
3296           
3297       
3298     
3299     
3300 });
3301
3302
3303
3304  
3305
3306  /*
3307  * - LGPL
3308  *
3309  * navbar
3310  * 
3311  */
3312
3313 /**
3314  * @class Roo.bootstrap.NavSidebar
3315  * @extends Roo.bootstrap.Navbar
3316  * Bootstrap Sidebar class
3317  * 
3318  * @constructor
3319  * Create a new Sidebar
3320  * @param {Object} config The config object
3321  */
3322
3323
3324 Roo.bootstrap.NavSidebar = function(config){
3325     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3326 };
3327
3328 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3329     
3330     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3331     
3332     getAutoCreate : function(){
3333         
3334         
3335         return  {
3336             tag: 'div',
3337             cls: 'sidebar sidebar-nav'
3338         };
3339     
3340         
3341     }
3342     
3343     
3344     
3345 });
3346
3347
3348
3349  
3350
3351  /*
3352  * - LGPL
3353  *
3354  * nav group
3355  * 
3356  */
3357
3358 /**
3359  * @class Roo.bootstrap.NavGroup
3360  * @extends Roo.bootstrap.Component
3361  * Bootstrap NavGroup class
3362  * @cfg {String} align left | right
3363  * @cfg {Boolean} inverse false | true
3364  * @cfg {String} type (nav|pills|tab) default nav
3365  * @cfg {String} navId - reference Id for navbar.
3366
3367  * 
3368  * @constructor
3369  * Create a new nav group
3370  * @param {Object} config The config object
3371  */
3372
3373 Roo.bootstrap.NavGroup = function(config){
3374     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3375     this.navItems = [];
3376    
3377     Roo.bootstrap.NavGroup.register(this);
3378      this.addEvents({
3379         /**
3380              * @event changed
3381              * Fires when the active item changes
3382              * @param {Roo.bootstrap.NavGroup} this
3383              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3384              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3385          */
3386         'changed': true
3387      });
3388     
3389 };
3390
3391 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3392     
3393     align: '',
3394     inverse: false,
3395     form: false,
3396     type: 'nav',
3397     navId : '',
3398     // private
3399     
3400     navItems : false, 
3401     
3402     getAutoCreate : function()
3403     {
3404         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3405         
3406         cfg = {
3407             tag : 'ul',
3408             cls: 'nav' 
3409         }
3410         
3411         if (['tabs','pills'].indexOf(this.type)!==-1) {
3412             cfg.cls += ' nav-' + this.type
3413         } else {
3414             if (this.type!=='nav') {
3415                 Roo.log('nav type must be nav/tabs/pills')
3416             }
3417             cfg.cls += ' navbar-nav'
3418         }
3419         
3420         if (this.parent().sidebar) {
3421             cfg = {
3422                 tag: 'ul',
3423                 cls: 'dashboard-menu sidebar-menu'
3424             }
3425             
3426             return cfg;
3427         }
3428         
3429         if (this.form === true) {
3430             cfg = {
3431                 tag: 'form',
3432                 cls: 'navbar-form'
3433             }
3434             
3435             if (this.align === 'right') {
3436                 cfg.cls += ' navbar-right';
3437             } else {
3438                 cfg.cls += ' navbar-left';
3439             }
3440         }
3441         
3442         if (this.align === 'right') {
3443             cfg.cls += ' navbar-right';
3444         }
3445         
3446         if (this.inverse) {
3447             cfg.cls += ' navbar-inverse';
3448             
3449         }
3450         
3451         
3452         return cfg;
3453     },
3454     /**
3455     * sets the active Navigation item
3456     * @param {Roo.bootstrap.NavItem} the new current navitem
3457     */
3458     setActiveItem : function(item)
3459     {
3460         var prev = false;
3461         Roo.each(this.navItems, function(v){
3462             if (v == item) {
3463                 return ;
3464             }
3465             if (v.isActive()) {
3466                 v.setActive(false, true);
3467                 prev = v;
3468                 
3469             }
3470             
3471         });
3472
3473         item.setActive(true, true);
3474         this.fireEvent('changed', this, item, prev);
3475         
3476         
3477     },
3478     /**
3479     * gets the active Navigation item
3480     * @return {Roo.bootstrap.NavItem} the current navitem
3481     */
3482     getActive : function()
3483     {
3484         
3485         var prev = false;
3486         Roo.each(this.navItems, function(v){
3487             
3488             if (v.isActive()) {
3489                 prev = v;
3490                 
3491             }
3492             
3493         });
3494         return prev;
3495     },
3496     
3497     indexOfNav : function()
3498     {
3499         
3500         var prev = false;
3501         Roo.each(this.navItems, function(v,i){
3502             
3503             if (v.isActive()) {
3504                 prev = i;
3505                 
3506             }
3507             
3508         });
3509         return prev;
3510     },
3511     /**
3512     * adds a Navigation item
3513     * @param {Roo.bootstrap.NavItem} the navitem to add
3514     */
3515     addItem : function(cfg)
3516     {
3517         var cn = new Roo.bootstrap.NavItem(cfg);
3518         this.register(cn);
3519         cn.parentId = this.id;
3520         cn.onRender(this.el, null);
3521         return cn;
3522     },
3523     /**
3524     * register a Navigation item
3525     * @param {Roo.bootstrap.NavItem} the navitem to add
3526     */
3527     register : function(item)
3528     {
3529         this.navItems.push( item);
3530         item.navId = this.navId;
3531     
3532     },
3533     
3534     /**
3535     * clear all the Navigation item
3536     */
3537    
3538     clearAll : function()
3539     {
3540         this.navItems = [];
3541         this.el.dom.innerHTML = '';
3542     },
3543     
3544     getNavItem: function(tabId)
3545     {
3546         var ret = false;
3547         Roo.each(this.navItems, function(e) {
3548             if (e.tabId == tabId) {
3549                ret =  e;
3550                return false;
3551             }
3552             return true;
3553             
3554         });
3555         return ret;
3556     },
3557     
3558     setActiveNext : function()
3559     {
3560         var i = this.indexOfNav(this.getActive());
3561         if (i > this.navItems.length) {
3562             return;
3563         }
3564         this.setActiveItem(this.navItems[i+1]);
3565     },
3566     setActivePrev : function()
3567     {
3568         var i = this.indexOfNav(this.getActive());
3569         if (i  < 1) {
3570             return;
3571         }
3572         this.setActiveItem(this.navItems[i-1]);
3573     },
3574     clearWasActive : function(except) {
3575         Roo.each(this.navItems, function(e) {
3576             if (e.tabId != except.tabId && e.was_active) {
3577                e.was_active = false;
3578                return false;
3579             }
3580             return true;
3581             
3582         });
3583     },
3584     getWasActive : function ()
3585     {
3586         var r = false;
3587         Roo.each(this.navItems, function(e) {
3588             if (e.was_active) {
3589                r = e;
3590                return false;
3591             }
3592             return true;
3593             
3594         });
3595         return r;
3596     }
3597     
3598     
3599 });
3600
3601  
3602 Roo.apply(Roo.bootstrap.NavGroup, {
3603     
3604     groups: {},
3605      /**
3606     * register a Navigation Group
3607     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3608     */
3609     register : function(navgrp)
3610     {
3611         this.groups[navgrp.navId] = navgrp;
3612         
3613     },
3614     /**
3615     * fetch a Navigation Group based on the navigation ID
3616     * @param {string} the navgroup to add
3617     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3618     */
3619     get: function(navId) {
3620         if (typeof(this.groups[navId]) == 'undefined') {
3621             return false;
3622             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3623         }
3624         return this.groups[navId] ;
3625     }
3626     
3627     
3628     
3629 });
3630
3631  /*
3632  * - LGPL
3633  *
3634  * row
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavItem
3640  * @extends Roo.bootstrap.Component
3641  * Bootstrap Navbar.NavItem class
3642  * @cfg {String} href  link to
3643  * @cfg {String} html content of button
3644  * @cfg {String} badge text inside badge
3645  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3646  * @cfg {String} glyphicon name of glyphicon
3647  * @cfg {String} icon name of font awesome icon
3648  * @cfg {Boolean} active Is item active
3649  * @cfg {Boolean} disabled Is item disabled
3650  
3651  * @cfg {Boolean} preventDefault (true | false) default false
3652  * @cfg {String} tabId the tab that this item activates.
3653  * @cfg {String} tagtype (a|span) render as a href or span?
3654   
3655  * @constructor
3656  * Create a new Navbar Item
3657  * @param {Object} config The config object
3658  */
3659 Roo.bootstrap.NavItem = function(config){
3660     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3661     this.addEvents({
3662         // raw events
3663         /**
3664          * @event click
3665          * The raw click event for the entire grid.
3666          * @param {Roo.EventObject} e
3667          */
3668         "click" : true,
3669          /**
3670             * @event changed
3671             * Fires when the active item active state changes
3672             * @param {Roo.bootstrap.NavItem} this
3673             * @param {boolean} state the new state
3674              
3675          */
3676         'changed': true
3677     });
3678    
3679 };
3680
3681 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3682     
3683     href: false,
3684     html: '',
3685     badge: '',
3686     icon: false,
3687     glyphicon: false,
3688     active: false,
3689     preventDefault : false,
3690     tabId : false,
3691     tagtype : 'a',
3692     disabled : false,
3693     
3694     was_active : false,
3695     
3696     getAutoCreate : function(){
3697          
3698         var cfg = {
3699             tag: 'li',
3700             cls: 'nav-item'
3701             
3702         }
3703         if (this.active) {
3704             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3705         }
3706         if (this.disabled) {
3707             cfg.cls += ' disabled';
3708         }
3709         
3710         if (this.href || this.html || this.glyphicon || this.icon) {
3711             cfg.cn = [
3712                 {
3713                     tag: this.tagtype,
3714                     href : this.href || "#",
3715                     html: this.html || ''
3716                 }
3717             ];
3718             
3719             if (this.icon) {
3720                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3721             }
3722
3723             if(this.glyphicon) {
3724                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3725             }
3726             
3727             if (this.menu) {
3728                 
3729                 cfg.cn[0].html += " <span class='caret'></span>";
3730              
3731             }
3732             
3733             if (this.badge !== '') {
3734                  
3735                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3736             }
3737         }
3738         
3739         
3740         
3741         return cfg;
3742     },
3743     initEvents: function() 
3744     {
3745         if (typeof (this.menu) != 'undefined') {
3746             this.menu.parentType = this.xtype;
3747             this.menu.triggerEl = this.el;
3748             this.addxtype(Roo.apply({}, this.menu));
3749         }
3750         
3751         this.el.select('a',true).on('click', this.onClick, this);
3752         
3753         if(this.tagtype == 'span'){
3754             this.el.select('span',true).on('click', this.onClick, this);
3755         }
3756        
3757         // at this point parent should be available..
3758         this.parent().register(this);
3759     },
3760     
3761     onClick : function(e)
3762     {
3763          
3764         if(this.preventDefault){
3765             e.preventDefault();
3766         }
3767         if (this.disabled) {
3768             return;
3769         }
3770         
3771         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3772         if (tg && tg.transition) {
3773             Roo.log("waiting for the transitionend");
3774             return;
3775         }
3776         
3777         Roo.log("fire event clicked");
3778         if(this.fireEvent('click', this, e) === false){
3779             return;
3780         };
3781         
3782         if(this.tagtype == 'span'){
3783             return;
3784         }
3785         
3786         var p = this.parent();
3787         if (['tabs','pills'].indexOf(p.type)!==-1) {
3788             if (typeof(p.setActiveItem) !== 'undefined') {
3789                 p.setActiveItem(this);
3790             }
3791         }
3792         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3793         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3794             // remove the collapsed menu expand...
3795             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3796         }
3797         
3798     },
3799     
3800     isActive: function () {
3801         return this.active
3802     },
3803     setActive : function(state, fire, is_was_active)
3804     {
3805         if (this.active && !state & this.navId) {
3806             this.was_active = true;
3807             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3808             if (nv) {
3809                 nv.clearWasActive(this);
3810             }
3811             
3812         }
3813         this.active = state;
3814         
3815         if (!state ) {
3816             this.el.removeClass('active');
3817         } else if (!this.el.hasClass('active')) {
3818             this.el.addClass('active');
3819         }
3820         if (fire) {
3821             this.fireEvent('changed', this, state);
3822         }
3823         
3824         // show a panel if it's registered and related..
3825         
3826         if (!this.navId || !this.tabId || !state || is_was_active) {
3827             return;
3828         }
3829         
3830         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3831         if (!tg) {
3832             return;
3833         }
3834         var pan = tg.getPanelByName(this.tabId);
3835         if (!pan) {
3836             return;
3837         }
3838         // if we can not flip to new panel - go back to old nav highlight..
3839         if (false == tg.showPanel(pan)) {
3840             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3841             if (nv) {
3842                 var onav = nv.getWasActive();
3843                 if (onav) {
3844                     onav.setActive(true, false, true);
3845                 }
3846             }
3847             
3848         }
3849         
3850         
3851         
3852     },
3853      // this should not be here...
3854     setDisabled : function(state)
3855     {
3856         this.disabled = state;
3857         if (!state ) {
3858             this.el.removeClass('disabled');
3859         } else if (!this.el.hasClass('disabled')) {
3860             this.el.addClass('disabled');
3861         }
3862         
3863     }
3864 });
3865  
3866
3867  /*
3868  * - LGPL
3869  *
3870  * sidebar item
3871  *
3872  *  li
3873  *    <span> icon </span>
3874  *    <span> text </span>
3875  *    <span>badge </span>
3876  */
3877
3878 /**
3879  * @class Roo.bootstrap.NavSidebarItem
3880  * @extends Roo.bootstrap.NavItem
3881  * Bootstrap Navbar.NavSidebarItem class
3882  * @constructor
3883  * Create a new Navbar Button
3884  * @param {Object} config The config object
3885  */
3886 Roo.bootstrap.NavSidebarItem = function(config){
3887     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3888     this.addEvents({
3889         // raw events
3890         /**
3891          * @event click
3892          * The raw click event for the entire grid.
3893          * @param {Roo.EventObject} e
3894          */
3895         "click" : true,
3896          /**
3897             * @event changed
3898             * Fires when the active item active state changes
3899             * @param {Roo.bootstrap.NavSidebarItem} this
3900             * @param {boolean} state the new state
3901              
3902          */
3903         'changed': true
3904     });
3905    
3906 };
3907
3908 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3909     
3910     
3911     getAutoCreate : function(){
3912         
3913         
3914         var a = {
3915                 tag: 'a',
3916                 href : this.href || '#',
3917                 cls: '',
3918                 html : '',
3919                 cn : []
3920         };
3921         var cfg = {
3922             tag: 'li',
3923             cls: '',
3924             cn: [ a ]
3925         }
3926         var span = {
3927             tag: 'span',
3928             html : this.html || ''
3929         }
3930         
3931         
3932         if (this.active) {
3933             cfg.cls += ' active';
3934         }
3935         
3936         // left icon..
3937         if (this.glyphicon || this.icon) {
3938             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3939             a.cn.push({ tag : 'i', cls : c }) ;
3940         }
3941         // html..
3942         a.cn.push(span);
3943         // then badge..
3944         if (this.badge !== '') {
3945             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3946         }
3947         // fi
3948         if (this.menu) {
3949             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3950             a.cls += 'dropdown-toggle treeview' ;
3951             
3952         }
3953         
3954         
3955         
3956         return cfg;
3957          
3958            
3959     }
3960    
3961      
3962  
3963 });
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * row
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.Row
3975  * @extends Roo.bootstrap.Component
3976  * Bootstrap Row class (contains columns...)
3977  * 
3978  * @constructor
3979  * Create a new Row
3980  * @param {Object} config The config object
3981  */
3982
3983 Roo.bootstrap.Row = function(config){
3984     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3985 };
3986
3987 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3988     
3989     getAutoCreate : function(){
3990        return {
3991             cls: 'row clearfix'
3992        };
3993     }
3994     
3995     
3996 });
3997
3998  
3999
4000  /*
4001  * - LGPL
4002  *
4003  * element
4004  * 
4005  */
4006
4007 /**
4008  * @class Roo.bootstrap.Element
4009  * @extends Roo.bootstrap.Component
4010  * Bootstrap Element class
4011  * @cfg {String} html contents of the element
4012  * @cfg {String} tag tag of the element
4013  * @cfg {String} cls class of the element
4014  * 
4015  * @constructor
4016  * Create a new Element
4017  * @param {Object} config The config object
4018  */
4019
4020 Roo.bootstrap.Element = function(config){
4021     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4022 };
4023
4024 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4025     
4026     tag: 'div',
4027     cls: '',
4028     html: '',
4029      
4030     
4031     getAutoCreate : function(){
4032         
4033         var cfg = {
4034             tag: this.tag,
4035             cls: this.cls,
4036             html: this.html
4037         }
4038         
4039         
4040         
4041         return cfg;
4042     }
4043    
4044 });
4045
4046  
4047
4048  /*
4049  * - LGPL
4050  *
4051  * pagination
4052  * 
4053  */
4054
4055 /**
4056  * @class Roo.bootstrap.Pagination
4057  * @extends Roo.bootstrap.Component
4058  * Bootstrap Pagination class
4059  * @cfg {String} size xs | sm | md | lg
4060  * @cfg {Boolean} inverse false | true
4061  * 
4062  * @constructor
4063  * Create a new Pagination
4064  * @param {Object} config The config object
4065  */
4066
4067 Roo.bootstrap.Pagination = function(config){
4068     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4069 };
4070
4071 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4072     
4073     cls: false,
4074     size: false,
4075     inverse: false,
4076     
4077     getAutoCreate : function(){
4078         var cfg = {
4079             tag: 'ul',
4080                 cls: 'pagination'
4081         };
4082         if (this.inverse) {
4083             cfg.cls += ' inverse';
4084         }
4085         if (this.html) {
4086             cfg.html=this.html;
4087         }
4088         if (this.cls) {
4089             cfg.cls += " " + this.cls;
4090         }
4091         return cfg;
4092     }
4093    
4094 });
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * Pagination item
4102  * 
4103  */
4104
4105
4106 /**
4107  * @class Roo.bootstrap.PaginationItem
4108  * @extends Roo.bootstrap.Component
4109  * Bootstrap PaginationItem class
4110  * @cfg {String} html text
4111  * @cfg {String} href the link
4112  * @cfg {Boolean} preventDefault (true | false) default true
4113  * @cfg {Boolean} active (true | false) default false
4114  * 
4115  * 
4116  * @constructor
4117  * Create a new PaginationItem
4118  * @param {Object} config The config object
4119  */
4120
4121
4122 Roo.bootstrap.PaginationItem = function(config){
4123     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4124     this.addEvents({
4125         // raw events
4126         /**
4127          * @event click
4128          * The raw click event for the entire grid.
4129          * @param {Roo.EventObject} e
4130          */
4131         "click" : true
4132     });
4133 };
4134
4135 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4136     
4137     href : false,
4138     html : false,
4139     preventDefault: true,
4140     active : false,
4141     cls : false,
4142     
4143     getAutoCreate : function(){
4144         var cfg= {
4145             tag: 'li',
4146             cn: [
4147                 {
4148                     tag : 'a',
4149                     href : this.href ? this.href : '#',
4150                     html : this.html ? this.html : ''
4151                 }
4152             ]
4153         };
4154         
4155         if(this.cls){
4156             cfg.cls = this.cls;
4157         }
4158         
4159         if(this.active){
4160             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4161         }
4162         
4163         return cfg;
4164     },
4165     
4166     initEvents: function() {
4167         
4168         this.el.on('click', this.onClick, this);
4169         
4170     },
4171     onClick : function(e)
4172     {
4173         Roo.log('PaginationItem on click ');
4174         if(this.preventDefault){
4175             e.preventDefault();
4176         }
4177         
4178         this.fireEvent('click', this, e);
4179     }
4180    
4181 });
4182
4183  
4184
4185  /*
4186  * - LGPL
4187  *
4188  * slider
4189  * 
4190  */
4191
4192
4193 /**
4194  * @class Roo.bootstrap.Slider
4195  * @extends Roo.bootstrap.Component
4196  * Bootstrap Slider class
4197  *    
4198  * @constructor
4199  * Create a new Slider
4200  * @param {Object} config The config object
4201  */
4202
4203 Roo.bootstrap.Slider = function(config){
4204     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4205 };
4206
4207 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4208     
4209     getAutoCreate : function(){
4210         
4211         var cfg = {
4212             tag: 'div',
4213             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4214             cn: [
4215                 {
4216                     tag: 'a',
4217                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4218                 }
4219             ]
4220         }
4221         
4222         return cfg;
4223     }
4224    
4225 });
4226
4227  /*
4228  * Based on:
4229  * Ext JS Library 1.1.1
4230  * Copyright(c) 2006-2007, Ext JS, LLC.
4231  *
4232  * Originally Released Under LGPL - original licence link has changed is not relivant.
4233  *
4234  * Fork - LGPL
4235  * <script type="text/javascript">
4236  */
4237  
4238
4239 /**
4240  * @class Roo.grid.ColumnModel
4241  * @extends Roo.util.Observable
4242  * This is the default implementation of a ColumnModel used by the Grid. It defines
4243  * the columns in the grid.
4244  * <br>Usage:<br>
4245  <pre><code>
4246  var colModel = new Roo.grid.ColumnModel([
4247         {header: "Ticker", width: 60, sortable: true, locked: true},
4248         {header: "Company Name", width: 150, sortable: true},
4249         {header: "Market Cap.", width: 100, sortable: true},
4250         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4251         {header: "Employees", width: 100, sortable: true, resizable: false}
4252  ]);
4253  </code></pre>
4254  * <p>
4255  
4256  * The config options listed for this class are options which may appear in each
4257  * individual column definition.
4258  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4259  * @constructor
4260  * @param {Object} config An Array of column config objects. See this class's
4261  * config objects for details.
4262 */
4263 Roo.grid.ColumnModel = function(config){
4264         /**
4265      * The config passed into the constructor
4266      */
4267     this.config = config;
4268     this.lookup = {};
4269
4270     // if no id, create one
4271     // if the column does not have a dataIndex mapping,
4272     // map it to the order it is in the config
4273     for(var i = 0, len = config.length; i < len; i++){
4274         var c = config[i];
4275         if(typeof c.dataIndex == "undefined"){
4276             c.dataIndex = i;
4277         }
4278         if(typeof c.renderer == "string"){
4279             c.renderer = Roo.util.Format[c.renderer];
4280         }
4281         if(typeof c.id == "undefined"){
4282             c.id = Roo.id();
4283         }
4284         if(c.editor && c.editor.xtype){
4285             c.editor  = Roo.factory(c.editor, Roo.grid);
4286         }
4287         if(c.editor && c.editor.isFormField){
4288             c.editor = new Roo.grid.GridEditor(c.editor);
4289         }
4290         this.lookup[c.id] = c;
4291     }
4292
4293     /**
4294      * The width of columns which have no width specified (defaults to 100)
4295      * @type Number
4296      */
4297     this.defaultWidth = 100;
4298
4299     /**
4300      * Default sortable of columns which have no sortable specified (defaults to false)
4301      * @type Boolean
4302      */
4303     this.defaultSortable = false;
4304
4305     this.addEvents({
4306         /**
4307              * @event widthchange
4308              * Fires when the width of a column changes.
4309              * @param {ColumnModel} this
4310              * @param {Number} columnIndex The column index
4311              * @param {Number} newWidth The new width
4312              */
4313             "widthchange": true,
4314         /**
4315              * @event headerchange
4316              * Fires when the text of a header changes.
4317              * @param {ColumnModel} this
4318              * @param {Number} columnIndex The column index
4319              * @param {Number} newText The new header text
4320              */
4321             "headerchange": true,
4322         /**
4323              * @event hiddenchange
4324              * Fires when a column is hidden or "unhidden".
4325              * @param {ColumnModel} this
4326              * @param {Number} columnIndex The column index
4327              * @param {Boolean} hidden true if hidden, false otherwise
4328              */
4329             "hiddenchange": true,
4330             /**
4331          * @event columnmoved
4332          * Fires when a column is moved.
4333          * @param {ColumnModel} this
4334          * @param {Number} oldIndex
4335          * @param {Number} newIndex
4336          */
4337         "columnmoved" : true,
4338         /**
4339          * @event columlockchange
4340          * Fires when a column's locked state is changed
4341          * @param {ColumnModel} this
4342          * @param {Number} colIndex
4343          * @param {Boolean} locked true if locked
4344          */
4345         "columnlockchange" : true
4346     });
4347     Roo.grid.ColumnModel.superclass.constructor.call(this);
4348 };
4349 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4350     /**
4351      * @cfg {String} header The header text to display in the Grid view.
4352      */
4353     /**
4354      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4355      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4356      * specified, the column's index is used as an index into the Record's data Array.
4357      */
4358     /**
4359      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4360      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4361      */
4362     /**
4363      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4364      * Defaults to the value of the {@link #defaultSortable} property.
4365      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4366      */
4367     /**
4368      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4369      */
4370     /**
4371      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4372      */
4373     /**
4374      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4375      */
4376     /**
4377      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4378      */
4379     /**
4380      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4381      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4382      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4383      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4384      */
4385        /**
4386      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4387      */
4388     /**
4389      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4390      */
4391
4392     /**
4393      * Returns the id of the column at the specified index.
4394      * @param {Number} index The column index
4395      * @return {String} the id
4396      */
4397     getColumnId : function(index){
4398         return this.config[index].id;
4399     },
4400
4401     /**
4402      * Returns the column for a specified id.
4403      * @param {String} id The column id
4404      * @return {Object} the column
4405      */
4406     getColumnById : function(id){
4407         return this.lookup[id];
4408     },
4409
4410     
4411     /**
4412      * Returns the column for a specified dataIndex.
4413      * @param {String} dataIndex The column dataIndex
4414      * @return {Object|Boolean} the column or false if not found
4415      */
4416     getColumnByDataIndex: function(dataIndex){
4417         var index = this.findColumnIndex(dataIndex);
4418         return index > -1 ? this.config[index] : false;
4419     },
4420     
4421     /**
4422      * Returns the index for a specified column id.
4423      * @param {String} id The column id
4424      * @return {Number} the index, or -1 if not found
4425      */
4426     getIndexById : function(id){
4427         for(var i = 0, len = this.config.length; i < len; i++){
4428             if(this.config[i].id == id){
4429                 return i;
4430             }
4431         }
4432         return -1;
4433     },
4434     
4435     /**
4436      * Returns the index for a specified column dataIndex.
4437      * @param {String} dataIndex The column dataIndex
4438      * @return {Number} the index, or -1 if not found
4439      */
4440     
4441     findColumnIndex : function(dataIndex){
4442         for(var i = 0, len = this.config.length; i < len; i++){
4443             if(this.config[i].dataIndex == dataIndex){
4444                 return i;
4445             }
4446         }
4447         return -1;
4448     },
4449     
4450     
4451     moveColumn : function(oldIndex, newIndex){
4452         var c = this.config[oldIndex];
4453         this.config.splice(oldIndex, 1);
4454         this.config.splice(newIndex, 0, c);
4455         this.dataMap = null;
4456         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4457     },
4458
4459     isLocked : function(colIndex){
4460         return this.config[colIndex].locked === true;
4461     },
4462
4463     setLocked : function(colIndex, value, suppressEvent){
4464         if(this.isLocked(colIndex) == value){
4465             return;
4466         }
4467         this.config[colIndex].locked = value;
4468         if(!suppressEvent){
4469             this.fireEvent("columnlockchange", this, colIndex, value);
4470         }
4471     },
4472
4473     getTotalLockedWidth : function(){
4474         var totalWidth = 0;
4475         for(var i = 0; i < this.config.length; i++){
4476             if(this.isLocked(i) && !this.isHidden(i)){
4477                 this.totalWidth += this.getColumnWidth(i);
4478             }
4479         }
4480         return totalWidth;
4481     },
4482
4483     getLockedCount : function(){
4484         for(var i = 0, len = this.config.length; i < len; i++){
4485             if(!this.isLocked(i)){
4486                 return i;
4487             }
4488         }
4489     },
4490
4491     /**
4492      * Returns the number of columns.
4493      * @return {Number}
4494      */
4495     getColumnCount : function(visibleOnly){
4496         if(visibleOnly === true){
4497             var c = 0;
4498             for(var i = 0, len = this.config.length; i < len; i++){
4499                 if(!this.isHidden(i)){
4500                     c++;
4501                 }
4502             }
4503             return c;
4504         }
4505         return this.config.length;
4506     },
4507
4508     /**
4509      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4510      * @param {Function} fn
4511      * @param {Object} scope (optional)
4512      * @return {Array} result
4513      */
4514     getColumnsBy : function(fn, scope){
4515         var r = [];
4516         for(var i = 0, len = this.config.length; i < len; i++){
4517             var c = this.config[i];
4518             if(fn.call(scope||this, c, i) === true){
4519                 r[r.length] = c;
4520             }
4521         }
4522         return r;
4523     },
4524
4525     /**
4526      * Returns true if the specified column is sortable.
4527      * @param {Number} col The column index
4528      * @return {Boolean}
4529      */
4530     isSortable : function(col){
4531         if(typeof this.config[col].sortable == "undefined"){
4532             return this.defaultSortable;
4533         }
4534         return this.config[col].sortable;
4535     },
4536
4537     /**
4538      * Returns the rendering (formatting) function defined for the column.
4539      * @param {Number} col The column index.
4540      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4541      */
4542     getRenderer : function(col){
4543         if(!this.config[col].renderer){
4544             return Roo.grid.ColumnModel.defaultRenderer;
4545         }
4546         return this.config[col].renderer;
4547     },
4548
4549     /**
4550      * Sets the rendering (formatting) function for a column.
4551      * @param {Number} col The column index
4552      * @param {Function} fn The function to use to process the cell's raw data
4553      * to return HTML markup for the grid view. The render function is called with
4554      * the following parameters:<ul>
4555      * <li>Data value.</li>
4556      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4557      * <li>css A CSS style string to apply to the table cell.</li>
4558      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4559      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4560      * <li>Row index</li>
4561      * <li>Column index</li>
4562      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4563      */
4564     setRenderer : function(col, fn){
4565         this.config[col].renderer = fn;
4566     },
4567
4568     /**
4569      * Returns the width for the specified column.
4570      * @param {Number} col The column index
4571      * @return {Number}
4572      */
4573     getColumnWidth : function(col){
4574         return this.config[col].width * 1 || this.defaultWidth;
4575     },
4576
4577     /**
4578      * Sets the width for a column.
4579      * @param {Number} col The column index
4580      * @param {Number} width The new width
4581      */
4582     setColumnWidth : function(col, width, suppressEvent){
4583         this.config[col].width = width;
4584         this.totalWidth = null;
4585         if(!suppressEvent){
4586              this.fireEvent("widthchange", this, col, width);
4587         }
4588     },
4589
4590     /**
4591      * Returns the total width of all columns.
4592      * @param {Boolean} includeHidden True to include hidden column widths
4593      * @return {Number}
4594      */
4595     getTotalWidth : function(includeHidden){
4596         if(!this.totalWidth){
4597             this.totalWidth = 0;
4598             for(var i = 0, len = this.config.length; i < len; i++){
4599                 if(includeHidden || !this.isHidden(i)){
4600                     this.totalWidth += this.getColumnWidth(i);
4601                 }
4602             }
4603         }
4604         return this.totalWidth;
4605     },
4606
4607     /**
4608      * Returns the header for the specified column.
4609      * @param {Number} col The column index
4610      * @return {String}
4611      */
4612     getColumnHeader : function(col){
4613         return this.config[col].header;
4614     },
4615
4616     /**
4617      * Sets the header for a column.
4618      * @param {Number} col The column index
4619      * @param {String} header The new header
4620      */
4621     setColumnHeader : function(col, header){
4622         this.config[col].header = header;
4623         this.fireEvent("headerchange", this, col, header);
4624     },
4625
4626     /**
4627      * Returns the tooltip for the specified column.
4628      * @param {Number} col The column index
4629      * @return {String}
4630      */
4631     getColumnTooltip : function(col){
4632             return this.config[col].tooltip;
4633     },
4634     /**
4635      * Sets the tooltip for a column.
4636      * @param {Number} col The column index
4637      * @param {String} tooltip The new tooltip
4638      */
4639     setColumnTooltip : function(col, tooltip){
4640             this.config[col].tooltip = tooltip;
4641     },
4642
4643     /**
4644      * Returns the dataIndex for the specified column.
4645      * @param {Number} col The column index
4646      * @return {Number}
4647      */
4648     getDataIndex : function(col){
4649         return this.config[col].dataIndex;
4650     },
4651
4652     /**
4653      * Sets the dataIndex for a column.
4654      * @param {Number} col The column index
4655      * @param {Number} dataIndex The new dataIndex
4656      */
4657     setDataIndex : function(col, dataIndex){
4658         this.config[col].dataIndex = dataIndex;
4659     },
4660
4661     
4662     
4663     /**
4664      * Returns true if the cell is editable.
4665      * @param {Number} colIndex The column index
4666      * @param {Number} rowIndex The row index
4667      * @return {Boolean}
4668      */
4669     isCellEditable : function(colIndex, rowIndex){
4670         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4671     },
4672
4673     /**
4674      * Returns the editor defined for the cell/column.
4675      * return false or null to disable editing.
4676      * @param {Number} colIndex The column index
4677      * @param {Number} rowIndex The row index
4678      * @return {Object}
4679      */
4680     getCellEditor : function(colIndex, rowIndex){
4681         return this.config[colIndex].editor;
4682     },
4683
4684     /**
4685      * Sets if a column is editable.
4686      * @param {Number} col The column index
4687      * @param {Boolean} editable True if the column is editable
4688      */
4689     setEditable : function(col, editable){
4690         this.config[col].editable = editable;
4691     },
4692
4693
4694     /**
4695      * Returns true if the column is hidden.
4696      * @param {Number} colIndex The column index
4697      * @return {Boolean}
4698      */
4699     isHidden : function(colIndex){
4700         return this.config[colIndex].hidden;
4701     },
4702
4703
4704     /**
4705      * Returns true if the column width cannot be changed
4706      */
4707     isFixed : function(colIndex){
4708         return this.config[colIndex].fixed;
4709     },
4710
4711     /**
4712      * Returns true if the column can be resized
4713      * @return {Boolean}
4714      */
4715     isResizable : function(colIndex){
4716         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4717     },
4718     /**
4719      * Sets if a column is hidden.
4720      * @param {Number} colIndex The column index
4721      * @param {Boolean} hidden True if the column is hidden
4722      */
4723     setHidden : function(colIndex, hidden){
4724         this.config[colIndex].hidden = hidden;
4725         this.totalWidth = null;
4726         this.fireEvent("hiddenchange", this, colIndex, hidden);
4727     },
4728
4729     /**
4730      * Sets the editor for a column.
4731      * @param {Number} col The column index
4732      * @param {Object} editor The editor object
4733      */
4734     setEditor : function(col, editor){
4735         this.config[col].editor = editor;
4736     }
4737 });
4738
4739 Roo.grid.ColumnModel.defaultRenderer = function(value){
4740         if(typeof value == "string" && value.length < 1){
4741             return "&#160;";
4742         }
4743         return value;
4744 };
4745
4746 // Alias for backwards compatibility
4747 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4748 /*
4749  * Based on:
4750  * Ext JS Library 1.1.1
4751  * Copyright(c) 2006-2007, Ext JS, LLC.
4752  *
4753  * Originally Released Under LGPL - original licence link has changed is not relivant.
4754  *
4755  * Fork - LGPL
4756  * <script type="text/javascript">
4757  */
4758  
4759 /**
4760  * @class Roo.LoadMask
4761  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4762  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4763  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4764  * element's UpdateManager load indicator and will be destroyed after the initial load.
4765  * @constructor
4766  * Create a new LoadMask
4767  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4768  * @param {Object} config The config object
4769  */
4770 Roo.LoadMask = function(el, config){
4771     this.el = Roo.get(el);
4772     Roo.apply(this, config);
4773     if(this.store){
4774         this.store.on('beforeload', this.onBeforeLoad, this);
4775         this.store.on('load', this.onLoad, this);
4776         this.store.on('loadexception', this.onLoadException, this);
4777         this.removeMask = false;
4778     }else{
4779         var um = this.el.getUpdateManager();
4780         um.showLoadIndicator = false; // disable the default indicator
4781         um.on('beforeupdate', this.onBeforeLoad, this);
4782         um.on('update', this.onLoad, this);
4783         um.on('failure', this.onLoad, this);
4784         this.removeMask = true;
4785     }
4786 };
4787
4788 Roo.LoadMask.prototype = {
4789     /**
4790      * @cfg {Boolean} removeMask
4791      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4792      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4793      */
4794     /**
4795      * @cfg {String} msg
4796      * The text to display in a centered loading message box (defaults to 'Loading...')
4797      */
4798     msg : 'Loading...',
4799     /**
4800      * @cfg {String} msgCls
4801      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4802      */
4803     msgCls : 'x-mask-loading',
4804
4805     /**
4806      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4807      * @type Boolean
4808      */
4809     disabled: false,
4810
4811     /**
4812      * Disables the mask to prevent it from being displayed
4813      */
4814     disable : function(){
4815        this.disabled = true;
4816     },
4817
4818     /**
4819      * Enables the mask so that it can be displayed
4820      */
4821     enable : function(){
4822         this.disabled = false;
4823     },
4824     
4825     onLoadException : function()
4826     {
4827         Roo.log(arguments);
4828         
4829         if (typeof(arguments[3]) != 'undefined') {
4830             Roo.MessageBox.alert("Error loading",arguments[3]);
4831         } 
4832         /*
4833         try {
4834             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4835                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4836             }   
4837         } catch(e) {
4838             
4839         }
4840         */
4841     
4842         
4843         
4844         this.el.unmask(this.removeMask);
4845     },
4846     // private
4847     onLoad : function()
4848     {
4849         this.el.unmask(this.removeMask);
4850     },
4851
4852     // private
4853     onBeforeLoad : function(){
4854         if(!this.disabled){
4855             this.el.mask(this.msg, this.msgCls);
4856         }
4857     },
4858
4859     // private
4860     destroy : function(){
4861         if(this.store){
4862             this.store.un('beforeload', this.onBeforeLoad, this);
4863             this.store.un('load', this.onLoad, this);
4864             this.store.un('loadexception', this.onLoadException, this);
4865         }else{
4866             var um = this.el.getUpdateManager();
4867             um.un('beforeupdate', this.onBeforeLoad, this);
4868             um.un('update', this.onLoad, this);
4869             um.un('failure', this.onLoad, this);
4870         }
4871     }
4872 };/*
4873  * - LGPL
4874  *
4875  * table
4876  * 
4877  */
4878
4879 /**
4880  * @class Roo.bootstrap.Table
4881  * @extends Roo.bootstrap.Component
4882  * Bootstrap Table class
4883  * @cfg {String} cls table class
4884  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4885  * @cfg {String} bgcolor Specifies the background color for a table
4886  * @cfg {Number} border Specifies whether the table cells should have borders or not
4887  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4888  * @cfg {Number} cellspacing Specifies the space between cells
4889  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4890  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4891  * @cfg {String} sortable Specifies that the table should be sortable
4892  * @cfg {String} summary Specifies a summary of the content of a table
4893  * @cfg {Number} width Specifies the width of a table
4894  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4895  * 
4896  * @cfg {boolean} striped Should the rows be alternative striped
4897  * @cfg {boolean} bordered Add borders to the table
4898  * @cfg {boolean} hover Add hover highlighting
4899  * @cfg {boolean} condensed Format condensed
4900  * @cfg {boolean} responsive Format condensed
4901  * @cfg {Boolean} loadMask (true|false) default false
4902  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4903  * @cfg {Boolean} thead (true|false) generate thead, default true
4904  * @cfg {Boolean} RowSelection (true|false) default false
4905  * @cfg {Boolean} CellSelection (true|false) default false
4906  *
4907  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4908  
4909  * 
4910  * @constructor
4911  * Create a new Table
4912  * @param {Object} config The config object
4913  */
4914
4915 Roo.bootstrap.Table = function(config){
4916     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4917     
4918     if (this.sm) {
4919         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4920         this.sm = this.selModel;
4921         this.sm.xmodule = this.xmodule || false;
4922     }
4923     if (this.cm && typeof(this.cm.config) == 'undefined') {
4924         this.colModel = new Roo.grid.ColumnModel(this.cm);
4925         this.cm = this.colModel;
4926         this.cm.xmodule = this.xmodule || false;
4927     }
4928     if (this.store) {
4929         this.store= Roo.factory(this.store, Roo.data);
4930         this.ds = this.store;
4931         this.ds.xmodule = this.xmodule || false;
4932          
4933     }
4934     if (this.footer && this.store) {
4935         this.footer.dataSource = this.ds;
4936         this.footer = Roo.factory(this.footer);
4937     }
4938     
4939     /** @private */
4940     this.addEvents({
4941         /**
4942          * @event cellclick
4943          * Fires when a cell is clicked
4944          * @param {Roo.bootstrap.Table} this
4945          * @param {Roo.Element} el
4946          * @param {Number} rowIndex
4947          * @param {Number} columnIndex
4948          * @param {Roo.EventObject} e
4949          */
4950         "cellclick" : true,
4951         /**
4952          * @event celldblclick
4953          * Fires when a cell is double clicked
4954          * @param {Roo.bootstrap.Table} this
4955          * @param {Roo.Element} el
4956          * @param {Number} rowIndex
4957          * @param {Number} columnIndex
4958          * @param {Roo.EventObject} e
4959          */
4960         "celldblclick" : true,
4961         /**
4962          * @event rowclick
4963          * Fires when a row is clicked
4964          * @param {Roo.bootstrap.Table} this
4965          * @param {Roo.Element} el
4966          * @param {Number} rowIndex
4967          * @param {Roo.EventObject} e
4968          */
4969         "rowclick" : true,
4970         /**
4971          * @event rowdblclick
4972          * Fires when a row is double clicked
4973          * @param {Roo.bootstrap.Table} this
4974          * @param {Roo.Element} el
4975          * @param {Number} rowIndex
4976          * @param {Roo.EventObject} e
4977          */
4978         "rowdblclick" : true,
4979         /**
4980          * @event mouseover
4981          * Fires when a mouseover occur
4982          * @param {Roo.bootstrap.Table} this
4983          * @param {Roo.Element} el
4984          * @param {Number} rowIndex
4985          * @param {Number} columnIndex
4986          * @param {Roo.EventObject} e
4987          */
4988         "mouseover" : true,
4989         /**
4990          * @event mouseout
4991          * Fires when a mouseout occur
4992          * @param {Roo.bootstrap.Table} this
4993          * @param {Roo.Element} el
4994          * @param {Number} rowIndex
4995          * @param {Number} columnIndex
4996          * @param {Roo.EventObject} e
4997          */
4998         "mouseout" : true,
4999         /**
5000          * @event rowclass
5001          * Fires when a row is rendered, so you can change add a style to it.
5002          * @param {Roo.bootstrap.Table} this
5003          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5004          */
5005         'rowclass' : true
5006         
5007     });
5008 };
5009
5010 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5011     
5012     cls: false,
5013     align: false,
5014     bgcolor: false,
5015     border: false,
5016     cellpadding: false,
5017     cellspacing: false,
5018     frame: false,
5019     rules: false,
5020     sortable: false,
5021     summary: false,
5022     width: false,
5023     striped : false,
5024     bordered: false,
5025     hover:  false,
5026     condensed : false,
5027     responsive : false,
5028     sm : false,
5029     cm : false,
5030     store : false,
5031     loadMask : false,
5032     tfoot : true,
5033     thead : true,
5034     RowSelection : false,
5035     CellSelection : false,
5036     layout : false,
5037     
5038     // Roo.Element - the tbody
5039     mainBody: false, 
5040     
5041     getAutoCreate : function(){
5042         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5043         
5044         cfg = {
5045             tag: 'table',
5046             cls : 'table',
5047             cn : []
5048         }
5049             
5050         if (this.striped) {
5051             cfg.cls += ' table-striped';
5052         }
5053         
5054         if (this.hover) {
5055             cfg.cls += ' table-hover';
5056         }
5057         if (this.bordered) {
5058             cfg.cls += ' table-bordered';
5059         }
5060         if (this.condensed) {
5061             cfg.cls += ' table-condensed';
5062         }
5063         if (this.responsive) {
5064             cfg.cls += ' table-responsive';
5065         }
5066         
5067         if (this.cls) {
5068             cfg.cls+=  ' ' +this.cls;
5069         }
5070         
5071         // this lot should be simplifed...
5072         
5073         if (this.align) {
5074             cfg.align=this.align;
5075         }
5076         if (this.bgcolor) {
5077             cfg.bgcolor=this.bgcolor;
5078         }
5079         if (this.border) {
5080             cfg.border=this.border;
5081         }
5082         if (this.cellpadding) {
5083             cfg.cellpadding=this.cellpadding;
5084         }
5085         if (this.cellspacing) {
5086             cfg.cellspacing=this.cellspacing;
5087         }
5088         if (this.frame) {
5089             cfg.frame=this.frame;
5090         }
5091         if (this.rules) {
5092             cfg.rules=this.rules;
5093         }
5094         if (this.sortable) {
5095             cfg.sortable=this.sortable;
5096         }
5097         if (this.summary) {
5098             cfg.summary=this.summary;
5099         }
5100         if (this.width) {
5101             cfg.width=this.width;
5102         }
5103         if (this.layout) {
5104             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5105         }
5106         
5107         if(this.store || this.cm){
5108             if(this.thead){
5109                 cfg.cn.push(this.renderHeader());
5110             }
5111             
5112             cfg.cn.push(this.renderBody());
5113             
5114             if(this.tfoot){
5115                 cfg.cn.push(this.renderFooter());
5116             }
5117             
5118             cfg.cls+=  ' TableGrid';
5119         }
5120         
5121         return { cn : [ cfg ] };
5122     },
5123     
5124     initEvents : function()
5125     {   
5126         if(!this.store || !this.cm){
5127             return;
5128         }
5129         
5130         //Roo.log('initEvents with ds!!!!');
5131         
5132         this.mainBody = this.el.select('tbody', true).first();
5133         
5134         
5135         var _this = this;
5136         
5137         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5138             e.on('click', _this.sort, _this);
5139         });
5140         
5141         this.el.on("click", this.onClick, this);
5142         this.el.on("dblclick", this.onDblClick, this);
5143         
5144         this.parent().el.setStyle('position', 'relative');
5145         if (this.footer) {
5146             this.footer.parentId = this.id;
5147             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5148         }
5149         
5150         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5151         
5152         this.store.on('load', this.onLoad, this);
5153         this.store.on('beforeload', this.onBeforeLoad, this);
5154         this.store.on('update', this.onUpdate, this);
5155         
5156     },
5157     
5158     onMouseover : function(e, el)
5159     {
5160         var cell = Roo.get(el);
5161         
5162         if(!cell){
5163             return;
5164         }
5165         
5166         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5167             cell = cell.findParent('td', false, true);
5168         }
5169         
5170         var row = cell.findParent('tr', false, true);
5171         var cellIndex = cell.dom.cellIndex;
5172         var rowIndex = row.dom.rowIndex - 1; // start from 0
5173         
5174         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5175         
5176     },
5177     
5178     onMouseout : function(e, el)
5179     {
5180         var cell = Roo.get(el);
5181         
5182         if(!cell){
5183             return;
5184         }
5185         
5186         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5187             cell = cell.findParent('td', false, true);
5188         }
5189         
5190         var row = cell.findParent('tr', false, true);
5191         var cellIndex = cell.dom.cellIndex;
5192         var rowIndex = row.dom.rowIndex - 1; // start from 0
5193         
5194         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5195         
5196     },
5197     
5198     onClick : function(e, el)
5199     {
5200         var cell = Roo.get(el);
5201         
5202         if(!cell || (!this.CellSelection && !this.RowSelection)){
5203             return;
5204         }
5205         
5206         
5207         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5208             cell = cell.findParent('td', false, true);
5209         }
5210         
5211         var row = cell.findParent('tr', false, true);
5212         var cellIndex = cell.dom.cellIndex;
5213         var rowIndex = row.dom.rowIndex - 1;
5214         
5215         if(this.CellSelection){
5216             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5217         }
5218         
5219         if(this.RowSelection){
5220             this.fireEvent('rowclick', this, row, rowIndex, e);
5221         }
5222         
5223         
5224     },
5225     
5226     onDblClick : function(e,el)
5227     {
5228         var cell = Roo.get(el);
5229         
5230         if(!cell || (!this.CellSelection && !this.RowSelection)){
5231             return;
5232         }
5233         
5234         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5235             cell = cell.findParent('td', false, true);
5236         }
5237         
5238         var row = cell.findParent('tr', false, true);
5239         var cellIndex = cell.dom.cellIndex;
5240         var rowIndex = row.dom.rowIndex - 1;
5241         
5242         if(this.CellSelection){
5243             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5244         }
5245         
5246         if(this.RowSelection){
5247             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5248         }
5249     },
5250     
5251     sort : function(e,el)
5252     {
5253         var col = Roo.get(el)
5254         
5255         if(!col.hasClass('sortable')){
5256             return;
5257         }
5258         
5259         var sort = col.attr('sort');
5260         var dir = 'ASC';
5261         
5262         if(col.hasClass('glyphicon-arrow-up')){
5263             dir = 'DESC';
5264         }
5265         
5266         this.store.sortInfo = {field : sort, direction : dir};
5267         
5268         if (this.footer) {
5269             Roo.log("calling footer first");
5270             this.footer.onClick('first');
5271         } else {
5272         
5273             this.store.load({ params : { start : 0 } });
5274         }
5275     },
5276     
5277     renderHeader : function()
5278     {
5279         var header = {
5280             tag: 'thead',
5281             cn : []
5282         };
5283         
5284         var cm = this.cm;
5285         
5286         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5287             
5288             var config = cm.config[i];
5289                     
5290             var c = {
5291                 tag: 'th',
5292                 style : '',
5293                 html: cm.getColumnHeader(i)
5294             };
5295             
5296             if(typeof(config.hidden) != 'undefined' && config.hidden){
5297                 c.style += ' display:none;';
5298             }
5299             
5300             if(typeof(config.dataIndex) != 'undefined'){
5301                 c.sort = config.dataIndex;
5302             }
5303             
5304             if(typeof(config.sortable) != 'undefined' && config.sortable){
5305                 c.cls = 'sortable';
5306             }
5307             
5308             if(typeof(config.align) != 'undefined' && config.align.length){
5309                 c.style += ' text-align:' + config.align + ';';
5310             }
5311             
5312             if(typeof(config.width) != 'undefined'){
5313                 c.style += ' width:' + config.width + 'px;';
5314             }
5315             
5316             header.cn.push(c)
5317         }
5318         
5319         return header;
5320     },
5321     
5322     renderBody : function()
5323     {
5324         var body = {
5325             tag: 'tbody',
5326             cn : [
5327                 {
5328                     tag: 'tr',
5329                     cn : [
5330                         {
5331                             tag : 'td',
5332                             colspan :  this.cm.getColumnCount()
5333                         }
5334                     ]
5335                 }
5336             ]
5337         };
5338         
5339         return body;
5340     },
5341     
5342     renderFooter : function()
5343     {
5344         var footer = {
5345             tag: 'tfoot',
5346             cn : [
5347                 {
5348                     tag: 'tr',
5349                     cn : [
5350                         {
5351                             tag : 'td',
5352                             colspan :  this.cm.getColumnCount()
5353                         }
5354                     ]
5355                 }
5356             ]
5357         };
5358         
5359         return footer;
5360     },
5361     
5362     
5363     
5364     onLoad : function()
5365     {
5366         Roo.log('ds onload');
5367         this.clear();
5368         
5369         var _this = this;
5370         var cm = this.cm;
5371         var ds = this.store;
5372         
5373         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5374             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5375             
5376             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5377                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5378             }
5379             
5380             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5381                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5382             }
5383         });
5384         
5385         var tbody =  this.mainBody;
5386               
5387         if(ds.getCount() > 0){
5388             ds.data.each(function(d,rowIndex){
5389                 var row =  this.renderRow(cm, ds, rowIndex);
5390                 
5391                 tbody.createChild(row);
5392                 
5393                 var _this = this;
5394                 
5395                 if(row.cellObjects.length){
5396                     Roo.each(row.cellObjects, function(r){
5397                         _this.renderCellObject(r);
5398                     })
5399                 }
5400                 
5401             }, this);
5402         }
5403         
5404         Roo.each(this.el.select('tbody td', true).elements, function(e){
5405             e.on('mouseover', _this.onMouseover, _this);
5406         });
5407         
5408         Roo.each(this.el.select('tbody td', true).elements, function(e){
5409             e.on('mouseout', _this.onMouseout, _this);
5410         });
5411
5412         //if(this.loadMask){
5413         //    this.maskEl.hide();
5414         //}
5415     },
5416     
5417     
5418     onUpdate : function(ds,record)
5419     {
5420         this.refreshRow(record);
5421     },
5422     onRemove : function(ds, record, index, isUpdate){
5423         if(isUpdate !== true){
5424             this.fireEvent("beforerowremoved", this, index, record);
5425         }
5426         var bt = this.mainBody.dom;
5427         if(bt.rows[index]){
5428             bt.removeChild(bt.rows[index]);
5429         }
5430         
5431         if(isUpdate !== true){
5432             //this.stripeRows(index);
5433             //this.syncRowHeights(index, index);
5434             //this.layout();
5435             this.fireEvent("rowremoved", this, index, record);
5436         }
5437     },
5438     
5439     
5440     refreshRow : function(record){
5441         var ds = this.store, index;
5442         if(typeof record == 'number'){
5443             index = record;
5444             record = ds.getAt(index);
5445         }else{
5446             index = ds.indexOf(record);
5447         }
5448         this.insertRow(ds, index, true);
5449         this.onRemove(ds, record, index+1, true);
5450         //this.syncRowHeights(index, index);
5451         //this.layout();
5452         this.fireEvent("rowupdated", this, index, record);
5453     },
5454     
5455     insertRow : function(dm, rowIndex, isUpdate){
5456         
5457         if(!isUpdate){
5458             this.fireEvent("beforerowsinserted", this, rowIndex);
5459         }
5460             //var s = this.getScrollState();
5461         var row = this.renderRow(this.cm, this.store, rowIndex);
5462         // insert before rowIndex..
5463         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5464         
5465         var _this = this;
5466                 
5467         if(row.cellObjects.length){
5468             Roo.each(row.cellObjects, function(r){
5469                 _this.renderCellObject(r);
5470             })
5471         }
5472             
5473         if(!isUpdate){
5474             this.fireEvent("rowsinserted", this, rowIndex);
5475             //this.syncRowHeights(firstRow, lastRow);
5476             //this.stripeRows(firstRow);
5477             //this.layout();
5478         }
5479         
5480     },
5481     
5482     
5483     getRowDom : function(rowIndex)
5484     {
5485         // not sure if I need to check this.. but let's do it anyway..
5486         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5487                 this.mainBody.dom.rows[rowIndex] : false
5488     },
5489     // returns the object tree for a tr..
5490   
5491     
5492     renderRow : function(cm, ds, rowIndex) {
5493         
5494         var d = ds.getAt(rowIndex);
5495         
5496         var row = {
5497             tag : 'tr',
5498             cn : []
5499         };
5500             
5501         var cellObjects = [];
5502         
5503         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5504             var config = cm.config[i];
5505             
5506             var renderer = cm.getRenderer(i);
5507             var value = '';
5508             var id = false;
5509             
5510             if(typeof(renderer) !== 'undefined'){
5511                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5512             }
5513             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5514             // and are rendered into the cells after the row is rendered - using the id for the element.
5515             
5516             if(typeof(value) === 'object'){
5517                 id = Roo.id();
5518                 cellObjects.push({
5519                     container : id,
5520                     cfg : value 
5521                 })
5522             }
5523             
5524             var rowcfg = {
5525                 record: d,
5526                 rowIndex : rowIndex,
5527                 colIndex : i,
5528                 rowClass : ''
5529             }
5530
5531             this.fireEvent('rowclass', this, rowcfg);
5532             
5533             var td = {
5534                 tag: 'td',
5535                 cls : rowcfg.rowClass,
5536                 style: '',
5537                 html: (typeof(value) === 'object') ? '' : value
5538             };
5539             
5540             if (id) {
5541                 td.id = id;
5542             }
5543             
5544             if(typeof(config.hidden) != 'undefined' && config.hidden){
5545                 td.style += ' display:none;';
5546             }
5547             
5548             if(typeof(config.align) != 'undefined' && config.align.length){
5549                 td.style += ' text-align:' + config.align + ';';
5550             }
5551             
5552             if(typeof(config.width) != 'undefined'){
5553                 td.style += ' width:' +  config.width + 'px;';
5554             }
5555              
5556             row.cn.push(td);
5557            
5558         }
5559         
5560         row.cellObjects = cellObjects;
5561         
5562         return row;
5563           
5564     },
5565     
5566     
5567     
5568     onBeforeLoad : function()
5569     {
5570         //Roo.log('ds onBeforeLoad');
5571         
5572         //this.clear();
5573         
5574         //if(this.loadMask){
5575         //    this.maskEl.show();
5576         //}
5577     },
5578     
5579     clear : function()
5580     {
5581         this.el.select('tbody', true).first().dom.innerHTML = '';
5582     },
5583     
5584     getSelectionModel : function(){
5585         if(!this.selModel){
5586             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5587         }
5588         return this.selModel;
5589     },
5590     /*
5591      * Render the Roo.bootstrap object from renderder
5592      */
5593     renderCellObject : function(r)
5594     {
5595         var _this = this;
5596         
5597         var t = r.cfg.render(r.container);
5598         
5599         if(r.cfg.cn){
5600             Roo.each(r.cfg.cn, function(c){
5601                 var child = {
5602                     container: t.getChildContainer(),
5603                     cfg: c
5604                 }
5605                 _this.renderCellObject(child);
5606             })
5607         }
5608     }
5609    
5610 });
5611
5612  
5613
5614  /*
5615  * - LGPL
5616  *
5617  * table cell
5618  * 
5619  */
5620
5621 /**
5622  * @class Roo.bootstrap.TableCell
5623  * @extends Roo.bootstrap.Component
5624  * Bootstrap TableCell class
5625  * @cfg {String} html cell contain text
5626  * @cfg {String} cls cell class
5627  * @cfg {String} tag cell tag (td|th) default td
5628  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5629  * @cfg {String} align Aligns the content in a cell
5630  * @cfg {String} axis Categorizes cells
5631  * @cfg {String} bgcolor Specifies the background color of a cell
5632  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5633  * @cfg {Number} colspan Specifies the number of columns a cell should span
5634  * @cfg {String} headers Specifies one or more header cells a cell is related to
5635  * @cfg {Number} height Sets the height of a cell
5636  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5637  * @cfg {Number} rowspan Sets the number of rows a cell should span
5638  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5639  * @cfg {String} valign Vertical aligns the content in a cell
5640  * @cfg {Number} width Specifies the width of a cell
5641  * 
5642  * @constructor
5643  * Create a new TableCell
5644  * @param {Object} config The config object
5645  */
5646
5647 Roo.bootstrap.TableCell = function(config){
5648     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5649 };
5650
5651 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5652     
5653     html: false,
5654     cls: false,
5655     tag: false,
5656     abbr: false,
5657     align: false,
5658     axis: false,
5659     bgcolor: false,
5660     charoff: false,
5661     colspan: false,
5662     headers: false,
5663     height: false,
5664     nowrap: false,
5665     rowspan: false,
5666     scope: false,
5667     valign: false,
5668     width: false,
5669     
5670     
5671     getAutoCreate : function(){
5672         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5673         
5674         cfg = {
5675             tag: 'td'
5676         }
5677         
5678         if(this.tag){
5679             cfg.tag = this.tag;
5680         }
5681         
5682         if (this.html) {
5683             cfg.html=this.html
5684         }
5685         if (this.cls) {
5686             cfg.cls=this.cls
5687         }
5688         if (this.abbr) {
5689             cfg.abbr=this.abbr
5690         }
5691         if (this.align) {
5692             cfg.align=this.align
5693         }
5694         if (this.axis) {
5695             cfg.axis=this.axis
5696         }
5697         if (this.bgcolor) {
5698             cfg.bgcolor=this.bgcolor
5699         }
5700         if (this.charoff) {
5701             cfg.charoff=this.charoff
5702         }
5703         if (this.colspan) {
5704             cfg.colspan=this.colspan
5705         }
5706         if (this.headers) {
5707             cfg.headers=this.headers
5708         }
5709         if (this.height) {
5710             cfg.height=this.height
5711         }
5712         if (this.nowrap) {
5713             cfg.nowrap=this.nowrap
5714         }
5715         if (this.rowspan) {
5716             cfg.rowspan=this.rowspan
5717         }
5718         if (this.scope) {
5719             cfg.scope=this.scope
5720         }
5721         if (this.valign) {
5722             cfg.valign=this.valign
5723         }
5724         if (this.width) {
5725             cfg.width=this.width
5726         }
5727         
5728         
5729         return cfg;
5730     }
5731    
5732 });
5733
5734  
5735
5736  /*
5737  * - LGPL
5738  *
5739  * table row
5740  * 
5741  */
5742
5743 /**
5744  * @class Roo.bootstrap.TableRow
5745  * @extends Roo.bootstrap.Component
5746  * Bootstrap TableRow class
5747  * @cfg {String} cls row class
5748  * @cfg {String} align Aligns the content in a table row
5749  * @cfg {String} bgcolor Specifies a background color for a table row
5750  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5751  * @cfg {String} valign Vertical aligns the content in a table row
5752  * 
5753  * @constructor
5754  * Create a new TableRow
5755  * @param {Object} config The config object
5756  */
5757
5758 Roo.bootstrap.TableRow = function(config){
5759     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5760 };
5761
5762 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5763     
5764     cls: false,
5765     align: false,
5766     bgcolor: false,
5767     charoff: false,
5768     valign: false,
5769     
5770     getAutoCreate : function(){
5771         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5772         
5773         cfg = {
5774             tag: 'tr'
5775         }
5776             
5777         if(this.cls){
5778             cfg.cls = this.cls;
5779         }
5780         if(this.align){
5781             cfg.align = this.align;
5782         }
5783         if(this.bgcolor){
5784             cfg.bgcolor = this.bgcolor;
5785         }
5786         if(this.charoff){
5787             cfg.charoff = this.charoff;
5788         }
5789         if(this.valign){
5790             cfg.valign = this.valign;
5791         }
5792         
5793         return cfg;
5794     }
5795    
5796 });
5797
5798  
5799
5800  /*
5801  * - LGPL
5802  *
5803  * table body
5804  * 
5805  */
5806
5807 /**
5808  * @class Roo.bootstrap.TableBody
5809  * @extends Roo.bootstrap.Component
5810  * Bootstrap TableBody class
5811  * @cfg {String} cls element class
5812  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5813  * @cfg {String} align Aligns the content inside the element
5814  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5815  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5816  * 
5817  * @constructor
5818  * Create a new TableBody
5819  * @param {Object} config The config object
5820  */
5821
5822 Roo.bootstrap.TableBody = function(config){
5823     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5824 };
5825
5826 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5827     
5828     cls: false,
5829     tag: false,
5830     align: false,
5831     charoff: false,
5832     valign: false,
5833     
5834     getAutoCreate : function(){
5835         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5836         
5837         cfg = {
5838             tag: 'tbody'
5839         }
5840             
5841         if (this.cls) {
5842             cfg.cls=this.cls
5843         }
5844         if(this.tag){
5845             cfg.tag = this.tag;
5846         }
5847         
5848         if(this.align){
5849             cfg.align = this.align;
5850         }
5851         if(this.charoff){
5852             cfg.charoff = this.charoff;
5853         }
5854         if(this.valign){
5855             cfg.valign = this.valign;
5856         }
5857         
5858         return cfg;
5859     }
5860     
5861     
5862 //    initEvents : function()
5863 //    {
5864 //        
5865 //        if(!this.store){
5866 //            return;
5867 //        }
5868 //        
5869 //        this.store = Roo.factory(this.store, Roo.data);
5870 //        this.store.on('load', this.onLoad, this);
5871 //        
5872 //        this.store.load();
5873 //        
5874 //    },
5875 //    
5876 //    onLoad: function () 
5877 //    {   
5878 //        this.fireEvent('load', this);
5879 //    }
5880 //    
5881 //   
5882 });
5883
5884  
5885
5886  /*
5887  * Based on:
5888  * Ext JS Library 1.1.1
5889  * Copyright(c) 2006-2007, Ext JS, LLC.
5890  *
5891  * Originally Released Under LGPL - original licence link has changed is not relivant.
5892  *
5893  * Fork - LGPL
5894  * <script type="text/javascript">
5895  */
5896
5897 // as we use this in bootstrap.
5898 Roo.namespace('Roo.form');
5899  /**
5900  * @class Roo.form.Action
5901  * Internal Class used to handle form actions
5902  * @constructor
5903  * @param {Roo.form.BasicForm} el The form element or its id
5904  * @param {Object} config Configuration options
5905  */
5906
5907  
5908  
5909 // define the action interface
5910 Roo.form.Action = function(form, options){
5911     this.form = form;
5912     this.options = options || {};
5913 };
5914 /**
5915  * Client Validation Failed
5916  * @const 
5917  */
5918 Roo.form.Action.CLIENT_INVALID = 'client';
5919 /**
5920  * Server Validation Failed
5921  * @const 
5922  */
5923 Roo.form.Action.SERVER_INVALID = 'server';
5924  /**
5925  * Connect to Server Failed
5926  * @const 
5927  */
5928 Roo.form.Action.CONNECT_FAILURE = 'connect';
5929 /**
5930  * Reading Data from Server Failed
5931  * @const 
5932  */
5933 Roo.form.Action.LOAD_FAILURE = 'load';
5934
5935 Roo.form.Action.prototype = {
5936     type : 'default',
5937     failureType : undefined,
5938     response : undefined,
5939     result : undefined,
5940
5941     // interface method
5942     run : function(options){
5943
5944     },
5945
5946     // interface method
5947     success : function(response){
5948
5949     },
5950
5951     // interface method
5952     handleResponse : function(response){
5953
5954     },
5955
5956     // default connection failure
5957     failure : function(response){
5958         
5959         this.response = response;
5960         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5961         this.form.afterAction(this, false);
5962     },
5963
5964     processResponse : function(response){
5965         this.response = response;
5966         if(!response.responseText){
5967             return true;
5968         }
5969         this.result = this.handleResponse(response);
5970         return this.result;
5971     },
5972
5973     // utility functions used internally
5974     getUrl : function(appendParams){
5975         var url = this.options.url || this.form.url || this.form.el.dom.action;
5976         if(appendParams){
5977             var p = this.getParams();
5978             if(p){
5979                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5980             }
5981         }
5982         return url;
5983     },
5984
5985     getMethod : function(){
5986         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5987     },
5988
5989     getParams : function(){
5990         var bp = this.form.baseParams;
5991         var p = this.options.params;
5992         if(p){
5993             if(typeof p == "object"){
5994                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5995             }else if(typeof p == 'string' && bp){
5996                 p += '&' + Roo.urlEncode(bp);
5997             }
5998         }else if(bp){
5999             p = Roo.urlEncode(bp);
6000         }
6001         return p;
6002     },
6003
6004     createCallback : function(){
6005         return {
6006             success: this.success,
6007             failure: this.failure,
6008             scope: this,
6009             timeout: (this.form.timeout*1000),
6010             upload: this.form.fileUpload ? this.success : undefined
6011         };
6012     }
6013 };
6014
6015 Roo.form.Action.Submit = function(form, options){
6016     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6017 };
6018
6019 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6020     type : 'submit',
6021
6022     haveProgress : false,
6023     uploadComplete : false,
6024     
6025     // uploadProgress indicator.
6026     uploadProgress : function()
6027     {
6028         if (!this.form.progressUrl) {
6029             return;
6030         }
6031         
6032         if (!this.haveProgress) {
6033             Roo.MessageBox.progress("Uploading", "Uploading");
6034         }
6035         if (this.uploadComplete) {
6036            Roo.MessageBox.hide();
6037            return;
6038         }
6039         
6040         this.haveProgress = true;
6041    
6042         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6043         
6044         var c = new Roo.data.Connection();
6045         c.request({
6046             url : this.form.progressUrl,
6047             params: {
6048                 id : uid
6049             },
6050             method: 'GET',
6051             success : function(req){
6052                //console.log(data);
6053                 var rdata = false;
6054                 var edata;
6055                 try  {
6056                    rdata = Roo.decode(req.responseText)
6057                 } catch (e) {
6058                     Roo.log("Invalid data from server..");
6059                     Roo.log(edata);
6060                     return;
6061                 }
6062                 if (!rdata || !rdata.success) {
6063                     Roo.log(rdata);
6064                     Roo.MessageBox.alert(Roo.encode(rdata));
6065                     return;
6066                 }
6067                 var data = rdata.data;
6068                 
6069                 if (this.uploadComplete) {
6070                    Roo.MessageBox.hide();
6071                    return;
6072                 }
6073                    
6074                 if (data){
6075                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6076                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6077                     );
6078                 }
6079                 this.uploadProgress.defer(2000,this);
6080             },
6081        
6082             failure: function(data) {
6083                 Roo.log('progress url failed ');
6084                 Roo.log(data);
6085             },
6086             scope : this
6087         });
6088            
6089     },
6090     
6091     
6092     run : function()
6093     {
6094         // run get Values on the form, so it syncs any secondary forms.
6095         this.form.getValues();
6096         
6097         var o = this.options;
6098         var method = this.getMethod();
6099         var isPost = method == 'POST';
6100         if(o.clientValidation === false || this.form.isValid()){
6101             
6102             if (this.form.progressUrl) {
6103                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6104                     (new Date() * 1) + '' + Math.random());
6105                     
6106             } 
6107             
6108             
6109             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6110                 form:this.form.el.dom,
6111                 url:this.getUrl(!isPost),
6112                 method: method,
6113                 params:isPost ? this.getParams() : null,
6114                 isUpload: this.form.fileUpload
6115             }));
6116             
6117             this.uploadProgress();
6118
6119         }else if (o.clientValidation !== false){ // client validation failed
6120             this.failureType = Roo.form.Action.CLIENT_INVALID;
6121             this.form.afterAction(this, false);
6122         }
6123     },
6124
6125     success : function(response)
6126     {
6127         this.uploadComplete= true;
6128         if (this.haveProgress) {
6129             Roo.MessageBox.hide();
6130         }
6131         
6132         
6133         var result = this.processResponse(response);
6134         if(result === true || result.success){
6135             this.form.afterAction(this, true);
6136             return;
6137         }
6138         if(result.errors){
6139             this.form.markInvalid(result.errors);
6140             this.failureType = Roo.form.Action.SERVER_INVALID;
6141         }
6142         this.form.afterAction(this, false);
6143     },
6144     failure : function(response)
6145     {
6146         this.uploadComplete= true;
6147         if (this.haveProgress) {
6148             Roo.MessageBox.hide();
6149         }
6150         
6151         this.response = response;
6152         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6153         this.form.afterAction(this, false);
6154     },
6155     
6156     handleResponse : function(response){
6157         if(this.form.errorReader){
6158             var rs = this.form.errorReader.read(response);
6159             var errors = [];
6160             if(rs.records){
6161                 for(var i = 0, len = rs.records.length; i < len; i++) {
6162                     var r = rs.records[i];
6163                     errors[i] = r.data;
6164                 }
6165             }
6166             if(errors.length < 1){
6167                 errors = null;
6168             }
6169             return {
6170                 success : rs.success,
6171                 errors : errors
6172             };
6173         }
6174         var ret = false;
6175         try {
6176             ret = Roo.decode(response.responseText);
6177         } catch (e) {
6178             ret = {
6179                 success: false,
6180                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6181                 errors : []
6182             };
6183         }
6184         return ret;
6185         
6186     }
6187 });
6188
6189
6190 Roo.form.Action.Load = function(form, options){
6191     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6192     this.reader = this.form.reader;
6193 };
6194
6195 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6196     type : 'load',
6197
6198     run : function(){
6199         
6200         Roo.Ajax.request(Roo.apply(
6201                 this.createCallback(), {
6202                     method:this.getMethod(),
6203                     url:this.getUrl(false),
6204                     params:this.getParams()
6205         }));
6206     },
6207
6208     success : function(response){
6209         
6210         var result = this.processResponse(response);
6211         if(result === true || !result.success || !result.data){
6212             this.failureType = Roo.form.Action.LOAD_FAILURE;
6213             this.form.afterAction(this, false);
6214             return;
6215         }
6216         this.form.clearInvalid();
6217         this.form.setValues(result.data);
6218         this.form.afterAction(this, true);
6219     },
6220
6221     handleResponse : function(response){
6222         if(this.form.reader){
6223             var rs = this.form.reader.read(response);
6224             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6225             return {
6226                 success : rs.success,
6227                 data : data
6228             };
6229         }
6230         return Roo.decode(response.responseText);
6231     }
6232 });
6233
6234 Roo.form.Action.ACTION_TYPES = {
6235     'load' : Roo.form.Action.Load,
6236     'submit' : Roo.form.Action.Submit
6237 };/*
6238  * - LGPL
6239  *
6240  * form
6241  * 
6242  */
6243
6244 /**
6245  * @class Roo.bootstrap.Form
6246  * @extends Roo.bootstrap.Component
6247  * Bootstrap Form class
6248  * @cfg {String} method  GET | POST (default POST)
6249  * @cfg {String} labelAlign top | left (default top)
6250  * @cfg {String} align left  | right - for navbars
6251  * @cfg {Boolean} loadMask load mask when submit (default true)
6252
6253  * 
6254  * @constructor
6255  * Create a new Form
6256  * @param {Object} config The config object
6257  */
6258
6259
6260 Roo.bootstrap.Form = function(config){
6261     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6262     this.addEvents({
6263         /**
6264          * @event clientvalidation
6265          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6266          * @param {Form} this
6267          * @param {Boolean} valid true if the form has passed client-side validation
6268          */
6269         clientvalidation: true,
6270         /**
6271          * @event beforeaction
6272          * Fires before any action is performed. Return false to cancel the action.
6273          * @param {Form} this
6274          * @param {Action} action The action to be performed
6275          */
6276         beforeaction: true,
6277         /**
6278          * @event actionfailed
6279          * Fires when an action fails.
6280          * @param {Form} this
6281          * @param {Action} action The action that failed
6282          */
6283         actionfailed : true,
6284         /**
6285          * @event actioncomplete
6286          * Fires when an action is completed.
6287          * @param {Form} this
6288          * @param {Action} action The action that completed
6289          */
6290         actioncomplete : true
6291     });
6292     
6293 };
6294
6295 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6296       
6297      /**
6298      * @cfg {String} method
6299      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6300      */
6301     method : 'POST',
6302     /**
6303      * @cfg {String} url
6304      * The URL to use for form actions if one isn't supplied in the action options.
6305      */
6306     /**
6307      * @cfg {Boolean} fileUpload
6308      * Set to true if this form is a file upload.
6309      */
6310      
6311     /**
6312      * @cfg {Object} baseParams
6313      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6314      */
6315       
6316     /**
6317      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6318      */
6319     timeout: 30,
6320     /**
6321      * @cfg {Sting} align (left|right) for navbar forms
6322      */
6323     align : 'left',
6324
6325     // private
6326     activeAction : null,
6327  
6328     /**
6329      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6330      * element by passing it or its id or mask the form itself by passing in true.
6331      * @type Mixed
6332      */
6333     waitMsgTarget : false,
6334     
6335     loadMask : true,
6336     
6337     getAutoCreate : function(){
6338         
6339         var cfg = {
6340             tag: 'form',
6341             method : this.method || 'POST',
6342             id : this.id || Roo.id(),
6343             cls : ''
6344         }
6345         if (this.parent().xtype.match(/^Nav/)) {
6346             cfg.cls = 'navbar-form navbar-' + this.align;
6347             
6348         }
6349         
6350         if (this.labelAlign == 'left' ) {
6351             cfg.cls += ' form-horizontal';
6352         }
6353         
6354         
6355         return cfg;
6356     },
6357     initEvents : function()
6358     {
6359         this.el.on('submit', this.onSubmit, this);
6360         // this was added as random key presses on the form where triggering form submit.
6361         this.el.on('keypress', function(e) {
6362             if (e.getCharCode() != 13) {
6363                 return true;
6364             }
6365             // we might need to allow it for textareas.. and some other items.
6366             // check e.getTarget().
6367             
6368             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6369                 return true;
6370             }
6371         
6372             Roo.log("keypress blocked");
6373             
6374             e.preventDefault();
6375             return false;
6376         });
6377         
6378     },
6379     // private
6380     onSubmit : function(e){
6381         e.stopEvent();
6382     },
6383     
6384      /**
6385      * Returns true if client-side validation on the form is successful.
6386      * @return Boolean
6387      */
6388     isValid : function(){
6389         var items = this.getItems();
6390         var valid = true;
6391         items.each(function(f){
6392            if(!f.validate()){
6393                valid = false;
6394                
6395            }
6396         });
6397         return valid;
6398     },
6399     /**
6400      * Returns true if any fields in this form have changed since their original load.
6401      * @return Boolean
6402      */
6403     isDirty : function(){
6404         var dirty = false;
6405         var items = this.getItems();
6406         items.each(function(f){
6407            if(f.isDirty()){
6408                dirty = true;
6409                return false;
6410            }
6411            return true;
6412         });
6413         return dirty;
6414     },
6415      /**
6416      * Performs a predefined action (submit or load) or custom actions you define on this form.
6417      * @param {String} actionName The name of the action type
6418      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6419      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6420      * accept other config options):
6421      * <pre>
6422 Property          Type             Description
6423 ----------------  ---------------  ----------------------------------------------------------------------------------
6424 url               String           The url for the action (defaults to the form's url)
6425 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6426 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6427 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6428                                    validate the form on the client (defaults to false)
6429      * </pre>
6430      * @return {BasicForm} this
6431      */
6432     doAction : function(action, options){
6433         if(typeof action == 'string'){
6434             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6435         }
6436         if(this.fireEvent('beforeaction', this, action) !== false){
6437             this.beforeAction(action);
6438             action.run.defer(100, action);
6439         }
6440         return this;
6441     },
6442     
6443     // private
6444     beforeAction : function(action){
6445         var o = action.options;
6446         
6447         if(this.loadMask){
6448             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6449         }
6450         // not really supported yet.. ??
6451         
6452         //if(this.waitMsgTarget === true){
6453         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6454         //}else if(this.waitMsgTarget){
6455         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6456         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6457         //}else {
6458         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6459        // }
6460          
6461     },
6462
6463     // private
6464     afterAction : function(action, success){
6465         this.activeAction = null;
6466         var o = action.options;
6467         
6468         //if(this.waitMsgTarget === true){
6469             this.el.unmask();
6470         //}else if(this.waitMsgTarget){
6471         //    this.waitMsgTarget.unmask();
6472         //}else{
6473         //    Roo.MessageBox.updateProgress(1);
6474         //    Roo.MessageBox.hide();
6475        // }
6476         // 
6477         if(success){
6478             if(o.reset){
6479                 this.reset();
6480             }
6481             Roo.callback(o.success, o.scope, [this, action]);
6482             this.fireEvent('actioncomplete', this, action);
6483             
6484         }else{
6485             
6486             // failure condition..
6487             // we have a scenario where updates need confirming.
6488             // eg. if a locking scenario exists..
6489             // we look for { errors : { needs_confirm : true }} in the response.
6490             if (
6491                 (typeof(action.result) != 'undefined')  &&
6492                 (typeof(action.result.errors) != 'undefined')  &&
6493                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6494            ){
6495                 var _t = this;
6496                 Roo.log("not supported yet");
6497                  /*
6498                 
6499                 Roo.MessageBox.confirm(
6500                     "Change requires confirmation",
6501                     action.result.errorMsg,
6502                     function(r) {
6503                         if (r != 'yes') {
6504                             return;
6505                         }
6506                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6507                     }
6508                     
6509                 );
6510                 */
6511                 
6512                 
6513                 return;
6514             }
6515             
6516             Roo.callback(o.failure, o.scope, [this, action]);
6517             // show an error message if no failed handler is set..
6518             if (!this.hasListener('actionfailed')) {
6519                 Roo.log("need to add dialog support");
6520                 /*
6521                 Roo.MessageBox.alert("Error",
6522                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6523                         action.result.errorMsg :
6524                         "Saving Failed, please check your entries or try again"
6525                 );
6526                 */
6527             }
6528             
6529             this.fireEvent('actionfailed', this, action);
6530         }
6531         
6532     },
6533     /**
6534      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6535      * @param {String} id The value to search for
6536      * @return Field
6537      */
6538     findField : function(id){
6539         var items = this.getItems();
6540         var field = items.get(id);
6541         if(!field){
6542              items.each(function(f){
6543                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6544                     field = f;
6545                     return false;
6546                 }
6547                 return true;
6548             });
6549         }
6550         return field || null;
6551     },
6552      /**
6553      * Mark fields in this form invalid in bulk.
6554      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6555      * @return {BasicForm} this
6556      */
6557     markInvalid : function(errors){
6558         if(errors instanceof Array){
6559             for(var i = 0, len = errors.length; i < len; i++){
6560                 var fieldError = errors[i];
6561                 var f = this.findField(fieldError.id);
6562                 if(f){
6563                     f.markInvalid(fieldError.msg);
6564                 }
6565             }
6566         }else{
6567             var field, id;
6568             for(id in errors){
6569                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6570                     field.markInvalid(errors[id]);
6571                 }
6572             }
6573         }
6574         //Roo.each(this.childForms || [], function (f) {
6575         //    f.markInvalid(errors);
6576         //});
6577         
6578         return this;
6579     },
6580
6581     /**
6582      * Set values for fields in this form in bulk.
6583      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6584      * @return {BasicForm} this
6585      */
6586     setValues : function(values){
6587         if(values instanceof Array){ // array of objects
6588             for(var i = 0, len = values.length; i < len; i++){
6589                 var v = values[i];
6590                 var f = this.findField(v.id);
6591                 if(f){
6592                     f.setValue(v.value);
6593                     if(this.trackResetOnLoad){
6594                         f.originalValue = f.getValue();
6595                     }
6596                 }
6597             }
6598         }else{ // object hash
6599             var field, id;
6600             for(id in values){
6601                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6602                     
6603                     if (field.setFromData && 
6604                         field.valueField && 
6605                         field.displayField &&
6606                         // combos' with local stores can 
6607                         // be queried via setValue()
6608                         // to set their value..
6609                         (field.store && !field.store.isLocal)
6610                         ) {
6611                         // it's a combo
6612                         var sd = { };
6613                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6614                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6615                         field.setFromData(sd);
6616                         
6617                     } else {
6618                         field.setValue(values[id]);
6619                     }
6620                     
6621                     
6622                     if(this.trackResetOnLoad){
6623                         field.originalValue = field.getValue();
6624                     }
6625                 }
6626             }
6627         }
6628          
6629         //Roo.each(this.childForms || [], function (f) {
6630         //    f.setValues(values);
6631         //});
6632                 
6633         return this;
6634     },
6635
6636     /**
6637      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6638      * they are returned as an array.
6639      * @param {Boolean} asString
6640      * @return {Object}
6641      */
6642     getValues : function(asString){
6643         //if (this.childForms) {
6644             // copy values from the child forms
6645         //    Roo.each(this.childForms, function (f) {
6646         //        this.setValues(f.getValues());
6647         //    }, this);
6648         //}
6649         
6650         
6651         
6652         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6653         if(asString === true){
6654             return fs;
6655         }
6656         return Roo.urlDecode(fs);
6657     },
6658     
6659     /**
6660      * Returns the fields in this form as an object with key/value pairs. 
6661      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6662      * @return {Object}
6663      */
6664     getFieldValues : function(with_hidden)
6665     {
6666         var items = this.getItems();
6667         var ret = {};
6668         items.each(function(f){
6669             if (!f.getName()) {
6670                 return;
6671             }
6672             var v = f.getValue();
6673             if (f.inputType =='radio') {
6674                 if (typeof(ret[f.getName()]) == 'undefined') {
6675                     ret[f.getName()] = ''; // empty..
6676                 }
6677                 
6678                 if (!f.el.dom.checked) {
6679                     return;
6680                     
6681                 }
6682                 v = f.el.dom.value;
6683                 
6684             }
6685             
6686             // not sure if this supported any more..
6687             if ((typeof(v) == 'object') && f.getRawValue) {
6688                 v = f.getRawValue() ; // dates..
6689             }
6690             // combo boxes where name != hiddenName...
6691             if (f.name != f.getName()) {
6692                 ret[f.name] = f.getRawValue();
6693             }
6694             ret[f.getName()] = v;
6695         });
6696         
6697         return ret;
6698     },
6699
6700     /**
6701      * Clears all invalid messages in this form.
6702      * @return {BasicForm} this
6703      */
6704     clearInvalid : function(){
6705         var items = this.getItems();
6706         
6707         items.each(function(f){
6708            f.clearInvalid();
6709         });
6710         
6711         
6712         
6713         return this;
6714     },
6715
6716     /**
6717      * Resets this form.
6718      * @return {BasicForm} this
6719      */
6720     reset : function(){
6721         var items = this.getItems();
6722         items.each(function(f){
6723             f.reset();
6724         });
6725         
6726         Roo.each(this.childForms || [], function (f) {
6727             f.reset();
6728         });
6729        
6730         
6731         return this;
6732     },
6733     getItems : function()
6734     {
6735         var r=new Roo.util.MixedCollection(false, function(o){
6736             return o.id || (o.id = Roo.id());
6737         });
6738         var iter = function(el) {
6739             if (el.inputEl) {
6740                 r.add(el);
6741             }
6742             if (!el.items) {
6743                 return;
6744             }
6745             Roo.each(el.items,function(e) {
6746                 iter(e);
6747             });
6748             
6749             
6750         };
6751         iter(this);
6752         return r;
6753         
6754         
6755         
6756         
6757     }
6758     
6759 });
6760
6761  
6762 /*
6763  * Based on:
6764  * Ext JS Library 1.1.1
6765  * Copyright(c) 2006-2007, Ext JS, LLC.
6766  *
6767  * Originally Released Under LGPL - original licence link has changed is not relivant.
6768  *
6769  * Fork - LGPL
6770  * <script type="text/javascript">
6771  */
6772 /**
6773  * @class Roo.form.VTypes
6774  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6775  * @singleton
6776  */
6777 Roo.form.VTypes = function(){
6778     // closure these in so they are only created once.
6779     var alpha = /^[a-zA-Z_]+$/;
6780     var alphanum = /^[a-zA-Z0-9_]+$/;
6781     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6782     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6783
6784     // All these messages and functions are configurable
6785     return {
6786         /**
6787          * The function used to validate email addresses
6788          * @param {String} value The email address
6789          */
6790         'email' : function(v){
6791             return email.test(v);
6792         },
6793         /**
6794          * The error text to display when the email validation function returns false
6795          * @type String
6796          */
6797         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6798         /**
6799          * The keystroke filter mask to be applied on email input
6800          * @type RegExp
6801          */
6802         'emailMask' : /[a-z0-9_\.\-@]/i,
6803
6804         /**
6805          * The function used to validate URLs
6806          * @param {String} value The URL
6807          */
6808         'url' : function(v){
6809             return url.test(v);
6810         },
6811         /**
6812          * The error text to display when the url validation function returns false
6813          * @type String
6814          */
6815         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6816         
6817         /**
6818          * The function used to validate alpha values
6819          * @param {String} value The value
6820          */
6821         'alpha' : function(v){
6822             return alpha.test(v);
6823         },
6824         /**
6825          * The error text to display when the alpha validation function returns false
6826          * @type String
6827          */
6828         'alphaText' : 'This field should only contain letters and _',
6829         /**
6830          * The keystroke filter mask to be applied on alpha input
6831          * @type RegExp
6832          */
6833         'alphaMask' : /[a-z_]/i,
6834
6835         /**
6836          * The function used to validate alphanumeric values
6837          * @param {String} value The value
6838          */
6839         'alphanum' : function(v){
6840             return alphanum.test(v);
6841         },
6842         /**
6843          * The error text to display when the alphanumeric validation function returns false
6844          * @type String
6845          */
6846         'alphanumText' : 'This field should only contain letters, numbers and _',
6847         /**
6848          * The keystroke filter mask to be applied on alphanumeric input
6849          * @type RegExp
6850          */
6851         'alphanumMask' : /[a-z0-9_]/i
6852     };
6853 }();/*
6854  * - LGPL
6855  *
6856  * Input
6857  * 
6858  */
6859
6860 /**
6861  * @class Roo.bootstrap.Input
6862  * @extends Roo.bootstrap.Component
6863  * Bootstrap Input class
6864  * @cfg {Boolean} disabled is it disabled
6865  * @cfg {String} fieldLabel - the label associated
6866  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6867  * @cfg {String} name name of the input
6868  * @cfg {string} fieldLabel - the label associated
6869  * @cfg {string}  inputType - input / file submit ...
6870  * @cfg {string} placeholder - placeholder to put in text.
6871  * @cfg {string}  before - input group add on before
6872  * @cfg {string} after - input group add on after
6873  * @cfg {string} size - (lg|sm) or leave empty..
6874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6876  * @cfg {Number} md colspan out of 12 for computer-sized screens
6877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6878  * @cfg {string} value default value of the input
6879  * @cfg {Number} labelWidth set the width of label (0-12)
6880  * @cfg {String} labelAlign (top|left)
6881  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6882  * @cfg {String} align (left|center|right) Default left
6883  * 
6884  * 
6885  * @constructor
6886  * Create a new Input
6887  * @param {Object} config The config object
6888  */
6889
6890 Roo.bootstrap.Input = function(config){
6891     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6892    
6893         this.addEvents({
6894             /**
6895              * @event focus
6896              * Fires when this field receives input focus.
6897              * @param {Roo.form.Field} this
6898              */
6899             focus : true,
6900             /**
6901              * @event blur
6902              * Fires when this field loses input focus.
6903              * @param {Roo.form.Field} this
6904              */
6905             blur : true,
6906             /**
6907              * @event specialkey
6908              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6909              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6910              * @param {Roo.form.Field} this
6911              * @param {Roo.EventObject} e The event object
6912              */
6913             specialkey : true,
6914             /**
6915              * @event change
6916              * Fires just before the field blurs if the field value has changed.
6917              * @param {Roo.form.Field} this
6918              * @param {Mixed} newValue The new value
6919              * @param {Mixed} oldValue The original value
6920              */
6921             change : true,
6922             /**
6923              * @event invalid
6924              * Fires after the field has been marked as invalid.
6925              * @param {Roo.form.Field} this
6926              * @param {String} msg The validation message
6927              */
6928             invalid : true,
6929             /**
6930              * @event valid
6931              * Fires after the field has been validated with no errors.
6932              * @param {Roo.form.Field} this
6933              */
6934             valid : true,
6935              /**
6936              * @event keyup
6937              * Fires after the key up
6938              * @param {Roo.form.Field} this
6939              * @param {Roo.EventObject}  e The event Object
6940              */
6941             keyup : true
6942         });
6943 };
6944
6945 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6946      /**
6947      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6948       automatic validation (defaults to "keyup").
6949      */
6950     validationEvent : "keyup",
6951      /**
6952      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6953      */
6954     validateOnBlur : true,
6955     /**
6956      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6957      */
6958     validationDelay : 250,
6959      /**
6960      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6961      */
6962     focusClass : "x-form-focus",  // not needed???
6963     
6964        
6965     /**
6966      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6967      */
6968     invalidClass : "has-error",
6969     
6970     /**
6971      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6972      */
6973     selectOnFocus : false,
6974     
6975      /**
6976      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6977      */
6978     maskRe : null,
6979        /**
6980      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6981      */
6982     vtype : null,
6983     
6984       /**
6985      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6986      */
6987     disableKeyFilter : false,
6988     
6989        /**
6990      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6991      */
6992     disabled : false,
6993      /**
6994      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6995      */
6996     allowBlank : true,
6997     /**
6998      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6999      */
7000     blankText : "This field is required",
7001     
7002      /**
7003      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7004      */
7005     minLength : 0,
7006     /**
7007      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7008      */
7009     maxLength : Number.MAX_VALUE,
7010     /**
7011      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7012      */
7013     minLengthText : "The minimum length for this field is {0}",
7014     /**
7015      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7016      */
7017     maxLengthText : "The maximum length for this field is {0}",
7018   
7019     
7020     /**
7021      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7022      * If available, this function will be called only after the basic validators all return true, and will be passed the
7023      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7024      */
7025     validator : null,
7026     /**
7027      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7028      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7029      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7030      */
7031     regex : null,
7032     /**
7033      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7034      */
7035     regexText : "",
7036     
7037     
7038     
7039     fieldLabel : '',
7040     inputType : 'text',
7041     
7042     name : false,
7043     placeholder: false,
7044     before : false,
7045     after : false,
7046     size : false,
7047     // private
7048     hasFocus : false,
7049     preventMark: false,
7050     isFormField : true,
7051     value : '',
7052     labelWidth : 2,
7053     labelAlign : false,
7054     readOnly : false,
7055     align : false,
7056     formatedValue : false,
7057     
7058     parentLabelAlign : function()
7059     {
7060         var parent = this;
7061         while (parent.parent()) {
7062             parent = parent.parent();
7063             if (typeof(parent.labelAlign) !='undefined') {
7064                 return parent.labelAlign;
7065             }
7066         }
7067         return 'left';
7068         
7069     },
7070     
7071     getAutoCreate : function(){
7072         
7073         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7074         
7075         var id = Roo.id();
7076         
7077         var cfg = {};
7078         
7079         if(this.inputType != 'hidden'){
7080             cfg.cls = 'form-group' //input-group
7081         }
7082         
7083         var input =  {
7084             tag: 'input',
7085             id : id,
7086             type : this.inputType,
7087             value : this.value,
7088             cls : 'form-control',
7089             placeholder : this.placeholder || ''
7090             
7091         };
7092         
7093         if(this.align){
7094             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7095         }
7096         
7097         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7098             input.maxLength = this.maxLength;
7099         }
7100         
7101         if (this.disabled) {
7102             input.disabled=true;
7103         }
7104         
7105         if (this.readOnly) {
7106             input.readonly=true;
7107         }
7108         
7109         if (this.name) {
7110             input.name = this.name;
7111         }
7112         if (this.size) {
7113             input.cls += ' input-' + this.size;
7114         }
7115         var settings=this;
7116         ['xs','sm','md','lg'].map(function(size){
7117             if (settings[size]) {
7118                 cfg.cls += ' col-' + size + '-' + settings[size];
7119             }
7120         });
7121         
7122         var inputblock = input;
7123         
7124         if (this.before || this.after) {
7125             
7126             inputblock = {
7127                 cls : 'input-group',
7128                 cn :  [] 
7129             };
7130             if (this.before && typeof(this.before) == 'string') {
7131                 
7132                 inputblock.cn.push({
7133                     tag :'span',
7134                     cls : 'roo-input-before input-group-addon',
7135                     html : this.before
7136                 });
7137             }
7138             if (this.before && typeof(this.before) == 'object') {
7139                 this.before = Roo.factory(this.before);
7140                 Roo.log(this.before);
7141                 inputblock.cn.push({
7142                     tag :'span',
7143                     cls : 'roo-input-before input-group-' +
7144                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7145                 });
7146             }
7147             
7148             inputblock.cn.push(input);
7149             
7150             if (this.after && typeof(this.after) == 'string') {
7151                 inputblock.cn.push({
7152                     tag :'span',
7153                     cls : 'roo-input-after input-group-addon',
7154                     html : this.after
7155                 });
7156             }
7157             if (this.after && typeof(this.after) == 'object') {
7158                 this.after = Roo.factory(this.after);
7159                 Roo.log(this.after);
7160                 inputblock.cn.push({
7161                     tag :'span',
7162                     cls : 'roo-input-after input-group-' +
7163                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7164                 });
7165             }
7166         };
7167         
7168         if (align ==='left' && this.fieldLabel.length) {
7169                 Roo.log("left and has label");
7170                 cfg.cn = [
7171                     
7172                     {
7173                         tag: 'label',
7174                         'for' :  id,
7175                         cls : 'control-label col-sm-' + this.labelWidth,
7176                         html : this.fieldLabel
7177                         
7178                     },
7179                     {
7180                         cls : "col-sm-" + (12 - this.labelWidth), 
7181                         cn: [
7182                             inputblock
7183                         ]
7184                     }
7185                     
7186                 ];
7187         } else if ( this.fieldLabel.length) {
7188                 Roo.log(" label");
7189                  cfg.cn = [
7190                    
7191                     {
7192                         tag: 'label',
7193                         //cls : 'input-group-addon',
7194                         html : this.fieldLabel
7195                         
7196                     },
7197                     
7198                     inputblock
7199                     
7200                 ];
7201
7202         } else {
7203             
7204                 Roo.log(" no label && no align");
7205                 cfg.cn = [
7206                     
7207                         inputblock
7208                     
7209                 ];
7210                 
7211                 
7212         };
7213         Roo.log('input-parentType: ' + this.parentType);
7214         
7215         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7216            cfg.cls += ' navbar-form';
7217            Roo.log(cfg);
7218         }
7219         
7220         return cfg;
7221         
7222     },
7223     /**
7224      * return the real input element.
7225      */
7226     inputEl: function ()
7227     {
7228         return this.el.select('input.form-control',true).first();
7229     },
7230     
7231     tooltipEl : function()
7232     {
7233         return this.inputEl();
7234     },
7235     
7236     setDisabled : function(v)
7237     {
7238         var i  = this.inputEl().dom;
7239         if (!v) {
7240             i.removeAttribute('disabled');
7241             return;
7242             
7243         }
7244         i.setAttribute('disabled','true');
7245     },
7246     initEvents : function()
7247     {
7248           
7249         this.inputEl().on("keydown" , this.fireKey,  this);
7250         this.inputEl().on("focus", this.onFocus,  this);
7251         this.inputEl().on("blur", this.onBlur,  this);
7252         
7253         this.inputEl().relayEvent('keyup', this);
7254
7255         // reference to original value for reset
7256         this.originalValue = this.getValue();
7257         //Roo.form.TextField.superclass.initEvents.call(this);
7258         if(this.validationEvent == 'keyup'){
7259             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7260             this.inputEl().on('keyup', this.filterValidation, this);
7261         }
7262         else if(this.validationEvent !== false){
7263             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7264         }
7265         
7266         if(this.selectOnFocus){
7267             this.on("focus", this.preFocus, this);
7268             
7269         }
7270         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7271             this.inputEl().on("keypress", this.filterKeys, this);
7272         }
7273        /* if(this.grow){
7274             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7275             this.el.on("click", this.autoSize,  this);
7276         }
7277         */
7278         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7279             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7280         }
7281         
7282         if (typeof(this.before) == 'object') {
7283             this.before.render(this.el.select('.roo-input-before',true).first());
7284         }
7285         if (typeof(this.after) == 'object') {
7286             this.after.render(this.el.select('.roo-input-after',true).first());
7287         }
7288         
7289         
7290     },
7291     filterValidation : function(e){
7292         if(!e.isNavKeyPress()){
7293             this.validationTask.delay(this.validationDelay);
7294         }
7295     },
7296      /**
7297      * Validates the field value
7298      * @return {Boolean} True if the value is valid, else false
7299      */
7300     validate : function(){
7301         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7302         if(this.disabled || this.validateValue(this.getRawValue())){
7303             this.clearInvalid();
7304             return true;
7305         }
7306         return false;
7307     },
7308     
7309     
7310     /**
7311      * Validates a value according to the field's validation rules and marks the field as invalid
7312      * if the validation fails
7313      * @param {Mixed} value The value to validate
7314      * @return {Boolean} True if the value is valid, else false
7315      */
7316     validateValue : function(value){
7317         if(value.length < 1)  { // if it's blank
7318              if(this.allowBlank){
7319                 this.clearInvalid();
7320                 return true;
7321              }else{
7322                 this.markInvalid(this.blankText);
7323                 return false;
7324              }
7325         }
7326         if(value.length < this.minLength){
7327             this.markInvalid(String.format(this.minLengthText, this.minLength));
7328             return false;
7329         }
7330         if(value.length > this.maxLength){
7331             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7332             return false;
7333         }
7334         if(this.vtype){
7335             var vt = Roo.form.VTypes;
7336             if(!vt[this.vtype](value, this)){
7337                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7338                 return false;
7339             }
7340         }
7341         if(typeof this.validator == "function"){
7342             var msg = this.validator(value);
7343             if(msg !== true){
7344                 this.markInvalid(msg);
7345                 return false;
7346             }
7347         }
7348         if(this.regex && !this.regex.test(value)){
7349             this.markInvalid(this.regexText);
7350             return false;
7351         }
7352         return true;
7353     },
7354
7355     
7356     
7357      // private
7358     fireKey : function(e){
7359         //Roo.log('field ' + e.getKey());
7360         if(e.isNavKeyPress()){
7361             this.fireEvent("specialkey", this, e);
7362         }
7363     },
7364     focus : function (selectText){
7365         if(this.rendered){
7366             this.inputEl().focus();
7367             if(selectText === true){
7368                 this.inputEl().dom.select();
7369             }
7370         }
7371         return this;
7372     } ,
7373     
7374     onFocus : function(){
7375         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7376            // this.el.addClass(this.focusClass);
7377         }
7378         if(!this.hasFocus){
7379             this.hasFocus = true;
7380             this.startValue = this.getValue();
7381             this.fireEvent("focus", this);
7382         }
7383     },
7384     
7385     beforeBlur : Roo.emptyFn,
7386
7387     
7388     // private
7389     onBlur : function(){
7390         this.beforeBlur();
7391         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7392             //this.el.removeClass(this.focusClass);
7393         }
7394         this.hasFocus = false;
7395         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7396             this.validate();
7397         }
7398         var v = this.getValue();
7399         if(String(v) !== String(this.startValue)){
7400             this.fireEvent('change', this, v, this.startValue);
7401         }
7402         this.fireEvent("blur", this);
7403     },
7404     
7405     /**
7406      * Resets the current field value to the originally loaded value and clears any validation messages
7407      */
7408     reset : function(){
7409         this.setValue(this.originalValue);
7410         this.clearInvalid();
7411     },
7412      /**
7413      * Returns the name of the field
7414      * @return {Mixed} name The name field
7415      */
7416     getName: function(){
7417         return this.name;
7418     },
7419      /**
7420      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7421      * @return {Mixed} value The field value
7422      */
7423     getValue : function(){
7424         
7425         var v = this.inputEl().getValue();
7426         
7427         return v;
7428     },
7429     /**
7430      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7431      * @return {Mixed} value The field value
7432      */
7433     getRawValue : function(){
7434         var v = this.inputEl().getValue();
7435         
7436         return v;
7437     },
7438     
7439     /**
7440      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7441      * @param {Mixed} value The value to set
7442      */
7443     setRawValue : function(v){
7444         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7445     },
7446     
7447     selectText : function(start, end){
7448         var v = this.getRawValue();
7449         if(v.length > 0){
7450             start = start === undefined ? 0 : start;
7451             end = end === undefined ? v.length : end;
7452             var d = this.inputEl().dom;
7453             if(d.setSelectionRange){
7454                 d.setSelectionRange(start, end);
7455             }else if(d.createTextRange){
7456                 var range = d.createTextRange();
7457                 range.moveStart("character", start);
7458                 range.moveEnd("character", v.length-end);
7459                 range.select();
7460             }
7461         }
7462     },
7463     
7464     /**
7465      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7466      * @param {Mixed} value The value to set
7467      */
7468     setValue : function(v){
7469         this.value = v;
7470         if(this.rendered){
7471             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7472             this.validate();
7473         }
7474     },
7475     
7476     /*
7477     processValue : function(value){
7478         if(this.stripCharsRe){
7479             var newValue = value.replace(this.stripCharsRe, '');
7480             if(newValue !== value){
7481                 this.setRawValue(newValue);
7482                 return newValue;
7483             }
7484         }
7485         return value;
7486     },
7487   */
7488     preFocus : function(){
7489         
7490         if(this.selectOnFocus){
7491             this.inputEl().dom.select();
7492         }
7493     },
7494     filterKeys : function(e){
7495         var k = e.getKey();
7496         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7497             return;
7498         }
7499         var c = e.getCharCode(), cc = String.fromCharCode(c);
7500         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7501             return;
7502         }
7503         if(!this.maskRe.test(cc)){
7504             e.stopEvent();
7505         }
7506     },
7507      /**
7508      * Clear any invalid styles/messages for this field
7509      */
7510     clearInvalid : function(){
7511         
7512         if(!this.el || this.preventMark){ // not rendered
7513             return;
7514         }
7515         this.el.removeClass(this.invalidClass);
7516         /*
7517         switch(this.msgTarget){
7518             case 'qtip':
7519                 this.el.dom.qtip = '';
7520                 break;
7521             case 'title':
7522                 this.el.dom.title = '';
7523                 break;
7524             case 'under':
7525                 if(this.errorEl){
7526                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7527                 }
7528                 break;
7529             case 'side':
7530                 if(this.errorIcon){
7531                     this.errorIcon.dom.qtip = '';
7532                     this.errorIcon.hide();
7533                     this.un('resize', this.alignErrorIcon, this);
7534                 }
7535                 break;
7536             default:
7537                 var t = Roo.getDom(this.msgTarget);
7538                 t.innerHTML = '';
7539                 t.style.display = 'none';
7540                 break;
7541         }
7542         */
7543         this.fireEvent('valid', this);
7544     },
7545      /**
7546      * Mark this field as invalid
7547      * @param {String} msg The validation message
7548      */
7549     markInvalid : function(msg){
7550         if(!this.el  || this.preventMark){ // not rendered
7551             return;
7552         }
7553         this.el.addClass(this.invalidClass);
7554         /*
7555         msg = msg || this.invalidText;
7556         switch(this.msgTarget){
7557             case 'qtip':
7558                 this.el.dom.qtip = msg;
7559                 this.el.dom.qclass = 'x-form-invalid-tip';
7560                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7561                     Roo.QuickTips.enable();
7562                 }
7563                 break;
7564             case 'title':
7565                 this.el.dom.title = msg;
7566                 break;
7567             case 'under':
7568                 if(!this.errorEl){
7569                     var elp = this.el.findParent('.x-form-element', 5, true);
7570                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7571                     this.errorEl.setWidth(elp.getWidth(true)-20);
7572                 }
7573                 this.errorEl.update(msg);
7574                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7575                 break;
7576             case 'side':
7577                 if(!this.errorIcon){
7578                     var elp = this.el.findParent('.x-form-element', 5, true);
7579                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7580                 }
7581                 this.alignErrorIcon();
7582                 this.errorIcon.dom.qtip = msg;
7583                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7584                 this.errorIcon.show();
7585                 this.on('resize', this.alignErrorIcon, this);
7586                 break;
7587             default:
7588                 var t = Roo.getDom(this.msgTarget);
7589                 t.innerHTML = msg;
7590                 t.style.display = this.msgDisplay;
7591                 break;
7592         }
7593         */
7594         this.fireEvent('invalid', this, msg);
7595     },
7596     // private
7597     SafariOnKeyDown : function(event)
7598     {
7599         // this is a workaround for a password hang bug on chrome/ webkit.
7600         
7601         var isSelectAll = false;
7602         
7603         if(this.inputEl().dom.selectionEnd > 0){
7604             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7605         }
7606         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7607             event.preventDefault();
7608             this.setValue('');
7609             return;
7610         }
7611         
7612         if(isSelectAll){ // backspace and delete key
7613             
7614             event.preventDefault();
7615             // this is very hacky as keydown always get's upper case.
7616             //
7617             var cc = String.fromCharCode(event.getCharCode());
7618             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7619             
7620         }
7621     },
7622     adjustWidth : function(tag, w){
7623         tag = tag.toLowerCase();
7624         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7625             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7626                 if(tag == 'input'){
7627                     return w + 2;
7628                 }
7629                 if(tag == 'textarea'){
7630                     return w-2;
7631                 }
7632             }else if(Roo.isOpera){
7633                 if(tag == 'input'){
7634                     return w + 2;
7635                 }
7636                 if(tag == 'textarea'){
7637                     return w-2;
7638                 }
7639             }
7640         }
7641         return w;
7642     }
7643     
7644 });
7645
7646  
7647 /*
7648  * - LGPL
7649  *
7650  * Input
7651  * 
7652  */
7653
7654 /**
7655  * @class Roo.bootstrap.TextArea
7656  * @extends Roo.bootstrap.Input
7657  * Bootstrap TextArea class
7658  * @cfg {Number} cols Specifies the visible width of a text area
7659  * @cfg {Number} rows Specifies the visible number of lines in a text area
7660  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7661  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7662  * @cfg {string} html text
7663  * 
7664  * @constructor
7665  * Create a new TextArea
7666  * @param {Object} config The config object
7667  */
7668
7669 Roo.bootstrap.TextArea = function(config){
7670     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7671    
7672 };
7673
7674 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7675      
7676     cols : false,
7677     rows : 5,
7678     readOnly : false,
7679     warp : 'soft',
7680     resize : false,
7681     value: false,
7682     html: false,
7683     
7684     getAutoCreate : function(){
7685         
7686         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7687         
7688         var id = Roo.id();
7689         
7690         var cfg = {};
7691         
7692         var input =  {
7693             tag: 'textarea',
7694             id : id,
7695             warp : this.warp,
7696             rows : this.rows,
7697             value : this.value || '',
7698             html: this.html || '',
7699             cls : 'form-control',
7700             placeholder : this.placeholder || '' 
7701             
7702         };
7703         
7704         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7705             input.maxLength = this.maxLength;
7706         }
7707         
7708         if(this.resize){
7709             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7710         }
7711         
7712         if(this.cols){
7713             input.cols = this.cols;
7714         }
7715         
7716         if (this.readOnly) {
7717             input.readonly = true;
7718         }
7719         
7720         if (this.name) {
7721             input.name = this.name;
7722         }
7723         
7724         if (this.size) {
7725             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7726         }
7727         
7728         var settings=this;
7729         ['xs','sm','md','lg'].map(function(size){
7730             if (settings[size]) {
7731                 cfg.cls += ' col-' + size + '-' + settings[size];
7732             }
7733         });
7734         
7735         var inputblock = input;
7736         
7737         if (this.before || this.after) {
7738             
7739             inputblock = {
7740                 cls : 'input-group',
7741                 cn :  [] 
7742             };
7743             if (this.before) {
7744                 inputblock.cn.push({
7745                     tag :'span',
7746                     cls : 'input-group-addon',
7747                     html : this.before
7748                 });
7749             }
7750             inputblock.cn.push(input);
7751             if (this.after) {
7752                 inputblock.cn.push({
7753                     tag :'span',
7754                     cls : 'input-group-addon',
7755                     html : this.after
7756                 });
7757             }
7758             
7759         }
7760         
7761         if (align ==='left' && this.fieldLabel.length) {
7762                 Roo.log("left and has label");
7763                 cfg.cn = [
7764                     
7765                     {
7766                         tag: 'label',
7767                         'for' :  id,
7768                         cls : 'control-label col-sm-' + this.labelWidth,
7769                         html : this.fieldLabel
7770                         
7771                     },
7772                     {
7773                         cls : "col-sm-" + (12 - this.labelWidth), 
7774                         cn: [
7775                             inputblock
7776                         ]
7777                     }
7778                     
7779                 ];
7780         } else if ( this.fieldLabel.length) {
7781                 Roo.log(" label");
7782                  cfg.cn = [
7783                    
7784                     {
7785                         tag: 'label',
7786                         //cls : 'input-group-addon',
7787                         html : this.fieldLabel
7788                         
7789                     },
7790                     
7791                     inputblock
7792                     
7793                 ];
7794
7795         } else {
7796             
7797                    Roo.log(" no label && no align");
7798                 cfg.cn = [
7799                     
7800                         inputblock
7801                     
7802                 ];
7803                 
7804                 
7805         }
7806         
7807         if (this.disabled) {
7808             input.disabled=true;
7809         }
7810         
7811         return cfg;
7812         
7813     },
7814     /**
7815      * return the real textarea element.
7816      */
7817     inputEl: function ()
7818     {
7819         return this.el.select('textarea.form-control',true).first();
7820     }
7821 });
7822
7823  
7824 /*
7825  * - LGPL
7826  *
7827  * trigger field - base class for combo..
7828  * 
7829  */
7830  
7831 /**
7832  * @class Roo.bootstrap.TriggerField
7833  * @extends Roo.bootstrap.Input
7834  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7835  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7836  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7837  * for which you can provide a custom implementation.  For example:
7838  * <pre><code>
7839 var trigger = new Roo.bootstrap.TriggerField();
7840 trigger.onTriggerClick = myTriggerFn;
7841 trigger.applyTo('my-field');
7842 </code></pre>
7843  *
7844  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7845  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7846  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7847  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7848  * @constructor
7849  * Create a new TriggerField.
7850  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7851  * to the base TextField)
7852  */
7853 Roo.bootstrap.TriggerField = function(config){
7854     this.mimicing = false;
7855     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7856 };
7857
7858 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7859     /**
7860      * @cfg {String} triggerClass A CSS class to apply to the trigger
7861      */
7862      /**
7863      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7864      */
7865     hideTrigger:false,
7866
7867     /** @cfg {Boolean} grow @hide */
7868     /** @cfg {Number} growMin @hide */
7869     /** @cfg {Number} growMax @hide */
7870
7871     /**
7872      * @hide 
7873      * @method
7874      */
7875     autoSize: Roo.emptyFn,
7876     // private
7877     monitorTab : true,
7878     // private
7879     deferHeight : true,
7880
7881     
7882     actionMode : 'wrap',
7883     
7884     
7885     
7886     getAutoCreate : function(){
7887        
7888         var align = this.labelAlign || this.parentLabelAlign();
7889         
7890         var id = Roo.id();
7891         
7892         var cfg = {
7893             cls: 'form-group' //input-group
7894         };
7895         
7896         
7897         var input =  {
7898             tag: 'input',
7899             id : id,
7900             type : this.inputType,
7901             cls : 'form-control',
7902             autocomplete: 'off',
7903             placeholder : this.placeholder || '' 
7904             
7905         };
7906         if (this.name) {
7907             input.name = this.name;
7908         }
7909         if (this.size) {
7910             input.cls += ' input-' + this.size;
7911         }
7912         
7913         if (this.disabled) {
7914             input.disabled=true;
7915         }
7916         
7917         var inputblock = input;
7918         
7919         if (this.before || this.after) {
7920             
7921             inputblock = {
7922                 cls : 'input-group',
7923                 cn :  [] 
7924             };
7925             if (this.before) {
7926                 inputblock.cn.push({
7927                     tag :'span',
7928                     cls : 'input-group-addon',
7929                     html : this.before
7930                 });
7931             }
7932             inputblock.cn.push(input);
7933             if (this.after) {
7934                 inputblock.cn.push({
7935                     tag :'span',
7936                     cls : 'input-group-addon',
7937                     html : this.after
7938                 });
7939             }
7940             
7941         };
7942         
7943         var box = {
7944             tag: 'div',
7945             cn: [
7946                 {
7947                     tag: 'input',
7948                     type : 'hidden',
7949                     cls: 'form-hidden-field'
7950                 },
7951                 inputblock
7952             ]
7953             
7954         };
7955         
7956         if(this.multiple){
7957             Roo.log('multiple');
7958             
7959             box = {
7960                 tag: 'div',
7961                 cn: [
7962                     {
7963                         tag: 'input',
7964                         type : 'hidden',
7965                         cls: 'form-hidden-field'
7966                     },
7967                     {
7968                         tag: 'ul',
7969                         cls: 'select2-choices',
7970                         cn:[
7971                             {
7972                                 tag: 'li',
7973                                 cls: 'select2-search-field',
7974                                 cn: [
7975
7976                                     inputblock
7977                                 ]
7978                             }
7979                         ]
7980                     }
7981                 ]
7982             }
7983         };
7984         
7985         var combobox = {
7986             cls: 'select2-container input-group',
7987             cn: [
7988                 box
7989 //                {
7990 //                    tag: 'ul',
7991 //                    cls: 'typeahead typeahead-long dropdown-menu',
7992 //                    style: 'display:none'
7993 //                }
7994             ]
7995         };
7996         
7997         if(!this.multiple && this.showToggleBtn){
7998             combobox.cn.push({
7999                 tag :'span',
8000                 cls : 'input-group-addon btn dropdown-toggle',
8001                 cn : [
8002                     {
8003                         tag: 'span',
8004                         cls: 'caret'
8005                     },
8006                     {
8007                         tag: 'span',
8008                         cls: 'combobox-clear',
8009                         cn  : [
8010                             {
8011                                 tag : 'i',
8012                                 cls: 'icon-remove'
8013                             }
8014                         ]
8015                     }
8016                 ]
8017
8018             })
8019         }
8020         
8021         if(this.multiple){
8022             combobox.cls += ' select2-container-multi';
8023         }
8024         
8025         if (align ==='left' && this.fieldLabel.length) {
8026             
8027                 Roo.log("left and has label");
8028                 cfg.cn = [
8029                     
8030                     {
8031                         tag: 'label',
8032                         'for' :  id,
8033                         cls : 'control-label col-sm-' + this.labelWidth,
8034                         html : this.fieldLabel
8035                         
8036                     },
8037                     {
8038                         cls : "col-sm-" + (12 - this.labelWidth), 
8039                         cn: [
8040                             combobox
8041                         ]
8042                     }
8043                     
8044                 ];
8045         } else if ( this.fieldLabel.length) {
8046                 Roo.log(" label");
8047                  cfg.cn = [
8048                    
8049                     {
8050                         tag: 'label',
8051                         //cls : 'input-group-addon',
8052                         html : this.fieldLabel
8053                         
8054                     },
8055                     
8056                     combobox
8057                     
8058                 ];
8059
8060         } else {
8061             
8062                 Roo.log(" no label && no align");
8063                 cfg = combobox
8064                      
8065                 
8066         }
8067          
8068         var settings=this;
8069         ['xs','sm','md','lg'].map(function(size){
8070             if (settings[size]) {
8071                 cfg.cls += ' col-' + size + '-' + settings[size];
8072             }
8073         });
8074         
8075         return cfg;
8076         
8077     },
8078     
8079     
8080     
8081     // private
8082     onResize : function(w, h){
8083 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8084 //        if(typeof w == 'number'){
8085 //            var x = w - this.trigger.getWidth();
8086 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8087 //            this.trigger.setStyle('left', x+'px');
8088 //        }
8089     },
8090
8091     // private
8092     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8093
8094     // private
8095     getResizeEl : function(){
8096         return this.inputEl();
8097     },
8098
8099     // private
8100     getPositionEl : function(){
8101         return this.inputEl();
8102     },
8103
8104     // private
8105     alignErrorIcon : function(){
8106         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8107     },
8108
8109     // private
8110     initEvents : function(){
8111         
8112         this.createList();
8113         
8114         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8115         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8116         if(!this.multiple && this.showToggleBtn){
8117             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8118             if(this.hideTrigger){
8119                 this.trigger.setDisplayed(false);
8120             }
8121             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8122         }
8123         
8124         if(this.multiple){
8125             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8126         }
8127         
8128         //this.trigger.addClassOnOver('x-form-trigger-over');
8129         //this.trigger.addClassOnClick('x-form-trigger-click');
8130         
8131         //if(!this.width){
8132         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8133         //}
8134     },
8135     
8136     createList : function()
8137     {
8138         this.list = Roo.get(document.body).createChild({
8139             tag: 'ul',
8140             cls: 'typeahead typeahead-long dropdown-menu',
8141             style: 'display:none'
8142         });
8143         
8144         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8145         
8146     },
8147
8148     // private
8149     initTrigger : function(){
8150        
8151     },
8152
8153     // private
8154     onDestroy : function(){
8155         if(this.trigger){
8156             this.trigger.removeAllListeners();
8157           //  this.trigger.remove();
8158         }
8159         //if(this.wrap){
8160         //    this.wrap.remove();
8161         //}
8162         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8163     },
8164
8165     // private
8166     onFocus : function(){
8167         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8168         /*
8169         if(!this.mimicing){
8170             this.wrap.addClass('x-trigger-wrap-focus');
8171             this.mimicing = true;
8172             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8173             if(this.monitorTab){
8174                 this.el.on("keydown", this.checkTab, this);
8175             }
8176         }
8177         */
8178     },
8179
8180     // private
8181     checkTab : function(e){
8182         if(e.getKey() == e.TAB){
8183             this.triggerBlur();
8184         }
8185     },
8186
8187     // private
8188     onBlur : function(){
8189         // do nothing
8190     },
8191
8192     // private
8193     mimicBlur : function(e, t){
8194         /*
8195         if(!this.wrap.contains(t) && this.validateBlur()){
8196             this.triggerBlur();
8197         }
8198         */
8199     },
8200
8201     // private
8202     triggerBlur : function(){
8203         this.mimicing = false;
8204         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8205         if(this.monitorTab){
8206             this.el.un("keydown", this.checkTab, this);
8207         }
8208         //this.wrap.removeClass('x-trigger-wrap-focus');
8209         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8210     },
8211
8212     // private
8213     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8214     validateBlur : function(e, t){
8215         return true;
8216     },
8217
8218     // private
8219     onDisable : function(){
8220         this.inputEl().dom.disabled = true;
8221         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8222         //if(this.wrap){
8223         //    this.wrap.addClass('x-item-disabled');
8224         //}
8225     },
8226
8227     // private
8228     onEnable : function(){
8229         this.inputEl().dom.disabled = false;
8230         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8231         //if(this.wrap){
8232         //    this.el.removeClass('x-item-disabled');
8233         //}
8234     },
8235
8236     // private
8237     onShow : function(){
8238         var ae = this.getActionEl();
8239         
8240         if(ae){
8241             ae.dom.style.display = '';
8242             ae.dom.style.visibility = 'visible';
8243         }
8244     },
8245
8246     // private
8247     
8248     onHide : function(){
8249         var ae = this.getActionEl();
8250         ae.dom.style.display = 'none';
8251     },
8252
8253     /**
8254      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8255      * by an implementing function.
8256      * @method
8257      * @param {EventObject} e
8258      */
8259     onTriggerClick : Roo.emptyFn
8260 });
8261  /*
8262  * Based on:
8263  * Ext JS Library 1.1.1
8264  * Copyright(c) 2006-2007, Ext JS, LLC.
8265  *
8266  * Originally Released Under LGPL - original licence link has changed is not relivant.
8267  *
8268  * Fork - LGPL
8269  * <script type="text/javascript">
8270  */
8271
8272
8273 /**
8274  * @class Roo.data.SortTypes
8275  * @singleton
8276  * Defines the default sorting (casting?) comparison functions used when sorting data.
8277  */
8278 Roo.data.SortTypes = {
8279     /**
8280      * Default sort that does nothing
8281      * @param {Mixed} s The value being converted
8282      * @return {Mixed} The comparison value
8283      */
8284     none : function(s){
8285         return s;
8286     },
8287     
8288     /**
8289      * The regular expression used to strip tags
8290      * @type {RegExp}
8291      * @property
8292      */
8293     stripTagsRE : /<\/?[^>]+>/gi,
8294     
8295     /**
8296      * Strips all HTML tags to sort on text only
8297      * @param {Mixed} s The value being converted
8298      * @return {String} The comparison value
8299      */
8300     asText : function(s){
8301         return String(s).replace(this.stripTagsRE, "");
8302     },
8303     
8304     /**
8305      * Strips all HTML tags to sort on text only - Case insensitive
8306      * @param {Mixed} s The value being converted
8307      * @return {String} The comparison value
8308      */
8309     asUCText : function(s){
8310         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8311     },
8312     
8313     /**
8314      * Case insensitive string
8315      * @param {Mixed} s The value being converted
8316      * @return {String} The comparison value
8317      */
8318     asUCString : function(s) {
8319         return String(s).toUpperCase();
8320     },
8321     
8322     /**
8323      * Date sorting
8324      * @param {Mixed} s The value being converted
8325      * @return {Number} The comparison value
8326      */
8327     asDate : function(s) {
8328         if(!s){
8329             return 0;
8330         }
8331         if(s instanceof Date){
8332             return s.getTime();
8333         }
8334         return Date.parse(String(s));
8335     },
8336     
8337     /**
8338      * Float sorting
8339      * @param {Mixed} s The value being converted
8340      * @return {Float} The comparison value
8341      */
8342     asFloat : function(s) {
8343         var val = parseFloat(String(s).replace(/,/g, ""));
8344         if(isNaN(val)) val = 0;
8345         return val;
8346     },
8347     
8348     /**
8349      * Integer sorting
8350      * @param {Mixed} s The value being converted
8351      * @return {Number} The comparison value
8352      */
8353     asInt : function(s) {
8354         var val = parseInt(String(s).replace(/,/g, ""));
8355         if(isNaN(val)) val = 0;
8356         return val;
8357     }
8358 };/*
8359  * Based on:
8360  * Ext JS Library 1.1.1
8361  * Copyright(c) 2006-2007, Ext JS, LLC.
8362  *
8363  * Originally Released Under LGPL - original licence link has changed is not relivant.
8364  *
8365  * Fork - LGPL
8366  * <script type="text/javascript">
8367  */
8368
8369 /**
8370 * @class Roo.data.Record
8371  * Instances of this class encapsulate both record <em>definition</em> information, and record
8372  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8373  * to access Records cached in an {@link Roo.data.Store} object.<br>
8374  * <p>
8375  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8376  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8377  * objects.<br>
8378  * <p>
8379  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8380  * @constructor
8381  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8382  * {@link #create}. The parameters are the same.
8383  * @param {Array} data An associative Array of data values keyed by the field name.
8384  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8385  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8386  * not specified an integer id is generated.
8387  */
8388 Roo.data.Record = function(data, id){
8389     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8390     this.data = data;
8391 };
8392
8393 /**
8394  * Generate a constructor for a specific record layout.
8395  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8396  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8397  * Each field definition object may contain the following properties: <ul>
8398  * <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,
8399  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8400  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8401  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8402  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8403  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8404  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8405  * this may be omitted.</p></li>
8406  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8407  * <ul><li>auto (Default, implies no conversion)</li>
8408  * <li>string</li>
8409  * <li>int</li>
8410  * <li>float</li>
8411  * <li>boolean</li>
8412  * <li>date</li></ul></p></li>
8413  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8414  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8415  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8416  * by the Reader into an object that will be stored in the Record. It is passed the
8417  * following parameters:<ul>
8418  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8419  * </ul></p></li>
8420  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8421  * </ul>
8422  * <br>usage:<br><pre><code>
8423 var TopicRecord = Roo.data.Record.create(
8424     {name: 'title', mapping: 'topic_title'},
8425     {name: 'author', mapping: 'username'},
8426     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8427     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8428     {name: 'lastPoster', mapping: 'user2'},
8429     {name: 'excerpt', mapping: 'post_text'}
8430 );
8431
8432 var myNewRecord = new TopicRecord({
8433     title: 'Do my job please',
8434     author: 'noobie',
8435     totalPosts: 1,
8436     lastPost: new Date(),
8437     lastPoster: 'Animal',
8438     excerpt: 'No way dude!'
8439 });
8440 myStore.add(myNewRecord);
8441 </code></pre>
8442  * @method create
8443  * @static
8444  */
8445 Roo.data.Record.create = function(o){
8446     var f = function(){
8447         f.superclass.constructor.apply(this, arguments);
8448     };
8449     Roo.extend(f, Roo.data.Record);
8450     var p = f.prototype;
8451     p.fields = new Roo.util.MixedCollection(false, function(field){
8452         return field.name;
8453     });
8454     for(var i = 0, len = o.length; i < len; i++){
8455         p.fields.add(new Roo.data.Field(o[i]));
8456     }
8457     f.getField = function(name){
8458         return p.fields.get(name);  
8459     };
8460     return f;
8461 };
8462
8463 Roo.data.Record.AUTO_ID = 1000;
8464 Roo.data.Record.EDIT = 'edit';
8465 Roo.data.Record.REJECT = 'reject';
8466 Roo.data.Record.COMMIT = 'commit';
8467
8468 Roo.data.Record.prototype = {
8469     /**
8470      * Readonly flag - true if this record has been modified.
8471      * @type Boolean
8472      */
8473     dirty : false,
8474     editing : false,
8475     error: null,
8476     modified: null,
8477
8478     // private
8479     join : function(store){
8480         this.store = store;
8481     },
8482
8483     /**
8484      * Set the named field to the specified value.
8485      * @param {String} name The name of the field to set.
8486      * @param {Object} value The value to set the field to.
8487      */
8488     set : function(name, value){
8489         if(this.data[name] == value){
8490             return;
8491         }
8492         this.dirty = true;
8493         if(!this.modified){
8494             this.modified = {};
8495         }
8496         if(typeof this.modified[name] == 'undefined'){
8497             this.modified[name] = this.data[name];
8498         }
8499         this.data[name] = value;
8500         if(!this.editing && this.store){
8501             this.store.afterEdit(this);
8502         }       
8503     },
8504
8505     /**
8506      * Get the value of the named field.
8507      * @param {String} name The name of the field to get the value of.
8508      * @return {Object} The value of the field.
8509      */
8510     get : function(name){
8511         return this.data[name]; 
8512     },
8513
8514     // private
8515     beginEdit : function(){
8516         this.editing = true;
8517         this.modified = {}; 
8518     },
8519
8520     // private
8521     cancelEdit : function(){
8522         this.editing = false;
8523         delete this.modified;
8524     },
8525
8526     // private
8527     endEdit : function(){
8528         this.editing = false;
8529         if(this.dirty && this.store){
8530             this.store.afterEdit(this);
8531         }
8532     },
8533
8534     /**
8535      * Usually called by the {@link Roo.data.Store} which owns the Record.
8536      * Rejects all changes made to the Record since either creation, or the last commit operation.
8537      * Modified fields are reverted to their original values.
8538      * <p>
8539      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8540      * of reject operations.
8541      */
8542     reject : function(){
8543         var m = this.modified;
8544         for(var n in m){
8545             if(typeof m[n] != "function"){
8546                 this.data[n] = m[n];
8547             }
8548         }
8549         this.dirty = false;
8550         delete this.modified;
8551         this.editing = false;
8552         if(this.store){
8553             this.store.afterReject(this);
8554         }
8555     },
8556
8557     /**
8558      * Usually called by the {@link Roo.data.Store} which owns the Record.
8559      * Commits all changes made to the Record since either creation, or the last commit operation.
8560      * <p>
8561      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8562      * of commit operations.
8563      */
8564     commit : function(){
8565         this.dirty = false;
8566         delete this.modified;
8567         this.editing = false;
8568         if(this.store){
8569             this.store.afterCommit(this);
8570         }
8571     },
8572
8573     // private
8574     hasError : function(){
8575         return this.error != null;
8576     },
8577
8578     // private
8579     clearError : function(){
8580         this.error = null;
8581     },
8582
8583     /**
8584      * Creates a copy of this record.
8585      * @param {String} id (optional) A new record id if you don't want to use this record's id
8586      * @return {Record}
8587      */
8588     copy : function(newId) {
8589         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8590     }
8591 };/*
8592  * Based on:
8593  * Ext JS Library 1.1.1
8594  * Copyright(c) 2006-2007, Ext JS, LLC.
8595  *
8596  * Originally Released Under LGPL - original licence link has changed is not relivant.
8597  *
8598  * Fork - LGPL
8599  * <script type="text/javascript">
8600  */
8601
8602
8603
8604 /**
8605  * @class Roo.data.Store
8606  * @extends Roo.util.Observable
8607  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8608  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8609  * <p>
8610  * 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
8611  * has no knowledge of the format of the data returned by the Proxy.<br>
8612  * <p>
8613  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8614  * instances from the data object. These records are cached and made available through accessor functions.
8615  * @constructor
8616  * Creates a new Store.
8617  * @param {Object} config A config object containing the objects needed for the Store to access data,
8618  * and read the data into Records.
8619  */
8620 Roo.data.Store = function(config){
8621     this.data = new Roo.util.MixedCollection(false);
8622     this.data.getKey = function(o){
8623         return o.id;
8624     };
8625     this.baseParams = {};
8626     // private
8627     this.paramNames = {
8628         "start" : "start",
8629         "limit" : "limit",
8630         "sort" : "sort",
8631         "dir" : "dir",
8632         "multisort" : "_multisort"
8633     };
8634
8635     if(config && config.data){
8636         this.inlineData = config.data;
8637         delete config.data;
8638     }
8639
8640     Roo.apply(this, config);
8641     
8642     if(this.reader){ // reader passed
8643         this.reader = Roo.factory(this.reader, Roo.data);
8644         this.reader.xmodule = this.xmodule || false;
8645         if(!this.recordType){
8646             this.recordType = this.reader.recordType;
8647         }
8648         if(this.reader.onMetaChange){
8649             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8650         }
8651     }
8652
8653     if(this.recordType){
8654         this.fields = this.recordType.prototype.fields;
8655     }
8656     this.modified = [];
8657
8658     this.addEvents({
8659         /**
8660          * @event datachanged
8661          * Fires when the data cache has changed, and a widget which is using this Store
8662          * as a Record cache should refresh its view.
8663          * @param {Store} this
8664          */
8665         datachanged : true,
8666         /**
8667          * @event metachange
8668          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8669          * @param {Store} this
8670          * @param {Object} meta The JSON metadata
8671          */
8672         metachange : true,
8673         /**
8674          * @event add
8675          * Fires when Records have been added to the Store
8676          * @param {Store} this
8677          * @param {Roo.data.Record[]} records The array of Records added
8678          * @param {Number} index The index at which the record(s) were added
8679          */
8680         add : true,
8681         /**
8682          * @event remove
8683          * Fires when a Record has been removed from the Store
8684          * @param {Store} this
8685          * @param {Roo.data.Record} record The Record that was removed
8686          * @param {Number} index The index at which the record was removed
8687          */
8688         remove : true,
8689         /**
8690          * @event update
8691          * Fires when a Record has been updated
8692          * @param {Store} this
8693          * @param {Roo.data.Record} record The Record that was updated
8694          * @param {String} operation The update operation being performed.  Value may be one of:
8695          * <pre><code>
8696  Roo.data.Record.EDIT
8697  Roo.data.Record.REJECT
8698  Roo.data.Record.COMMIT
8699          * </code></pre>
8700          */
8701         update : true,
8702         /**
8703          * @event clear
8704          * Fires when the data cache has been cleared.
8705          * @param {Store} this
8706          */
8707         clear : true,
8708         /**
8709          * @event beforeload
8710          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8711          * the load action will be canceled.
8712          * @param {Store} this
8713          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8714          */
8715         beforeload : true,
8716         /**
8717          * @event beforeloadadd
8718          * Fires after a new set of Records has been loaded.
8719          * @param {Store} this
8720          * @param {Roo.data.Record[]} records The Records that were loaded
8721          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8722          */
8723         beforeloadadd : true,
8724         /**
8725          * @event load
8726          * Fires after a new set of Records has been loaded, before they are added to the store.
8727          * @param {Store} this
8728          * @param {Roo.data.Record[]} records The Records that were loaded
8729          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8730          * @params {Object} return from reader
8731          */
8732         load : true,
8733         /**
8734          * @event loadexception
8735          * Fires if an exception occurs in the Proxy during loading.
8736          * Called with the signature of the Proxy's "loadexception" event.
8737          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8738          * 
8739          * @param {Proxy} 
8740          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8741          * @param {Object} load options 
8742          * @param {Object} jsonData from your request (normally this contains the Exception)
8743          */
8744         loadexception : true
8745     });
8746     
8747     if(this.proxy){
8748         this.proxy = Roo.factory(this.proxy, Roo.data);
8749         this.proxy.xmodule = this.xmodule || false;
8750         this.relayEvents(this.proxy,  ["loadexception"]);
8751     }
8752     this.sortToggle = {};
8753     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8754
8755     Roo.data.Store.superclass.constructor.call(this);
8756
8757     if(this.inlineData){
8758         this.loadData(this.inlineData);
8759         delete this.inlineData;
8760     }
8761 };
8762
8763 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8764      /**
8765     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8766     * without a remote query - used by combo/forms at present.
8767     */
8768     
8769     /**
8770     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8771     */
8772     /**
8773     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8774     */
8775     /**
8776     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8777     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8778     */
8779     /**
8780     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8781     * on any HTTP request
8782     */
8783     /**
8784     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8785     */
8786     /**
8787     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8788     */
8789     multiSort: false,
8790     /**
8791     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8792     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8793     */
8794     remoteSort : false,
8795
8796     /**
8797     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8798      * loaded or when a record is removed. (defaults to false).
8799     */
8800     pruneModifiedRecords : false,
8801
8802     // private
8803     lastOptions : null,
8804
8805     /**
8806      * Add Records to the Store and fires the add event.
8807      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8808      */
8809     add : function(records){
8810         records = [].concat(records);
8811         for(var i = 0, len = records.length; i < len; i++){
8812             records[i].join(this);
8813         }
8814         var index = this.data.length;
8815         this.data.addAll(records);
8816         this.fireEvent("add", this, records, index);
8817     },
8818
8819     /**
8820      * Remove a Record from the Store and fires the remove event.
8821      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8822      */
8823     remove : function(record){
8824         var index = this.data.indexOf(record);
8825         this.data.removeAt(index);
8826         if(this.pruneModifiedRecords){
8827             this.modified.remove(record);
8828         }
8829         this.fireEvent("remove", this, record, index);
8830     },
8831
8832     /**
8833      * Remove all Records from the Store and fires the clear event.
8834      */
8835     removeAll : function(){
8836         this.data.clear();
8837         if(this.pruneModifiedRecords){
8838             this.modified = [];
8839         }
8840         this.fireEvent("clear", this);
8841     },
8842
8843     /**
8844      * Inserts Records to the Store at the given index and fires the add event.
8845      * @param {Number} index The start index at which to insert the passed Records.
8846      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8847      */
8848     insert : function(index, records){
8849         records = [].concat(records);
8850         for(var i = 0, len = records.length; i < len; i++){
8851             this.data.insert(index, records[i]);
8852             records[i].join(this);
8853         }
8854         this.fireEvent("add", this, records, index);
8855     },
8856
8857     /**
8858      * Get the index within the cache of the passed Record.
8859      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8860      * @return {Number} The index of the passed Record. Returns -1 if not found.
8861      */
8862     indexOf : function(record){
8863         return this.data.indexOf(record);
8864     },
8865
8866     /**
8867      * Get the index within the cache of the Record with the passed id.
8868      * @param {String} id The id of the Record to find.
8869      * @return {Number} The index of the Record. Returns -1 if not found.
8870      */
8871     indexOfId : function(id){
8872         return this.data.indexOfKey(id);
8873     },
8874
8875     /**
8876      * Get the Record with the specified id.
8877      * @param {String} id The id of the Record to find.
8878      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8879      */
8880     getById : function(id){
8881         return this.data.key(id);
8882     },
8883
8884     /**
8885      * Get the Record at the specified index.
8886      * @param {Number} index The index of the Record to find.
8887      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8888      */
8889     getAt : function(index){
8890         return this.data.itemAt(index);
8891     },
8892
8893     /**
8894      * Returns a range of Records between specified indices.
8895      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8896      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8897      * @return {Roo.data.Record[]} An array of Records
8898      */
8899     getRange : function(start, end){
8900         return this.data.getRange(start, end);
8901     },
8902
8903     // private
8904     storeOptions : function(o){
8905         o = Roo.apply({}, o);
8906         delete o.callback;
8907         delete o.scope;
8908         this.lastOptions = o;
8909     },
8910
8911     /**
8912      * Loads the Record cache from the configured Proxy using the configured Reader.
8913      * <p>
8914      * If using remote paging, then the first load call must specify the <em>start</em>
8915      * and <em>limit</em> properties in the options.params property to establish the initial
8916      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8917      * <p>
8918      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8919      * and this call will return before the new data has been loaded. Perform any post-processing
8920      * in a callback function, or in a "load" event handler.</strong>
8921      * <p>
8922      * @param {Object} options An object containing properties which control loading options:<ul>
8923      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8924      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8925      * passed the following arguments:<ul>
8926      * <li>r : Roo.data.Record[]</li>
8927      * <li>options: Options object from the load call</li>
8928      * <li>success: Boolean success indicator</li></ul></li>
8929      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8930      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8931      * </ul>
8932      */
8933     load : function(options){
8934         options = options || {};
8935         if(this.fireEvent("beforeload", this, options) !== false){
8936             this.storeOptions(options);
8937             var p = Roo.apply(options.params || {}, this.baseParams);
8938             // if meta was not loaded from remote source.. try requesting it.
8939             if (!this.reader.metaFromRemote) {
8940                 p._requestMeta = 1;
8941             }
8942             if(this.sortInfo && this.remoteSort){
8943                 var pn = this.paramNames;
8944                 p[pn["sort"]] = this.sortInfo.field;
8945                 p[pn["dir"]] = this.sortInfo.direction;
8946             }
8947             if (this.multiSort) {
8948                 var pn = this.paramNames;
8949                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8950             }
8951             
8952             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8953         }
8954     },
8955
8956     /**
8957      * Reloads the Record cache from the configured Proxy using the configured Reader and
8958      * the options from the last load operation performed.
8959      * @param {Object} options (optional) An object containing properties which may override the options
8960      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8961      * the most recently used options are reused).
8962      */
8963     reload : function(options){
8964         this.load(Roo.applyIf(options||{}, this.lastOptions));
8965     },
8966
8967     // private
8968     // Called as a callback by the Reader during a load operation.
8969     loadRecords : function(o, options, success){
8970         if(!o || success === false){
8971             if(success !== false){
8972                 this.fireEvent("load", this, [], options, o);
8973             }
8974             if(options.callback){
8975                 options.callback.call(options.scope || this, [], options, false);
8976             }
8977             return;
8978         }
8979         // if data returned failure - throw an exception.
8980         if (o.success === false) {
8981             // show a message if no listener is registered.
8982             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8983                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8984             }
8985             // loadmask wil be hooked into this..
8986             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8987             return;
8988         }
8989         var r = o.records, t = o.totalRecords || r.length;
8990         
8991         this.fireEvent("beforeloadadd", this, r, options, o);
8992         
8993         if(!options || options.add !== true){
8994             if(this.pruneModifiedRecords){
8995                 this.modified = [];
8996             }
8997             for(var i = 0, len = r.length; i < len; i++){
8998                 r[i].join(this);
8999             }
9000             if(this.snapshot){
9001                 this.data = this.snapshot;
9002                 delete this.snapshot;
9003             }
9004             this.data.clear();
9005             this.data.addAll(r);
9006             this.totalLength = t;
9007             this.applySort();
9008             this.fireEvent("datachanged", this);
9009         }else{
9010             this.totalLength = Math.max(t, this.data.length+r.length);
9011             this.add(r);
9012         }
9013         this.fireEvent("load", this, r, options, o);
9014         if(options.callback){
9015             options.callback.call(options.scope || this, r, options, true);
9016         }
9017     },
9018
9019
9020     /**
9021      * Loads data from a passed data block. A Reader which understands the format of the data
9022      * must have been configured in the constructor.
9023      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9024      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9025      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9026      */
9027     loadData : function(o, append){
9028         var r = this.reader.readRecords(o);
9029         this.loadRecords(r, {add: append}, true);
9030     },
9031
9032     /**
9033      * Gets the number of cached records.
9034      * <p>
9035      * <em>If using paging, this may not be the total size of the dataset. If the data object
9036      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9037      * the data set size</em>
9038      */
9039     getCount : function(){
9040         return this.data.length || 0;
9041     },
9042
9043     /**
9044      * Gets the total number of records in the dataset as returned by the server.
9045      * <p>
9046      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9047      * the dataset size</em>
9048      */
9049     getTotalCount : function(){
9050         return this.totalLength || 0;
9051     },
9052
9053     /**
9054      * Returns the sort state of the Store as an object with two properties:
9055      * <pre><code>
9056  field {String} The name of the field by which the Records are sorted
9057  direction {String} The sort order, "ASC" or "DESC"
9058      * </code></pre>
9059      */
9060     getSortState : function(){
9061         return this.sortInfo;
9062     },
9063
9064     // private
9065     applySort : function(){
9066         if(this.sortInfo && !this.remoteSort){
9067             var s = this.sortInfo, f = s.field;
9068             var st = this.fields.get(f).sortType;
9069             var fn = function(r1, r2){
9070                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9071                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9072             };
9073             this.data.sort(s.direction, fn);
9074             if(this.snapshot && this.snapshot != this.data){
9075                 this.snapshot.sort(s.direction, fn);
9076             }
9077         }
9078     },
9079
9080     /**
9081      * Sets the default sort column and order to be used by the next load operation.
9082      * @param {String} fieldName The name of the field to sort by.
9083      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9084      */
9085     setDefaultSort : function(field, dir){
9086         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9087     },
9088
9089     /**
9090      * Sort the Records.
9091      * If remote sorting is used, the sort is performed on the server, and the cache is
9092      * reloaded. If local sorting is used, the cache is sorted internally.
9093      * @param {String} fieldName The name of the field to sort by.
9094      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9095      */
9096     sort : function(fieldName, dir){
9097         var f = this.fields.get(fieldName);
9098         if(!dir){
9099             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9100             
9101             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9102                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9103             }else{
9104                 dir = f.sortDir;
9105             }
9106         }
9107         this.sortToggle[f.name] = dir;
9108         this.sortInfo = {field: f.name, direction: dir};
9109         if(!this.remoteSort){
9110             this.applySort();
9111             this.fireEvent("datachanged", this);
9112         }else{
9113             this.load(this.lastOptions);
9114         }
9115     },
9116
9117     /**
9118      * Calls the specified function for each of the Records in the cache.
9119      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9120      * Returning <em>false</em> aborts and exits the iteration.
9121      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9122      */
9123     each : function(fn, scope){
9124         this.data.each(fn, scope);
9125     },
9126
9127     /**
9128      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9129      * (e.g., during paging).
9130      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9131      */
9132     getModifiedRecords : function(){
9133         return this.modified;
9134     },
9135
9136     // private
9137     createFilterFn : function(property, value, anyMatch){
9138         if(!value.exec){ // not a regex
9139             value = String(value);
9140             if(value.length == 0){
9141                 return false;
9142             }
9143             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9144         }
9145         return function(r){
9146             return value.test(r.data[property]);
9147         };
9148     },
9149
9150     /**
9151      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9152      * @param {String} property A field on your records
9153      * @param {Number} start The record index to start at (defaults to 0)
9154      * @param {Number} end The last record index to include (defaults to length - 1)
9155      * @return {Number} The sum
9156      */
9157     sum : function(property, start, end){
9158         var rs = this.data.items, v = 0;
9159         start = start || 0;
9160         end = (end || end === 0) ? end : rs.length-1;
9161
9162         for(var i = start; i <= end; i++){
9163             v += (rs[i].data[property] || 0);
9164         }
9165         return v;
9166     },
9167
9168     /**
9169      * Filter the records by a specified property.
9170      * @param {String} field A field on your records
9171      * @param {String/RegExp} value Either a string that the field
9172      * should start with or a RegExp to test against the field
9173      * @param {Boolean} anyMatch True to match any part not just the beginning
9174      */
9175     filter : function(property, value, anyMatch){
9176         var fn = this.createFilterFn(property, value, anyMatch);
9177         return fn ? this.filterBy(fn) : this.clearFilter();
9178     },
9179
9180     /**
9181      * Filter by a function. The specified function will be called with each
9182      * record in this data source. If the function returns true the record is included,
9183      * otherwise it is filtered.
9184      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9185      * @param {Object} scope (optional) The scope of the function (defaults to this)
9186      */
9187     filterBy : function(fn, scope){
9188         this.snapshot = this.snapshot || this.data;
9189         this.data = this.queryBy(fn, scope||this);
9190         this.fireEvent("datachanged", this);
9191     },
9192
9193     /**
9194      * Query the records by a specified property.
9195      * @param {String} field A field on your records
9196      * @param {String/RegExp} value Either a string that the field
9197      * should start with or a RegExp to test against the field
9198      * @param {Boolean} anyMatch True to match any part not just the beginning
9199      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9200      */
9201     query : function(property, value, anyMatch){
9202         var fn = this.createFilterFn(property, value, anyMatch);
9203         return fn ? this.queryBy(fn) : this.data.clone();
9204     },
9205
9206     /**
9207      * Query by a function. The specified function will be called with each
9208      * record in this data source. If the function returns true the record is included
9209      * in the results.
9210      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9211      * @param {Object} scope (optional) The scope of the function (defaults to this)
9212       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9213      **/
9214     queryBy : function(fn, scope){
9215         var data = this.snapshot || this.data;
9216         return data.filterBy(fn, scope||this);
9217     },
9218
9219     /**
9220      * Collects unique values for a particular dataIndex from this store.
9221      * @param {String} dataIndex The property to collect
9222      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9223      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9224      * @return {Array} An array of the unique values
9225      **/
9226     collect : function(dataIndex, allowNull, bypassFilter){
9227         var d = (bypassFilter === true && this.snapshot) ?
9228                 this.snapshot.items : this.data.items;
9229         var v, sv, r = [], l = {};
9230         for(var i = 0, len = d.length; i < len; i++){
9231             v = d[i].data[dataIndex];
9232             sv = String(v);
9233             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9234                 l[sv] = true;
9235                 r[r.length] = v;
9236             }
9237         }
9238         return r;
9239     },
9240
9241     /**
9242      * Revert to a view of the Record cache with no filtering applied.
9243      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9244      */
9245     clearFilter : function(suppressEvent){
9246         if(this.snapshot && this.snapshot != this.data){
9247             this.data = this.snapshot;
9248             delete this.snapshot;
9249             if(suppressEvent !== true){
9250                 this.fireEvent("datachanged", this);
9251             }
9252         }
9253     },
9254
9255     // private
9256     afterEdit : function(record){
9257         if(this.modified.indexOf(record) == -1){
9258             this.modified.push(record);
9259         }
9260         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9261     },
9262     
9263     // private
9264     afterReject : function(record){
9265         this.modified.remove(record);
9266         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9267     },
9268
9269     // private
9270     afterCommit : function(record){
9271         this.modified.remove(record);
9272         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9273     },
9274
9275     /**
9276      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9277      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9278      */
9279     commitChanges : function(){
9280         var m = this.modified.slice(0);
9281         this.modified = [];
9282         for(var i = 0, len = m.length; i < len; i++){
9283             m[i].commit();
9284         }
9285     },
9286
9287     /**
9288      * Cancel outstanding changes on all changed records.
9289      */
9290     rejectChanges : function(){
9291         var m = this.modified.slice(0);
9292         this.modified = [];
9293         for(var i = 0, len = m.length; i < len; i++){
9294             m[i].reject();
9295         }
9296     },
9297
9298     onMetaChange : function(meta, rtype, o){
9299         this.recordType = rtype;
9300         this.fields = rtype.prototype.fields;
9301         delete this.snapshot;
9302         this.sortInfo = meta.sortInfo || this.sortInfo;
9303         this.modified = [];
9304         this.fireEvent('metachange', this, this.reader.meta);
9305     },
9306     
9307     moveIndex : function(data, type)
9308     {
9309         var index = this.indexOf(data);
9310         
9311         var newIndex = index + type;
9312         
9313         this.remove(data);
9314         
9315         this.insert(newIndex, data);
9316         
9317     }
9318 });/*
9319  * Based on:
9320  * Ext JS Library 1.1.1
9321  * Copyright(c) 2006-2007, Ext JS, LLC.
9322  *
9323  * Originally Released Under LGPL - original licence link has changed is not relivant.
9324  *
9325  * Fork - LGPL
9326  * <script type="text/javascript">
9327  */
9328
9329 /**
9330  * @class Roo.data.SimpleStore
9331  * @extends Roo.data.Store
9332  * Small helper class to make creating Stores from Array data easier.
9333  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9334  * @cfg {Array} fields An array of field definition objects, or field name strings.
9335  * @cfg {Array} data The multi-dimensional array of data
9336  * @constructor
9337  * @param {Object} config
9338  */
9339 Roo.data.SimpleStore = function(config){
9340     Roo.data.SimpleStore.superclass.constructor.call(this, {
9341         isLocal : true,
9342         reader: new Roo.data.ArrayReader({
9343                 id: config.id
9344             },
9345             Roo.data.Record.create(config.fields)
9346         ),
9347         proxy : new Roo.data.MemoryProxy(config.data)
9348     });
9349     this.load();
9350 };
9351 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9352  * Based on:
9353  * Ext JS Library 1.1.1
9354  * Copyright(c) 2006-2007, Ext JS, LLC.
9355  *
9356  * Originally Released Under LGPL - original licence link has changed is not relivant.
9357  *
9358  * Fork - LGPL
9359  * <script type="text/javascript">
9360  */
9361
9362 /**
9363 /**
9364  * @extends Roo.data.Store
9365  * @class Roo.data.JsonStore
9366  * Small helper class to make creating Stores for JSON data easier. <br/>
9367 <pre><code>
9368 var store = new Roo.data.JsonStore({
9369     url: 'get-images.php',
9370     root: 'images',
9371     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9372 });
9373 </code></pre>
9374  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9375  * JsonReader and HttpProxy (unless inline data is provided).</b>
9376  * @cfg {Array} fields An array of field definition objects, or field name strings.
9377  * @constructor
9378  * @param {Object} config
9379  */
9380 Roo.data.JsonStore = function(c){
9381     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9382         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9383         reader: new Roo.data.JsonReader(c, c.fields)
9384     }));
9385 };
9386 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9387  * Based on:
9388  * Ext JS Library 1.1.1
9389  * Copyright(c) 2006-2007, Ext JS, LLC.
9390  *
9391  * Originally Released Under LGPL - original licence link has changed is not relivant.
9392  *
9393  * Fork - LGPL
9394  * <script type="text/javascript">
9395  */
9396
9397  
9398 Roo.data.Field = function(config){
9399     if(typeof config == "string"){
9400         config = {name: config};
9401     }
9402     Roo.apply(this, config);
9403     
9404     if(!this.type){
9405         this.type = "auto";
9406     }
9407     
9408     var st = Roo.data.SortTypes;
9409     // named sortTypes are supported, here we look them up
9410     if(typeof this.sortType == "string"){
9411         this.sortType = st[this.sortType];
9412     }
9413     
9414     // set default sortType for strings and dates
9415     if(!this.sortType){
9416         switch(this.type){
9417             case "string":
9418                 this.sortType = st.asUCString;
9419                 break;
9420             case "date":
9421                 this.sortType = st.asDate;
9422                 break;
9423             default:
9424                 this.sortType = st.none;
9425         }
9426     }
9427
9428     // define once
9429     var stripRe = /[\$,%]/g;
9430
9431     // prebuilt conversion function for this field, instead of
9432     // switching every time we're reading a value
9433     if(!this.convert){
9434         var cv, dateFormat = this.dateFormat;
9435         switch(this.type){
9436             case "":
9437             case "auto":
9438             case undefined:
9439                 cv = function(v){ return v; };
9440                 break;
9441             case "string":
9442                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9443                 break;
9444             case "int":
9445                 cv = function(v){
9446                     return v !== undefined && v !== null && v !== '' ?
9447                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9448                     };
9449                 break;
9450             case "float":
9451                 cv = function(v){
9452                     return v !== undefined && v !== null && v !== '' ?
9453                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9454                     };
9455                 break;
9456             case "bool":
9457             case "boolean":
9458                 cv = function(v){ return v === true || v === "true" || v == 1; };
9459                 break;
9460             case "date":
9461                 cv = function(v){
9462                     if(!v){
9463                         return '';
9464                     }
9465                     if(v instanceof Date){
9466                         return v;
9467                     }
9468                     if(dateFormat){
9469                         if(dateFormat == "timestamp"){
9470                             return new Date(v*1000);
9471                         }
9472                         return Date.parseDate(v, dateFormat);
9473                     }
9474                     var parsed = Date.parse(v);
9475                     return parsed ? new Date(parsed) : null;
9476                 };
9477              break;
9478             
9479         }
9480         this.convert = cv;
9481     }
9482 };
9483
9484 Roo.data.Field.prototype = {
9485     dateFormat: null,
9486     defaultValue: "",
9487     mapping: null,
9488     sortType : null,
9489     sortDir : "ASC"
9490 };/*
9491  * Based on:
9492  * Ext JS Library 1.1.1
9493  * Copyright(c) 2006-2007, Ext JS, LLC.
9494  *
9495  * Originally Released Under LGPL - original licence link has changed is not relivant.
9496  *
9497  * Fork - LGPL
9498  * <script type="text/javascript">
9499  */
9500  
9501 // Base class for reading structured data from a data source.  This class is intended to be
9502 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9503
9504 /**
9505  * @class Roo.data.DataReader
9506  * Base class for reading structured data from a data source.  This class is intended to be
9507  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9508  */
9509
9510 Roo.data.DataReader = function(meta, recordType){
9511     
9512     this.meta = meta;
9513     
9514     this.recordType = recordType instanceof Array ? 
9515         Roo.data.Record.create(recordType) : recordType;
9516 };
9517
9518 Roo.data.DataReader.prototype = {
9519      /**
9520      * Create an empty record
9521      * @param {Object} data (optional) - overlay some values
9522      * @return {Roo.data.Record} record created.
9523      */
9524     newRow :  function(d) {
9525         var da =  {};
9526         this.recordType.prototype.fields.each(function(c) {
9527             switch( c.type) {
9528                 case 'int' : da[c.name] = 0; break;
9529                 case 'date' : da[c.name] = new Date(); break;
9530                 case 'float' : da[c.name] = 0.0; break;
9531                 case 'boolean' : da[c.name] = false; break;
9532                 default : da[c.name] = ""; break;
9533             }
9534             
9535         });
9536         return new this.recordType(Roo.apply(da, d));
9537     }
9538     
9539 };/*
9540  * Based on:
9541  * Ext JS Library 1.1.1
9542  * Copyright(c) 2006-2007, Ext JS, LLC.
9543  *
9544  * Originally Released Under LGPL - original licence link has changed is not relivant.
9545  *
9546  * Fork - LGPL
9547  * <script type="text/javascript">
9548  */
9549
9550 /**
9551  * @class Roo.data.DataProxy
9552  * @extends Roo.data.Observable
9553  * This class is an abstract base class for implementations which provide retrieval of
9554  * unformatted data objects.<br>
9555  * <p>
9556  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9557  * (of the appropriate type which knows how to parse the data object) to provide a block of
9558  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9559  * <p>
9560  * Custom implementations must implement the load method as described in
9561  * {@link Roo.data.HttpProxy#load}.
9562  */
9563 Roo.data.DataProxy = function(){
9564     this.addEvents({
9565         /**
9566          * @event beforeload
9567          * Fires before a network request is made to retrieve a data object.
9568          * @param {Object} This DataProxy object.
9569          * @param {Object} params The params parameter to the load function.
9570          */
9571         beforeload : true,
9572         /**
9573          * @event load
9574          * Fires before the load method's callback is called.
9575          * @param {Object} This DataProxy object.
9576          * @param {Object} o The data object.
9577          * @param {Object} arg The callback argument object passed to the load function.
9578          */
9579         load : true,
9580         /**
9581          * @event loadexception
9582          * Fires if an Exception occurs during data retrieval.
9583          * @param {Object} This DataProxy object.
9584          * @param {Object} o The data object.
9585          * @param {Object} arg The callback argument object passed to the load function.
9586          * @param {Object} e The Exception.
9587          */
9588         loadexception : true
9589     });
9590     Roo.data.DataProxy.superclass.constructor.call(this);
9591 };
9592
9593 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9594
9595     /**
9596      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9597      */
9598 /*
9599  * Based on:
9600  * Ext JS Library 1.1.1
9601  * Copyright(c) 2006-2007, Ext JS, LLC.
9602  *
9603  * Originally Released Under LGPL - original licence link has changed is not relivant.
9604  *
9605  * Fork - LGPL
9606  * <script type="text/javascript">
9607  */
9608 /**
9609  * @class Roo.data.MemoryProxy
9610  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9611  * to the Reader when its load method is called.
9612  * @constructor
9613  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9614  */
9615 Roo.data.MemoryProxy = function(data){
9616     if (data.data) {
9617         data = data.data;
9618     }
9619     Roo.data.MemoryProxy.superclass.constructor.call(this);
9620     this.data = data;
9621 };
9622
9623 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9624     /**
9625      * Load data from the requested source (in this case an in-memory
9626      * data object passed to the constructor), read the data object into
9627      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9628      * process that block using the passed callback.
9629      * @param {Object} params This parameter is not used by the MemoryProxy class.
9630      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9631      * object into a block of Roo.data.Records.
9632      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9633      * The function must be passed <ul>
9634      * <li>The Record block object</li>
9635      * <li>The "arg" argument from the load function</li>
9636      * <li>A boolean success indicator</li>
9637      * </ul>
9638      * @param {Object} scope The scope in which to call the callback
9639      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9640      */
9641     load : function(params, reader, callback, scope, arg){
9642         params = params || {};
9643         var result;
9644         try {
9645             result = reader.readRecords(this.data);
9646         }catch(e){
9647             this.fireEvent("loadexception", this, arg, null, e);
9648             callback.call(scope, null, arg, false);
9649             return;
9650         }
9651         callback.call(scope, result, arg, true);
9652     },
9653     
9654     // private
9655     update : function(params, records){
9656         
9657     }
9658 });/*
9659  * Based on:
9660  * Ext JS Library 1.1.1
9661  * Copyright(c) 2006-2007, Ext JS, LLC.
9662  *
9663  * Originally Released Under LGPL - original licence link has changed is not relivant.
9664  *
9665  * Fork - LGPL
9666  * <script type="text/javascript">
9667  */
9668 /**
9669  * @class Roo.data.HttpProxy
9670  * @extends Roo.data.DataProxy
9671  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9672  * configured to reference a certain URL.<br><br>
9673  * <p>
9674  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9675  * from which the running page was served.<br><br>
9676  * <p>
9677  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9678  * <p>
9679  * Be aware that to enable the browser to parse an XML document, the server must set
9680  * the Content-Type header in the HTTP response to "text/xml".
9681  * @constructor
9682  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9683  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9684  * will be used to make the request.
9685  */
9686 Roo.data.HttpProxy = function(conn){
9687     Roo.data.HttpProxy.superclass.constructor.call(this);
9688     // is conn a conn config or a real conn?
9689     this.conn = conn;
9690     this.useAjax = !conn || !conn.events;
9691   
9692 };
9693
9694 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9695     // thse are take from connection...
9696     
9697     /**
9698      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9699      */
9700     /**
9701      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9702      * extra parameters to each request made by this object. (defaults to undefined)
9703      */
9704     /**
9705      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9706      *  to each request made by this object. (defaults to undefined)
9707      */
9708     /**
9709      * @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)
9710      */
9711     /**
9712      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9713      */
9714      /**
9715      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9716      * @type Boolean
9717      */
9718   
9719
9720     /**
9721      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9722      * @type Boolean
9723      */
9724     /**
9725      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9726      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9727      * a finer-grained basis than the DataProxy events.
9728      */
9729     getConnection : function(){
9730         return this.useAjax ? Roo.Ajax : this.conn;
9731     },
9732
9733     /**
9734      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9735      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9736      * process that block using the passed callback.
9737      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9738      * for the request to the remote server.
9739      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9740      * object into a block of Roo.data.Records.
9741      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9742      * The function must be passed <ul>
9743      * <li>The Record block object</li>
9744      * <li>The "arg" argument from the load function</li>
9745      * <li>A boolean success indicator</li>
9746      * </ul>
9747      * @param {Object} scope The scope in which to call the callback
9748      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9749      */
9750     load : function(params, reader, callback, scope, arg){
9751         if(this.fireEvent("beforeload", this, params) !== false){
9752             var  o = {
9753                 params : params || {},
9754                 request: {
9755                     callback : callback,
9756                     scope : scope,
9757                     arg : arg
9758                 },
9759                 reader: reader,
9760                 callback : this.loadResponse,
9761                 scope: this
9762             };
9763             if(this.useAjax){
9764                 Roo.applyIf(o, this.conn);
9765                 if(this.activeRequest){
9766                     Roo.Ajax.abort(this.activeRequest);
9767                 }
9768                 this.activeRequest = Roo.Ajax.request(o);
9769             }else{
9770                 this.conn.request(o);
9771             }
9772         }else{
9773             callback.call(scope||this, null, arg, false);
9774         }
9775     },
9776
9777     // private
9778     loadResponse : function(o, success, response){
9779         delete this.activeRequest;
9780         if(!success){
9781             this.fireEvent("loadexception", this, o, response);
9782             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9783             return;
9784         }
9785         var result;
9786         try {
9787             result = o.reader.read(response);
9788         }catch(e){
9789             this.fireEvent("loadexception", this, o, response, e);
9790             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9791             return;
9792         }
9793         
9794         this.fireEvent("load", this, o, o.request.arg);
9795         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9796     },
9797
9798     // private
9799     update : function(dataSet){
9800
9801     },
9802
9803     // private
9804     updateResponse : function(dataSet){
9805
9806     }
9807 });/*
9808  * Based on:
9809  * Ext JS Library 1.1.1
9810  * Copyright(c) 2006-2007, Ext JS, LLC.
9811  *
9812  * Originally Released Under LGPL - original licence link has changed is not relivant.
9813  *
9814  * Fork - LGPL
9815  * <script type="text/javascript">
9816  */
9817
9818 /**
9819  * @class Roo.data.ScriptTagProxy
9820  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9821  * other than the originating domain of the running page.<br><br>
9822  * <p>
9823  * <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
9824  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9825  * <p>
9826  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9827  * source code that is used as the source inside a &lt;script> tag.<br><br>
9828  * <p>
9829  * In order for the browser to process the returned data, the server must wrap the data object
9830  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9831  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9832  * depending on whether the callback name was passed:
9833  * <p>
9834  * <pre><code>
9835 boolean scriptTag = false;
9836 String cb = request.getParameter("callback");
9837 if (cb != null) {
9838     scriptTag = true;
9839     response.setContentType("text/javascript");
9840 } else {
9841     response.setContentType("application/x-json");
9842 }
9843 Writer out = response.getWriter();
9844 if (scriptTag) {
9845     out.write(cb + "(");
9846 }
9847 out.print(dataBlock.toJsonString());
9848 if (scriptTag) {
9849     out.write(");");
9850 }
9851 </pre></code>
9852  *
9853  * @constructor
9854  * @param {Object} config A configuration object.
9855  */
9856 Roo.data.ScriptTagProxy = function(config){
9857     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9858     Roo.apply(this, config);
9859     this.head = document.getElementsByTagName("head")[0];
9860 };
9861
9862 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9863
9864 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9865     /**
9866      * @cfg {String} url The URL from which to request the data object.
9867      */
9868     /**
9869      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9870      */
9871     timeout : 30000,
9872     /**
9873      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9874      * the server the name of the callback function set up by the load call to process the returned data object.
9875      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9876      * javascript output which calls this named function passing the data object as its only parameter.
9877      */
9878     callbackParam : "callback",
9879     /**
9880      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9881      * name to the request.
9882      */
9883     nocache : true,
9884
9885     /**
9886      * Load data from the configured URL, read the data object into
9887      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9888      * process that block using the passed callback.
9889      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9890      * for the request to the remote server.
9891      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9892      * object into a block of Roo.data.Records.
9893      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9894      * The function must be passed <ul>
9895      * <li>The Record block object</li>
9896      * <li>The "arg" argument from the load function</li>
9897      * <li>A boolean success indicator</li>
9898      * </ul>
9899      * @param {Object} scope The scope in which to call the callback
9900      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9901      */
9902     load : function(params, reader, callback, scope, arg){
9903         if(this.fireEvent("beforeload", this, params) !== false){
9904
9905             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9906
9907             var url = this.url;
9908             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9909             if(this.nocache){
9910                 url += "&_dc=" + (new Date().getTime());
9911             }
9912             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9913             var trans = {
9914                 id : transId,
9915                 cb : "stcCallback"+transId,
9916                 scriptId : "stcScript"+transId,
9917                 params : params,
9918                 arg : arg,
9919                 url : url,
9920                 callback : callback,
9921                 scope : scope,
9922                 reader : reader
9923             };
9924             var conn = this;
9925
9926             window[trans.cb] = function(o){
9927                 conn.handleResponse(o, trans);
9928             };
9929
9930             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9931
9932             if(this.autoAbort !== false){
9933                 this.abort();
9934             }
9935
9936             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9937
9938             var script = document.createElement("script");
9939             script.setAttribute("src", url);
9940             script.setAttribute("type", "text/javascript");
9941             script.setAttribute("id", trans.scriptId);
9942             this.head.appendChild(script);
9943
9944             this.trans = trans;
9945         }else{
9946             callback.call(scope||this, null, arg, false);
9947         }
9948     },
9949
9950     // private
9951     isLoading : function(){
9952         return this.trans ? true : false;
9953     },
9954
9955     /**
9956      * Abort the current server request.
9957      */
9958     abort : function(){
9959         if(this.isLoading()){
9960             this.destroyTrans(this.trans);
9961         }
9962     },
9963
9964     // private
9965     destroyTrans : function(trans, isLoaded){
9966         this.head.removeChild(document.getElementById(trans.scriptId));
9967         clearTimeout(trans.timeoutId);
9968         if(isLoaded){
9969             window[trans.cb] = undefined;
9970             try{
9971                 delete window[trans.cb];
9972             }catch(e){}
9973         }else{
9974             // if hasn't been loaded, wait for load to remove it to prevent script error
9975             window[trans.cb] = function(){
9976                 window[trans.cb] = undefined;
9977                 try{
9978                     delete window[trans.cb];
9979                 }catch(e){}
9980             };
9981         }
9982     },
9983
9984     // private
9985     handleResponse : function(o, trans){
9986         this.trans = false;
9987         this.destroyTrans(trans, true);
9988         var result;
9989         try {
9990             result = trans.reader.readRecords(o);
9991         }catch(e){
9992             this.fireEvent("loadexception", this, o, trans.arg, e);
9993             trans.callback.call(trans.scope||window, null, trans.arg, false);
9994             return;
9995         }
9996         this.fireEvent("load", this, o, trans.arg);
9997         trans.callback.call(trans.scope||window, result, trans.arg, true);
9998     },
9999
10000     // private
10001     handleFailure : function(trans){
10002         this.trans = false;
10003         this.destroyTrans(trans, false);
10004         this.fireEvent("loadexception", this, null, trans.arg);
10005         trans.callback.call(trans.scope||window, null, trans.arg, false);
10006     }
10007 });/*
10008  * Based on:
10009  * Ext JS Library 1.1.1
10010  * Copyright(c) 2006-2007, Ext JS, LLC.
10011  *
10012  * Originally Released Under LGPL - original licence link has changed is not relivant.
10013  *
10014  * Fork - LGPL
10015  * <script type="text/javascript">
10016  */
10017
10018 /**
10019  * @class Roo.data.JsonReader
10020  * @extends Roo.data.DataReader
10021  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10022  * based on mappings in a provided Roo.data.Record constructor.
10023  * 
10024  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10025  * in the reply previously. 
10026  * 
10027  * <p>
10028  * Example code:
10029  * <pre><code>
10030 var RecordDef = Roo.data.Record.create([
10031     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10032     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10033 ]);
10034 var myReader = new Roo.data.JsonReader({
10035     totalProperty: "results",    // The property which contains the total dataset size (optional)
10036     root: "rows",                // The property which contains an Array of row objects
10037     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10038 }, RecordDef);
10039 </code></pre>
10040  * <p>
10041  * This would consume a JSON file like this:
10042  * <pre><code>
10043 { 'results': 2, 'rows': [
10044     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10045     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10046 }
10047 </code></pre>
10048  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10049  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10050  * paged from the remote server.
10051  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10052  * @cfg {String} root name of the property which contains the Array of row objects.
10053  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10054  * @constructor
10055  * Create a new JsonReader
10056  * @param {Object} meta Metadata configuration options
10057  * @param {Object} recordType Either an Array of field definition objects,
10058  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10059  */
10060 Roo.data.JsonReader = function(meta, recordType){
10061     
10062     meta = meta || {};
10063     // set some defaults:
10064     Roo.applyIf(meta, {
10065         totalProperty: 'total',
10066         successProperty : 'success',
10067         root : 'data',
10068         id : 'id'
10069     });
10070     
10071     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10072 };
10073 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10074     
10075     /**
10076      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10077      * Used by Store query builder to append _requestMeta to params.
10078      * 
10079      */
10080     metaFromRemote : false,
10081     /**
10082      * This method is only used by a DataProxy which has retrieved data from a remote server.
10083      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10084      * @return {Object} data A data block which is used by an Roo.data.Store object as
10085      * a cache of Roo.data.Records.
10086      */
10087     read : function(response){
10088         var json = response.responseText;
10089        
10090         var o = /* eval:var:o */ eval("("+json+")");
10091         if(!o) {
10092             throw {message: "JsonReader.read: Json object not found"};
10093         }
10094         
10095         if(o.metaData){
10096             
10097             delete this.ef;
10098             this.metaFromRemote = true;
10099             this.meta = o.metaData;
10100             this.recordType = Roo.data.Record.create(o.metaData.fields);
10101             this.onMetaChange(this.meta, this.recordType, o);
10102         }
10103         return this.readRecords(o);
10104     },
10105
10106     // private function a store will implement
10107     onMetaChange : function(meta, recordType, o){
10108
10109     },
10110
10111     /**
10112          * @ignore
10113          */
10114     simpleAccess: function(obj, subsc) {
10115         return obj[subsc];
10116     },
10117
10118         /**
10119          * @ignore
10120          */
10121     getJsonAccessor: function(){
10122         var re = /[\[\.]/;
10123         return function(expr) {
10124             try {
10125                 return(re.test(expr))
10126                     ? new Function("obj", "return obj." + expr)
10127                     : function(obj){
10128                         return obj[expr];
10129                     };
10130             } catch(e){}
10131             return Roo.emptyFn;
10132         };
10133     }(),
10134
10135     /**
10136      * Create a data block containing Roo.data.Records from an XML document.
10137      * @param {Object} o An object which contains an Array of row objects in the property specified
10138      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10139      * which contains the total size of the dataset.
10140      * @return {Object} data A data block which is used by an Roo.data.Store object as
10141      * a cache of Roo.data.Records.
10142      */
10143     readRecords : function(o){
10144         /**
10145          * After any data loads, the raw JSON data is available for further custom processing.
10146          * @type Object
10147          */
10148         this.o = o;
10149         var s = this.meta, Record = this.recordType,
10150             f = Record.prototype.fields, fi = f.items, fl = f.length;
10151
10152 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10153         if (!this.ef) {
10154             if(s.totalProperty) {
10155                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10156                 }
10157                 if(s.successProperty) {
10158                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10159                 }
10160                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10161                 if (s.id) {
10162                         var g = this.getJsonAccessor(s.id);
10163                         this.getId = function(rec) {
10164                                 var r = g(rec);  
10165                                 return (r === undefined || r === "") ? null : r;
10166                         };
10167                 } else {
10168                         this.getId = function(){return null;};
10169                 }
10170             this.ef = [];
10171             for(var jj = 0; jj < fl; jj++){
10172                 f = fi[jj];
10173                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10174                 this.ef[jj] = this.getJsonAccessor(map);
10175             }
10176         }
10177
10178         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10179         if(s.totalProperty){
10180             var vt = parseInt(this.getTotal(o), 10);
10181             if(!isNaN(vt)){
10182                 totalRecords = vt;
10183             }
10184         }
10185         if(s.successProperty){
10186             var vs = this.getSuccess(o);
10187             if(vs === false || vs === 'false'){
10188                 success = false;
10189             }
10190         }
10191         var records = [];
10192             for(var i = 0; i < c; i++){
10193                     var n = root[i];
10194                 var values = {};
10195                 var id = this.getId(n);
10196                 for(var j = 0; j < fl; j++){
10197                     f = fi[j];
10198                 var v = this.ef[j](n);
10199                 if (!f.convert) {
10200                     Roo.log('missing convert for ' + f.name);
10201                     Roo.log(f);
10202                     continue;
10203                 }
10204                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10205                 }
10206                 var record = new Record(values, id);
10207                 record.json = n;
10208                 records[i] = record;
10209             }
10210             return {
10211             raw : o,
10212                 success : success,
10213                 records : records,
10214                 totalRecords : totalRecords
10215             };
10216     }
10217 });/*
10218  * Based on:
10219  * Ext JS Library 1.1.1
10220  * Copyright(c) 2006-2007, Ext JS, LLC.
10221  *
10222  * Originally Released Under LGPL - original licence link has changed is not relivant.
10223  *
10224  * Fork - LGPL
10225  * <script type="text/javascript">
10226  */
10227
10228 /**
10229  * @class Roo.data.ArrayReader
10230  * @extends Roo.data.DataReader
10231  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10232  * Each element of that Array represents a row of data fields. The
10233  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10234  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10235  * <p>
10236  * Example code:.
10237  * <pre><code>
10238 var RecordDef = Roo.data.Record.create([
10239     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10240     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10241 ]);
10242 var myReader = new Roo.data.ArrayReader({
10243     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10244 }, RecordDef);
10245 </code></pre>
10246  * <p>
10247  * This would consume an Array like this:
10248  * <pre><code>
10249 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10250   </code></pre>
10251  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10252  * @constructor
10253  * Create a new JsonReader
10254  * @param {Object} meta Metadata configuration options.
10255  * @param {Object} recordType Either an Array of field definition objects
10256  * as specified to {@link Roo.data.Record#create},
10257  * or an {@link Roo.data.Record} object
10258  * created using {@link Roo.data.Record#create}.
10259  */
10260 Roo.data.ArrayReader = function(meta, recordType){
10261     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10262 };
10263
10264 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10265     /**
10266      * Create a data block containing Roo.data.Records from an XML document.
10267      * @param {Object} o An Array of row objects which represents the dataset.
10268      * @return {Object} data A data block which is used by an Roo.data.Store object as
10269      * a cache of Roo.data.Records.
10270      */
10271     readRecords : function(o){
10272         var sid = this.meta ? this.meta.id : null;
10273         var recordType = this.recordType, fields = recordType.prototype.fields;
10274         var records = [];
10275         var root = o;
10276             for(var i = 0; i < root.length; i++){
10277                     var n = root[i];
10278                 var values = {};
10279                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10280                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10281                 var f = fields.items[j];
10282                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10283                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10284                 v = f.convert(v);
10285                 values[f.name] = v;
10286             }
10287                 var record = new recordType(values, id);
10288                 record.json = n;
10289                 records[records.length] = record;
10290             }
10291             return {
10292                 records : records,
10293                 totalRecords : records.length
10294             };
10295     }
10296 });/*
10297  * - LGPL
10298  * * 
10299  */
10300
10301 /**
10302  * @class Roo.bootstrap.ComboBox
10303  * @extends Roo.bootstrap.TriggerField
10304  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10305  * @cfg {Boolean} append (true|false) default false
10306  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10307  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10308  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10309  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10310  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10311  * @constructor
10312  * Create a new ComboBox.
10313  * @param {Object} config Configuration options
10314  */
10315 Roo.bootstrap.ComboBox = function(config){
10316     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10317     this.addEvents({
10318         /**
10319          * @event expand
10320          * Fires when the dropdown list is expanded
10321              * @param {Roo.bootstrap.ComboBox} combo This combo box
10322              */
10323         'expand' : true,
10324         /**
10325          * @event collapse
10326          * Fires when the dropdown list is collapsed
10327              * @param {Roo.bootstrap.ComboBox} combo This combo box
10328              */
10329         'collapse' : true,
10330         /**
10331          * @event beforeselect
10332          * Fires before a list item is selected. Return false to cancel the selection.
10333              * @param {Roo.bootstrap.ComboBox} combo This combo box
10334              * @param {Roo.data.Record} record The data record returned from the underlying store
10335              * @param {Number} index The index of the selected item in the dropdown list
10336              */
10337         'beforeselect' : true,
10338         /**
10339          * @event select
10340          * Fires when a list item is selected
10341              * @param {Roo.bootstrap.ComboBox} combo This combo box
10342              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10343              * @param {Number} index The index of the selected item in the dropdown list
10344              */
10345         'select' : true,
10346         /**
10347          * @event beforequery
10348          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10349          * The event object passed has these properties:
10350              * @param {Roo.bootstrap.ComboBox} combo This combo box
10351              * @param {String} query The query
10352              * @param {Boolean} forceAll true to force "all" query
10353              * @param {Boolean} cancel true to cancel the query
10354              * @param {Object} e The query event object
10355              */
10356         'beforequery': true,
10357          /**
10358          * @event add
10359          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10360              * @param {Roo.bootstrap.ComboBox} combo This combo box
10361              */
10362         'add' : true,
10363         /**
10364          * @event edit
10365          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10366              * @param {Roo.bootstrap.ComboBox} combo This combo box
10367              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10368              */
10369         'edit' : true,
10370         /**
10371          * @event remove
10372          * Fires when the remove value from the combobox array
10373              * @param {Roo.bootstrap.ComboBox} combo This combo box
10374              */
10375         'remove' : true
10376         
10377     });
10378     
10379     this.item = [];
10380     this.tickItems = [];
10381     
10382     this.selectedIndex = -1;
10383     if(this.mode == 'local'){
10384         if(config.queryDelay === undefined){
10385             this.queryDelay = 10;
10386         }
10387         if(config.minChars === undefined){
10388             this.minChars = 0;
10389         }
10390     }
10391 };
10392
10393 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10394      
10395     /**
10396      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10397      * rendering into an Roo.Editor, defaults to false)
10398      */
10399     /**
10400      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10401      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10402      */
10403     /**
10404      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10405      */
10406     /**
10407      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10408      * the dropdown list (defaults to undefined, with no header element)
10409      */
10410
10411      /**
10412      * @cfg {String/Roo.Template} tpl The template to use to render the output
10413      */
10414      
10415      /**
10416      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10417      */
10418     listWidth: undefined,
10419     /**
10420      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10421      * mode = 'remote' or 'text' if mode = 'local')
10422      */
10423     displayField: undefined,
10424     /**
10425      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10426      * mode = 'remote' or 'value' if mode = 'local'). 
10427      * Note: use of a valueField requires the user make a selection
10428      * in order for a value to be mapped.
10429      */
10430     valueField: undefined,
10431     
10432     
10433     /**
10434      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10435      * field's data value (defaults to the underlying DOM element's name)
10436      */
10437     hiddenName: undefined,
10438     /**
10439      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10440      */
10441     listClass: '',
10442     /**
10443      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10444      */
10445     selectedClass: 'active',
10446     
10447     /**
10448      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10449      */
10450     shadow:'sides',
10451     /**
10452      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10453      * anchor positions (defaults to 'tl-bl')
10454      */
10455     listAlign: 'tl-bl?',
10456     /**
10457      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10458      */
10459     maxHeight: 300,
10460     /**
10461      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10462      * query specified by the allQuery config option (defaults to 'query')
10463      */
10464     triggerAction: 'query',
10465     /**
10466      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10467      * (defaults to 4, does not apply if editable = false)
10468      */
10469     minChars : 4,
10470     /**
10471      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10472      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10473      */
10474     typeAhead: false,
10475     /**
10476      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10477      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10478      */
10479     queryDelay: 500,
10480     /**
10481      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10482      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10483      */
10484     pageSize: 0,
10485     /**
10486      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10487      * when editable = true (defaults to false)
10488      */
10489     selectOnFocus:false,
10490     /**
10491      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10492      */
10493     queryParam: 'query',
10494     /**
10495      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10496      * when mode = 'remote' (defaults to 'Loading...')
10497      */
10498     loadingText: 'Loading...',
10499     /**
10500      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10501      */
10502     resizable: false,
10503     /**
10504      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10505      */
10506     handleHeight : 8,
10507     /**
10508      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10509      * traditional select (defaults to true)
10510      */
10511     editable: true,
10512     /**
10513      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10514      */
10515     allQuery: '',
10516     /**
10517      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10518      */
10519     mode: 'remote',
10520     /**
10521      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10522      * listWidth has a higher value)
10523      */
10524     minListWidth : 70,
10525     /**
10526      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10527      * allow the user to set arbitrary text into the field (defaults to false)
10528      */
10529     forceSelection:false,
10530     /**
10531      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10532      * if typeAhead = true (defaults to 250)
10533      */
10534     typeAheadDelay : 250,
10535     /**
10536      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10537      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10538      */
10539     valueNotFoundText : undefined,
10540     /**
10541      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10542      */
10543     blockFocus : false,
10544     
10545     /**
10546      * @cfg {Boolean} disableClear Disable showing of clear button.
10547      */
10548     disableClear : false,
10549     /**
10550      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10551      */
10552     alwaysQuery : false,
10553     
10554     /**
10555      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10556      */
10557     multiple : false,
10558     
10559     //private
10560     addicon : false,
10561     editicon: false,
10562     
10563     page: 0,
10564     hasQuery: false,
10565     append: false,
10566     loadNext: false,
10567     autoFocus : true,
10568     tickable : false,
10569     btnPosition : 'right',
10570     triggerList : true,
10571     showToggleBtn : true,
10572     // element that contains real text value.. (when hidden is used..)
10573     
10574     getAutoCreate : function()
10575     {
10576         var cfg = false;
10577         
10578         /*
10579          *  Normal ComboBox
10580          */
10581         if(!this.tickable){
10582             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10583             return cfg;
10584         }
10585         
10586         /*
10587          *  ComboBox with tickable selections
10588          */
10589              
10590         var align = this.labelAlign || this.parentLabelAlign();
10591         
10592         cfg = {
10593             cls : 'form-group roo-combobox-tickable' //input-group
10594         };
10595         
10596         
10597         var buttons = {
10598             tag : 'div',
10599             cls : 'tickable-buttons',
10600             cn : [
10601                 {
10602                     tag : 'button',
10603                     type : 'button',
10604                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10605                     html : 'Edit'
10606                 },
10607                 {
10608                     tag : 'button',
10609                     type : 'button',
10610                     name : 'ok',
10611                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10612                     html : 'Done'
10613                 },
10614                 {
10615                     tag : 'button',
10616                     type : 'button',
10617                     name : 'cancel',
10618                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10619                     html : 'Cancel'
10620                 }
10621             ]
10622         };
10623         
10624         var _this = this;
10625         Roo.each(buttons.cn, function(c){
10626             if (_this.size) {
10627                 c.cls += ' btn-' + _this.size;
10628             }
10629
10630             if (_this.disabled) {
10631                 c.disabled = true;
10632             }
10633         });
10634         
10635         var box = {
10636             tag: 'div',
10637             cn: [
10638                 {
10639                     tag: 'input',
10640                     type : 'hidden',
10641                     cls: 'form-hidden-field'
10642                 },
10643                 {
10644                     tag: 'ul',
10645                     cls: 'select2-choices',
10646                     cn:[
10647                         {
10648                             tag: 'li',
10649                             cls: 'select2-search-field',
10650                             cn: [
10651
10652                                 buttons
10653                             ]
10654                         }
10655                     ]
10656                 }
10657             ]
10658         }
10659         
10660         var combobox = {
10661             cls: 'select2-container input-group select2-container-multi',
10662             cn: [
10663                 box
10664 //                {
10665 //                    tag: 'ul',
10666 //                    cls: 'typeahead typeahead-long dropdown-menu',
10667 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10668 //                }
10669             ]
10670         };
10671         
10672         if (align ==='left' && this.fieldLabel.length) {
10673             
10674                 Roo.log("left and has label");
10675                 cfg.cn = [
10676                     
10677                     {
10678                         tag: 'label',
10679                         'for' :  id,
10680                         cls : 'control-label col-sm-' + this.labelWidth,
10681                         html : this.fieldLabel
10682                         
10683                     },
10684                     {
10685                         cls : "col-sm-" + (12 - this.labelWidth), 
10686                         cn: [
10687                             combobox
10688                         ]
10689                     }
10690                     
10691                 ];
10692         } else if ( this.fieldLabel.length) {
10693                 Roo.log(" label");
10694                  cfg.cn = [
10695                    
10696                     {
10697                         tag: 'label',
10698                         //cls : 'input-group-addon',
10699                         html : this.fieldLabel
10700                         
10701                     },
10702                     
10703                     combobox
10704                     
10705                 ];
10706
10707         } else {
10708             
10709                 Roo.log(" no label && no align");
10710                 cfg = combobox
10711                      
10712                 
10713         }
10714          
10715         var settings=this;
10716         ['xs','sm','md','lg'].map(function(size){
10717             if (settings[size]) {
10718                 cfg.cls += ' col-' + size + '-' + settings[size];
10719             }
10720         });
10721         
10722         return cfg;
10723         
10724     },
10725     
10726     // private
10727     initEvents: function()
10728     {
10729         
10730         if (!this.store) {
10731             throw "can not find store for combo";
10732         }
10733         this.store = Roo.factory(this.store, Roo.data);
10734         
10735         if(this.tickable){
10736             this.initTickableEvents();
10737             return;
10738         }
10739         
10740         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10741         
10742         if(this.hiddenName){
10743             
10744             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10745             
10746             this.hiddenField.dom.value =
10747                 this.hiddenValue !== undefined ? this.hiddenValue :
10748                 this.value !== undefined ? this.value : '';
10749
10750             // prevent input submission
10751             this.el.dom.removeAttribute('name');
10752             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10753              
10754              
10755         }
10756         //if(Roo.isGecko){
10757         //    this.el.dom.setAttribute('autocomplete', 'off');
10758         //}
10759         
10760         var cls = 'x-combo-list';
10761         
10762         //this.list = new Roo.Layer({
10763         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10764         //});
10765         
10766         var _this = this;
10767         
10768         (function(){
10769             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10770             _this.list.setWidth(lw);
10771         }).defer(100);
10772         
10773         this.list.on('mouseover', this.onViewOver, this);
10774         this.list.on('mousemove', this.onViewMove, this);
10775         
10776         this.list.on('scroll', this.onViewScroll, this);
10777         
10778         /*
10779         this.list.swallowEvent('mousewheel');
10780         this.assetHeight = 0;
10781
10782         if(this.title){
10783             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10784             this.assetHeight += this.header.getHeight();
10785         }
10786
10787         this.innerList = this.list.createChild({cls:cls+'-inner'});
10788         this.innerList.on('mouseover', this.onViewOver, this);
10789         this.innerList.on('mousemove', this.onViewMove, this);
10790         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10791         
10792         if(this.allowBlank && !this.pageSize && !this.disableClear){
10793             this.footer = this.list.createChild({cls:cls+'-ft'});
10794             this.pageTb = new Roo.Toolbar(this.footer);
10795            
10796         }
10797         if(this.pageSize){
10798             this.footer = this.list.createChild({cls:cls+'-ft'});
10799             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10800                     {pageSize: this.pageSize});
10801             
10802         }
10803         
10804         if (this.pageTb && this.allowBlank && !this.disableClear) {
10805             var _this = this;
10806             this.pageTb.add(new Roo.Toolbar.Fill(), {
10807                 cls: 'x-btn-icon x-btn-clear',
10808                 text: '&#160;',
10809                 handler: function()
10810                 {
10811                     _this.collapse();
10812                     _this.clearValue();
10813                     _this.onSelect(false, -1);
10814                 }
10815             });
10816         }
10817         if (this.footer) {
10818             this.assetHeight += this.footer.getHeight();
10819         }
10820         */
10821             
10822         if(!this.tpl){
10823             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10824         }
10825
10826         this.view = new Roo.View(this.list, this.tpl, {
10827             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10828         });
10829         //this.view.wrapEl.setDisplayed(false);
10830         this.view.on('click', this.onViewClick, this);
10831         
10832         
10833         
10834         this.store.on('beforeload', this.onBeforeLoad, this);
10835         this.store.on('load', this.onLoad, this);
10836         this.store.on('loadexception', this.onLoadException, this);
10837         /*
10838         if(this.resizable){
10839             this.resizer = new Roo.Resizable(this.list,  {
10840                pinned:true, handles:'se'
10841             });
10842             this.resizer.on('resize', function(r, w, h){
10843                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10844                 this.listWidth = w;
10845                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10846                 this.restrictHeight();
10847             }, this);
10848             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10849         }
10850         */
10851         if(!this.editable){
10852             this.editable = true;
10853             this.setEditable(false);
10854         }
10855         
10856         /*
10857         
10858         if (typeof(this.events.add.listeners) != 'undefined') {
10859             
10860             this.addicon = this.wrap.createChild(
10861                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10862        
10863             this.addicon.on('click', function(e) {
10864                 this.fireEvent('add', this);
10865             }, this);
10866         }
10867         if (typeof(this.events.edit.listeners) != 'undefined') {
10868             
10869             this.editicon = this.wrap.createChild(
10870                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10871             if (this.addicon) {
10872                 this.editicon.setStyle('margin-left', '40px');
10873             }
10874             this.editicon.on('click', function(e) {
10875                 
10876                 // we fire even  if inothing is selected..
10877                 this.fireEvent('edit', this, this.lastData );
10878                 
10879             }, this);
10880         }
10881         */
10882         
10883         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10884             "up" : function(e){
10885                 this.inKeyMode = true;
10886                 this.selectPrev();
10887             },
10888
10889             "down" : function(e){
10890                 if(!this.isExpanded()){
10891                     this.onTriggerClick();
10892                 }else{
10893                     this.inKeyMode = true;
10894                     this.selectNext();
10895                 }
10896             },
10897
10898             "enter" : function(e){
10899 //                this.onViewClick();
10900                 //return true;
10901                 this.collapse();
10902                 
10903                 if(this.fireEvent("specialkey", this, e)){
10904                     this.onViewClick(false);
10905                 }
10906                 
10907                 return true;
10908             },
10909
10910             "esc" : function(e){
10911                 this.collapse();
10912             },
10913
10914             "tab" : function(e){
10915                 this.collapse();
10916                 
10917                 if(this.fireEvent("specialkey", this, e)){
10918                     this.onViewClick(false);
10919                 }
10920                 
10921                 return true;
10922             },
10923
10924             scope : this,
10925
10926             doRelay : function(foo, bar, hname){
10927                 if(hname == 'down' || this.scope.isExpanded()){
10928                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10929                 }
10930                 return true;
10931             },
10932
10933             forceKeyDown: true
10934         });
10935         
10936         
10937         this.queryDelay = Math.max(this.queryDelay || 10,
10938                 this.mode == 'local' ? 10 : 250);
10939         
10940         
10941         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10942         
10943         if(this.typeAhead){
10944             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10945         }
10946         if(this.editable !== false){
10947             this.inputEl().on("keyup", this.onKeyUp, this);
10948         }
10949         if(this.forceSelection){
10950             this.inputEl().on('blur', this.doForce, this);
10951         }
10952         
10953         if(this.multiple){
10954             this.choices = this.el.select('ul.select2-choices', true).first();
10955             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10956         }
10957     },
10958     
10959     initTickableEvents: function()
10960     {   
10961         this.createList();
10962         
10963         if(this.hiddenName){
10964             
10965             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10966             
10967             this.hiddenField.dom.value =
10968                 this.hiddenValue !== undefined ? this.hiddenValue :
10969                 this.value !== undefined ? this.value : '';
10970
10971             // prevent input submission
10972             this.el.dom.removeAttribute('name');
10973             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10974              
10975              
10976         }
10977         
10978 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10979         
10980         this.choices = this.el.select('ul.select2-choices', true).first();
10981         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10982         if(this.triggerList){
10983             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10984         }
10985          
10986         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10987         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10988         
10989         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10990         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10991         
10992         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10993         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10994         
10995         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10996         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10997         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10998         
10999         this.okBtn.hide();
11000         this.cancelBtn.hide();
11001         
11002         var _this = this;
11003         
11004         (function(){
11005             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11006             _this.list.setWidth(lw);
11007         }).defer(100);
11008         
11009         this.list.on('mouseover', this.onViewOver, this);
11010         this.list.on('mousemove', this.onViewMove, this);
11011         
11012         this.list.on('scroll', this.onViewScroll, this);
11013         
11014         if(!this.tpl){
11015             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>';
11016         }
11017
11018         this.view = new Roo.View(this.list, this.tpl, {
11019             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11020         });
11021         
11022         //this.view.wrapEl.setDisplayed(false);
11023         this.view.on('click', this.onViewClick, this);
11024         
11025         
11026         
11027         this.store.on('beforeload', this.onBeforeLoad, this);
11028         this.store.on('load', this.onLoad, this);
11029         this.store.on('loadexception', this.onLoadException, this);
11030         
11031 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11032 //            "up" : function(e){
11033 //                this.inKeyMode = true;
11034 //                this.selectPrev();
11035 //            },
11036 //
11037 //            "down" : function(e){
11038 //                if(!this.isExpanded()){
11039 //                    this.onTriggerClick();
11040 //                }else{
11041 //                    this.inKeyMode = true;
11042 //                    this.selectNext();
11043 //                }
11044 //            },
11045 //
11046 //            "enter" : function(e){
11047 ////                this.onViewClick();
11048 //                //return true;
11049 //                this.collapse();
11050 //                
11051 //                if(this.fireEvent("specialkey", this, e)){
11052 //                    this.onViewClick(false);
11053 //                }
11054 //                
11055 //                return true;
11056 //            },
11057 //
11058 //            "esc" : function(e){
11059 //                this.collapse();
11060 //            },
11061 //
11062 //            "tab" : function(e){
11063 //                this.collapse();
11064 //                
11065 //                if(this.fireEvent("specialkey", this, e)){
11066 //                    this.onViewClick(false);
11067 //                }
11068 //                
11069 //                return true;
11070 //            },
11071 //
11072 //            scope : this,
11073 //
11074 //            doRelay : function(foo, bar, hname){
11075 //                if(hname == 'down' || this.scope.isExpanded()){
11076 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11077 //                }
11078 //                return true;
11079 //            },
11080 //
11081 //            forceKeyDown: true
11082 //        });
11083         
11084         
11085         this.queryDelay = Math.max(this.queryDelay || 10,
11086                 this.mode == 'local' ? 10 : 250);
11087         
11088         
11089         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11090         
11091         if(this.typeAhead){
11092             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11093         }
11094     },
11095
11096     onDestroy : function(){
11097         if(this.view){
11098             this.view.setStore(null);
11099             this.view.el.removeAllListeners();
11100             this.view.el.remove();
11101             this.view.purgeListeners();
11102         }
11103         if(this.list){
11104             this.list.dom.innerHTML  = '';
11105         }
11106         
11107         if(this.store){
11108             this.store.un('beforeload', this.onBeforeLoad, this);
11109             this.store.un('load', this.onLoad, this);
11110             this.store.un('loadexception', this.onLoadException, this);
11111         }
11112         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11113     },
11114
11115     // private
11116     fireKey : function(e){
11117         if(e.isNavKeyPress() && !this.list.isVisible()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121
11122     // private
11123     onResize: function(w, h){
11124 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11125 //        
11126 //        if(typeof w != 'number'){
11127 //            // we do not handle it!?!?
11128 //            return;
11129 //        }
11130 //        var tw = this.trigger.getWidth();
11131 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11132 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11133 //        var x = w - tw;
11134 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11135 //            
11136 //        //this.trigger.setStyle('left', x+'px');
11137 //        
11138 //        if(this.list && this.listWidth === undefined){
11139 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11140 //            this.list.setWidth(lw);
11141 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11142 //        }
11143         
11144     
11145         
11146     },
11147
11148     /**
11149      * Allow or prevent the user from directly editing the field text.  If false is passed,
11150      * the user will only be able to select from the items defined in the dropdown list.  This method
11151      * is the runtime equivalent of setting the 'editable' config option at config time.
11152      * @param {Boolean} value True to allow the user to directly edit the field text
11153      */
11154     setEditable : function(value){
11155         if(value == this.editable){
11156             return;
11157         }
11158         this.editable = value;
11159         if(!value){
11160             this.inputEl().dom.setAttribute('readOnly', true);
11161             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11162             this.inputEl().addClass('x-combo-noedit');
11163         }else{
11164             this.inputEl().dom.setAttribute('readOnly', false);
11165             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11166             this.inputEl().removeClass('x-combo-noedit');
11167         }
11168     },
11169
11170     // private
11171     
11172     onBeforeLoad : function(combo,opts){
11173         if(!this.hasFocus){
11174             return;
11175         }
11176          if (!opts.add) {
11177             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11178          }
11179         this.restrictHeight();
11180         this.selectedIndex = -1;
11181     },
11182
11183     // private
11184     onLoad : function(){
11185         
11186         this.hasQuery = false;
11187         
11188         if(!this.hasFocus){
11189             return;
11190         }
11191         
11192         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11193             this.loading.hide();
11194         }
11195         
11196         if(this.store.getCount() > 0){
11197             this.expand();
11198 //            this.restrictHeight();
11199             if(this.lastQuery == this.allQuery){
11200                 if(this.editable && !this.tickable){
11201                     this.inputEl().dom.select();
11202                 }
11203                 
11204                 if(
11205                     !this.selectByValue(this.value, true) &&
11206                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11207                     this.store.lastOptions.add != true)
11208                 ){
11209                     this.select(0, true);
11210                 }
11211             }else{
11212                 if(this.autoFocus){
11213                     this.selectNext();
11214                 }
11215                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11216                     this.taTask.delay(this.typeAheadDelay);
11217                 }
11218             }
11219         }else{
11220             this.onEmptyResults();
11221         }
11222         
11223         //this.el.focus();
11224     },
11225     // private
11226     onLoadException : function()
11227     {
11228         this.hasQuery = false;
11229         
11230         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11231             this.loading.hide();
11232         }
11233         
11234         this.collapse();
11235         Roo.log(this.store.reader.jsonData);
11236         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11237             // fixme
11238             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11239         }
11240         
11241         
11242     },
11243     // private
11244     onTypeAhead : function(){
11245         if(this.store.getCount() > 0){
11246             var r = this.store.getAt(0);
11247             var newValue = r.data[this.displayField];
11248             var len = newValue.length;
11249             var selStart = this.getRawValue().length;
11250             
11251             if(selStart != len){
11252                 this.setRawValue(newValue);
11253                 this.selectText(selStart, newValue.length);
11254             }
11255         }
11256     },
11257
11258     // private
11259     onSelect : function(record, index){
11260         
11261         if(this.fireEvent('beforeselect', this, record, index) !== false){
11262         
11263             this.setFromData(index > -1 ? record.data : false);
11264             
11265             this.collapse();
11266             this.fireEvent('select', this, record, index);
11267         }
11268     },
11269
11270     /**
11271      * Returns the currently selected field value or empty string if no value is set.
11272      * @return {String} value The selected value
11273      */
11274     getValue : function(){
11275         
11276         if(this.multiple){
11277             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11278         }
11279         
11280         if(this.valueField){
11281             return typeof this.value != 'undefined' ? this.value : '';
11282         }else{
11283             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11284         }
11285     },
11286
11287     /**
11288      * Clears any text/value currently set in the field
11289      */
11290     clearValue : function(){
11291         if(this.hiddenField){
11292             this.hiddenField.dom.value = '';
11293         }
11294         this.value = '';
11295         this.setRawValue('');
11296         this.lastSelectionText = '';
11297         
11298     },
11299
11300     /**
11301      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11302      * will be displayed in the field.  If the value does not match the data value of an existing item,
11303      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11304      * Otherwise the field will be blank (although the value will still be set).
11305      * @param {String} value The value to match
11306      */
11307     setValue : function(v){
11308         if(this.multiple){
11309             this.syncValue();
11310             return;
11311         }
11312         
11313         var text = v;
11314         if(this.valueField){
11315             var r = this.findRecord(this.valueField, v);
11316             if(r){
11317                 text = r.data[this.displayField];
11318             }else if(this.valueNotFoundText !== undefined){
11319                 text = this.valueNotFoundText;
11320             }
11321         }
11322         this.lastSelectionText = text;
11323         if(this.hiddenField){
11324             this.hiddenField.dom.value = v;
11325         }
11326         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11327         this.value = v;
11328     },
11329     /**
11330      * @property {Object} the last set data for the element
11331      */
11332     
11333     lastData : false,
11334     /**
11335      * Sets the value of the field based on a object which is related to the record format for the store.
11336      * @param {Object} value the value to set as. or false on reset?
11337      */
11338     setFromData : function(o){
11339         
11340         if(this.multiple){
11341             this.addItem(o);
11342             return;
11343         }
11344             
11345         var dv = ''; // display value
11346         var vv = ''; // value value..
11347         this.lastData = o;
11348         if (this.displayField) {
11349             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11350         } else {
11351             // this is an error condition!!!
11352             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11353         }
11354         
11355         if(this.valueField){
11356             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11357         }
11358         
11359         if(this.hiddenField){
11360             this.hiddenField.dom.value = vv;
11361             
11362             this.lastSelectionText = dv;
11363             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11364             this.value = vv;
11365             return;
11366         }
11367         // no hidden field.. - we store the value in 'value', but still display
11368         // display field!!!!
11369         this.lastSelectionText = dv;
11370         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11371         this.value = vv;
11372         
11373         
11374     },
11375     // private
11376     reset : function(){
11377         // overridden so that last data is reset..
11378         this.setValue(this.originalValue);
11379         this.clearInvalid();
11380         this.lastData = false;
11381         if (this.view) {
11382             this.view.clearSelections();
11383         }
11384     },
11385     // private
11386     findRecord : function(prop, value){
11387         var record;
11388         if(this.store.getCount() > 0){
11389             this.store.each(function(r){
11390                 if(r.data[prop] == value){
11391                     record = r;
11392                     return false;
11393                 }
11394                 return true;
11395             });
11396         }
11397         return record;
11398     },
11399     
11400     getName: function()
11401     {
11402         // returns hidden if it's set..
11403         if (!this.rendered) {return ''};
11404         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11405         
11406     },
11407     // private
11408     onViewMove : function(e, t){
11409         this.inKeyMode = false;
11410     },
11411
11412     // private
11413     onViewOver : function(e, t){
11414         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11415             return;
11416         }
11417         var item = this.view.findItemFromChild(t);
11418         
11419         if(item){
11420             var index = this.view.indexOf(item);
11421             this.select(index, false);
11422         }
11423     },
11424
11425     // private
11426     onViewClick : function(view, doFocus, el, e)
11427     {
11428         var index = this.view.getSelectedIndexes()[0];
11429         
11430         var r = this.store.getAt(index);
11431         
11432         if(this.tickable){
11433             
11434             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11435                 return;
11436             }
11437             
11438             var rm = false;
11439             var _this = this;
11440             
11441             Roo.each(this.tickItems, function(v,k){
11442                 
11443                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11444                     _this.tickItems.splice(k, 1);
11445                     rm = true;
11446                     return;
11447                 }
11448             })
11449             
11450             if(rm){
11451                 return;
11452             }
11453             
11454             this.tickItems.push(r.data);
11455             return;
11456         }
11457         
11458         if(r){
11459             this.onSelect(r, index);
11460         }
11461         if(doFocus !== false && !this.blockFocus){
11462             this.inputEl().focus();
11463         }
11464     },
11465
11466     // private
11467     restrictHeight : function(){
11468         //this.innerList.dom.style.height = '';
11469         //var inner = this.innerList.dom;
11470         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11471         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11472         //this.list.beginUpdate();
11473         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11474         this.list.alignTo(this.inputEl(), this.listAlign);
11475         this.list.alignTo(this.inputEl(), this.listAlign);
11476         //this.list.endUpdate();
11477     },
11478
11479     // private
11480     onEmptyResults : function(){
11481         this.collapse();
11482     },
11483
11484     /**
11485      * Returns true if the dropdown list is expanded, else false.
11486      */
11487     isExpanded : function(){
11488         return this.list.isVisible();
11489     },
11490
11491     /**
11492      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11493      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11494      * @param {String} value The data value of the item to select
11495      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11496      * selected item if it is not currently in view (defaults to true)
11497      * @return {Boolean} True if the value matched an item in the list, else false
11498      */
11499     selectByValue : function(v, scrollIntoView){
11500         if(v !== undefined && v !== null){
11501             var r = this.findRecord(this.valueField || this.displayField, v);
11502             if(r){
11503                 this.select(this.store.indexOf(r), scrollIntoView);
11504                 return true;
11505             }
11506         }
11507         return false;
11508     },
11509
11510     /**
11511      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11512      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11513      * @param {Number} index The zero-based index of the list item to select
11514      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11515      * selected item if it is not currently in view (defaults to true)
11516      */
11517     select : function(index, scrollIntoView){
11518         this.selectedIndex = index;
11519         this.view.select(index);
11520         if(scrollIntoView !== false){
11521             var el = this.view.getNode(index);
11522             if(el && !this.multiple && !this.tickable){
11523                 this.list.scrollChildIntoView(el, false);
11524             }
11525         }
11526     },
11527
11528     // private
11529     selectNext : function(){
11530         var ct = this.store.getCount();
11531         if(ct > 0){
11532             if(this.selectedIndex == -1){
11533                 this.select(0);
11534             }else if(this.selectedIndex < ct-1){
11535                 this.select(this.selectedIndex+1);
11536             }
11537         }
11538     },
11539
11540     // private
11541     selectPrev : function(){
11542         var ct = this.store.getCount();
11543         if(ct > 0){
11544             if(this.selectedIndex == -1){
11545                 this.select(0);
11546             }else if(this.selectedIndex != 0){
11547                 this.select(this.selectedIndex-1);
11548             }
11549         }
11550     },
11551
11552     // private
11553     onKeyUp : function(e){
11554         if(this.editable !== false && !e.isSpecialKey()){
11555             this.lastKey = e.getKey();
11556             this.dqTask.delay(this.queryDelay);
11557         }
11558     },
11559
11560     // private
11561     validateBlur : function(){
11562         return !this.list || !this.list.isVisible();   
11563     },
11564
11565     // private
11566     initQuery : function(){
11567         this.doQuery(this.getRawValue());
11568     },
11569
11570     // private
11571     doForce : function(){
11572         if(this.inputEl().dom.value.length > 0){
11573             this.inputEl().dom.value =
11574                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11575              
11576         }
11577     },
11578
11579     /**
11580      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11581      * query allowing the query action to be canceled if needed.
11582      * @param {String} query The SQL query to execute
11583      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11584      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11585      * saved in the current store (defaults to false)
11586      */
11587     doQuery : function(q, forceAll){
11588         
11589         if(q === undefined || q === null){
11590             q = '';
11591         }
11592         var qe = {
11593             query: q,
11594             forceAll: forceAll,
11595             combo: this,
11596             cancel:false
11597         };
11598         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11599             return false;
11600         }
11601         q = qe.query;
11602         
11603         forceAll = qe.forceAll;
11604         if(forceAll === true || (q.length >= this.minChars)){
11605             
11606             this.hasQuery = true;
11607             
11608             if(this.lastQuery != q || this.alwaysQuery){
11609                 this.lastQuery = q;
11610                 if(this.mode == 'local'){
11611                     this.selectedIndex = -1;
11612                     if(forceAll){
11613                         this.store.clearFilter();
11614                     }else{
11615                         this.store.filter(this.displayField, q);
11616                     }
11617                     this.onLoad();
11618                 }else{
11619                     this.store.baseParams[this.queryParam] = q;
11620                     
11621                     var options = {params : this.getParams(q)};
11622                     
11623                     if(this.loadNext){
11624                         options.add = true;
11625                         options.params.start = this.page * this.pageSize;
11626                     }
11627                     
11628                     this.store.load(options);
11629                     /*
11630                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11631                      *  we should expand the list on onLoad
11632                      *  so command out it
11633                      */
11634 //                    this.expand();
11635                 }
11636             }else{
11637                 this.selectedIndex = -1;
11638                 this.onLoad();   
11639             }
11640         }
11641         
11642         this.loadNext = false;
11643     },
11644
11645     // private
11646     getParams : function(q){
11647         var p = {};
11648         //p[this.queryParam] = q;
11649         
11650         if(this.pageSize){
11651             p.start = 0;
11652             p.limit = this.pageSize;
11653         }
11654         return p;
11655     },
11656
11657     /**
11658      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11659      */
11660     collapse : function(){
11661         if(!this.isExpanded()){
11662             return;
11663         }
11664         
11665         this.list.hide();
11666         
11667         if(this.tickable){
11668             this.okBtn.hide();
11669             this.cancelBtn.hide();
11670             this.trigger.show();
11671         }
11672         
11673         Roo.get(document).un('mousedown', this.collapseIf, this);
11674         Roo.get(document).un('mousewheel', this.collapseIf, this);
11675         if (!this.editable) {
11676             Roo.get(document).un('keydown', this.listKeyPress, this);
11677         }
11678         this.fireEvent('collapse', this);
11679     },
11680
11681     // private
11682     collapseIf : function(e){
11683         var in_combo  = e.within(this.el);
11684         var in_list =  e.within(this.list);
11685         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11686         
11687         if (in_combo || in_list || is_list) {
11688             //e.stopPropagation();
11689             return;
11690         }
11691         
11692         if(this.tickable){
11693             this.onTickableFooterButtonClick(e, false, false);
11694         }
11695
11696         this.collapse();
11697         
11698     },
11699
11700     /**
11701      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11702      */
11703     expand : function(){
11704        
11705         if(this.isExpanded() || !this.hasFocus){
11706             return;
11707         }
11708         
11709         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11710         this.list.setWidth(lw);
11711         
11712         
11713          Roo.log('expand');
11714         
11715         this.list.show();
11716         
11717         this.restrictHeight();
11718         
11719         if(this.tickable){
11720             
11721             this.tickItems = Roo.apply([], this.item);
11722             
11723             this.okBtn.show();
11724             this.cancelBtn.show();
11725             this.trigger.hide();
11726             
11727         }
11728         
11729         Roo.get(document).on('mousedown', this.collapseIf, this);
11730         Roo.get(document).on('mousewheel', this.collapseIf, this);
11731         if (!this.editable) {
11732             Roo.get(document).on('keydown', this.listKeyPress, this);
11733         }
11734         
11735         this.fireEvent('expand', this);
11736     },
11737
11738     // private
11739     // Implements the default empty TriggerField.onTriggerClick function
11740     onTriggerClick : function(e)
11741     {
11742         Roo.log('trigger click');
11743         
11744         if(this.disabled || !this.triggerList){
11745             return;
11746         }
11747         
11748         this.page = 0;
11749         this.loadNext = false;
11750         
11751         if(this.isExpanded()){
11752             this.collapse();
11753             if (!this.blockFocus) {
11754                 this.inputEl().focus();
11755             }
11756             
11757         }else {
11758             this.hasFocus = true;
11759             if(this.triggerAction == 'all') {
11760                 this.doQuery(this.allQuery, true);
11761             } else {
11762                 this.doQuery(this.getRawValue());
11763             }
11764             if (!this.blockFocus) {
11765                 this.inputEl().focus();
11766             }
11767         }
11768     },
11769     
11770     onTickableTriggerClick : function(e)
11771     {
11772         if(this.disabled){
11773             return;
11774         }
11775         
11776         this.page = 0;
11777         this.loadNext = false;
11778         this.hasFocus = true;
11779         
11780         if(this.triggerAction == 'all') {
11781             this.doQuery(this.allQuery, true);
11782         } else {
11783             this.doQuery(this.getRawValue());
11784         }
11785     },
11786     
11787     onSearchFieldClick : function(e)
11788     {
11789         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11790             return;
11791         }
11792         
11793         this.page = 0;
11794         this.loadNext = false;
11795         this.hasFocus = true;
11796         
11797         if(this.triggerAction == 'all') {
11798             this.doQuery(this.allQuery, true);
11799         } else {
11800             this.doQuery(this.getRawValue());
11801         }
11802     },
11803     
11804     listKeyPress : function(e)
11805     {
11806         //Roo.log('listkeypress');
11807         // scroll to first matching element based on key pres..
11808         if (e.isSpecialKey()) {
11809             return false;
11810         }
11811         var k = String.fromCharCode(e.getKey()).toUpperCase();
11812         //Roo.log(k);
11813         var match  = false;
11814         var csel = this.view.getSelectedNodes();
11815         var cselitem = false;
11816         if (csel.length) {
11817             var ix = this.view.indexOf(csel[0]);
11818             cselitem  = this.store.getAt(ix);
11819             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11820                 cselitem = false;
11821             }
11822             
11823         }
11824         
11825         this.store.each(function(v) { 
11826             if (cselitem) {
11827                 // start at existing selection.
11828                 if (cselitem.id == v.id) {
11829                     cselitem = false;
11830                 }
11831                 return true;
11832             }
11833                 
11834             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11835                 match = this.store.indexOf(v);
11836                 return false;
11837             }
11838             return true;
11839         }, this);
11840         
11841         if (match === false) {
11842             return true; // no more action?
11843         }
11844         // scroll to?
11845         this.view.select(match);
11846         var sn = Roo.get(this.view.getSelectedNodes()[0])
11847         sn.scrollIntoView(sn.dom.parentNode, false);
11848     },
11849     
11850     onViewScroll : function(e, t){
11851         
11852         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){
11853             return;
11854         }
11855         
11856         this.hasQuery = true;
11857         
11858         this.loading = this.list.select('.loading', true).first();
11859         
11860         if(this.loading === null){
11861             this.list.createChild({
11862                 tag: 'div',
11863                 cls: 'loading select2-more-results select2-active',
11864                 html: 'Loading more results...'
11865             })
11866             
11867             this.loading = this.list.select('.loading', true).first();
11868             
11869             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11870             
11871             this.loading.hide();
11872         }
11873         
11874         this.loading.show();
11875         
11876         var _combo = this;
11877         
11878         this.page++;
11879         this.loadNext = true;
11880         
11881         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11882         
11883         return;
11884     },
11885     
11886     addItem : function(o)
11887     {   
11888         var dv = ''; // display value
11889         
11890         if (this.displayField) {
11891             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11892         } else {
11893             // this is an error condition!!!
11894             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11895         }
11896         
11897         if(!dv.length){
11898             return;
11899         }
11900         
11901         var choice = this.choices.createChild({
11902             tag: 'li',
11903             cls: 'select2-search-choice',
11904             cn: [
11905                 {
11906                     tag: 'div',
11907                     html: dv
11908                 },
11909                 {
11910                     tag: 'a',
11911                     href: '#',
11912                     cls: 'select2-search-choice-close',
11913                     tabindex: '-1'
11914                 }
11915             ]
11916             
11917         }, this.searchField);
11918         
11919         var close = choice.select('a.select2-search-choice-close', true).first()
11920         
11921         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11922         
11923         this.item.push(o);
11924         
11925         this.lastData = o;
11926         
11927         this.syncValue();
11928         
11929         this.inputEl().dom.value = '';
11930         
11931     },
11932     
11933     onRemoveItem : function(e, _self, o)
11934     {
11935         e.preventDefault();
11936         var index = this.item.indexOf(o.data) * 1;
11937         
11938         if( index < 0){
11939             Roo.log('not this item?!');
11940             return;
11941         }
11942         
11943         this.item.splice(index, 1);
11944         o.item.remove();
11945         
11946         this.syncValue();
11947         
11948         this.fireEvent('remove', this, e);
11949         
11950     },
11951     
11952     syncValue : function()
11953     {
11954         if(!this.item.length){
11955             this.clearValue();
11956             return;
11957         }
11958             
11959         var value = [];
11960         var _this = this;
11961         Roo.each(this.item, function(i){
11962             if(_this.valueField){
11963                 value.push(i[_this.valueField]);
11964                 return;
11965             }
11966
11967             value.push(i);
11968         });
11969
11970         this.value = value.join(',');
11971
11972         if(this.hiddenField){
11973             this.hiddenField.dom.value = this.value;
11974         }
11975     },
11976     
11977     clearItem : function()
11978     {
11979         if(!this.multiple){
11980             return;
11981         }
11982         
11983         this.item = [];
11984         
11985         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11986            c.remove();
11987         });
11988         
11989         this.syncValue();
11990     },
11991     
11992     inputEl: function ()
11993     {
11994         if(this.tickable){
11995             return this.searchField;
11996         }
11997         return this.el.select('input.form-control',true).first();
11998     },
11999     
12000     
12001     onTickableFooterButtonClick : function(e, btn, el)
12002     {
12003         e.preventDefault();
12004         
12005         if(btn && btn.name == 'cancel'){
12006             this.tickItems = Roo.apply([], this.item);
12007             this.collapse();
12008             return;
12009         }
12010         
12011         this.clearItem();
12012         
12013         var _this = this;
12014         
12015         Roo.each(this.tickItems, function(o){
12016             _this.addItem(o);
12017         });
12018         
12019         this.collapse();
12020         
12021     }
12022     
12023     
12024
12025     /** 
12026     * @cfg {Boolean} grow 
12027     * @hide 
12028     */
12029     /** 
12030     * @cfg {Number} growMin 
12031     * @hide 
12032     */
12033     /** 
12034     * @cfg {Number} growMax 
12035     * @hide 
12036     */
12037     /**
12038      * @hide
12039      * @method autoSize
12040      */
12041 });
12042 /*
12043  * Based on:
12044  * Ext JS Library 1.1.1
12045  * Copyright(c) 2006-2007, Ext JS, LLC.
12046  *
12047  * Originally Released Under LGPL - original licence link has changed is not relivant.
12048  *
12049  * Fork - LGPL
12050  * <script type="text/javascript">
12051  */
12052
12053 /**
12054  * @class Roo.View
12055  * @extends Roo.util.Observable
12056  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12057  * This class also supports single and multi selection modes. <br>
12058  * Create a data model bound view:
12059  <pre><code>
12060  var store = new Roo.data.Store(...);
12061
12062  var view = new Roo.View({
12063     el : "my-element",
12064     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12065  
12066     singleSelect: true,
12067     selectedClass: "ydataview-selected",
12068     store: store
12069  });
12070
12071  // listen for node click?
12072  view.on("click", function(vw, index, node, e){
12073  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12074  });
12075
12076  // load XML data
12077  dataModel.load("foobar.xml");
12078  </code></pre>
12079  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12080  * <br><br>
12081  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12082  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12083  * 
12084  * Note: old style constructor is still suported (container, template, config)
12085  * 
12086  * @constructor
12087  * Create a new View
12088  * @param {Object} config The config object
12089  * 
12090  */
12091 Roo.View = function(config, depreciated_tpl, depreciated_config){
12092     
12093     this.parent = false;
12094     
12095     if (typeof(depreciated_tpl) == 'undefined') {
12096         // new way.. - universal constructor.
12097         Roo.apply(this, config);
12098         this.el  = Roo.get(this.el);
12099     } else {
12100         // old format..
12101         this.el  = Roo.get(config);
12102         this.tpl = depreciated_tpl;
12103         Roo.apply(this, depreciated_config);
12104     }
12105     this.wrapEl  = this.el.wrap().wrap();
12106     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12107     
12108     
12109     if(typeof(this.tpl) == "string"){
12110         this.tpl = new Roo.Template(this.tpl);
12111     } else {
12112         // support xtype ctors..
12113         this.tpl = new Roo.factory(this.tpl, Roo);
12114     }
12115     
12116     
12117     this.tpl.compile();
12118     
12119     /** @private */
12120     this.addEvents({
12121         /**
12122          * @event beforeclick
12123          * Fires before a click is processed. Returns false to cancel the default action.
12124          * @param {Roo.View} this
12125          * @param {Number} index The index of the target node
12126          * @param {HTMLElement} node The target node
12127          * @param {Roo.EventObject} e The raw event object
12128          */
12129             "beforeclick" : true,
12130         /**
12131          * @event click
12132          * Fires when a template node is clicked.
12133          * @param {Roo.View} this
12134          * @param {Number} index The index of the target node
12135          * @param {HTMLElement} node The target node
12136          * @param {Roo.EventObject} e The raw event object
12137          */
12138             "click" : true,
12139         /**
12140          * @event dblclick
12141          * Fires when a template node is double clicked.
12142          * @param {Roo.View} this
12143          * @param {Number} index The index of the target node
12144          * @param {HTMLElement} node The target node
12145          * @param {Roo.EventObject} e The raw event object
12146          */
12147             "dblclick" : true,
12148         /**
12149          * @event contextmenu
12150          * Fires when a template node is right clicked.
12151          * @param {Roo.View} this
12152          * @param {Number} index The index of the target node
12153          * @param {HTMLElement} node The target node
12154          * @param {Roo.EventObject} e The raw event object
12155          */
12156             "contextmenu" : true,
12157         /**
12158          * @event selectionchange
12159          * Fires when the selected nodes change.
12160          * @param {Roo.View} this
12161          * @param {Array} selections Array of the selected nodes
12162          */
12163             "selectionchange" : true,
12164     
12165         /**
12166          * @event beforeselect
12167          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12168          * @param {Roo.View} this
12169          * @param {HTMLElement} node The node to be selected
12170          * @param {Array} selections Array of currently selected nodes
12171          */
12172             "beforeselect" : true,
12173         /**
12174          * @event preparedata
12175          * Fires on every row to render, to allow you to change the data.
12176          * @param {Roo.View} this
12177          * @param {Object} data to be rendered (change this)
12178          */
12179           "preparedata" : true
12180           
12181           
12182         });
12183
12184
12185
12186     this.el.on({
12187         "click": this.onClick,
12188         "dblclick": this.onDblClick,
12189         "contextmenu": this.onContextMenu,
12190         scope:this
12191     });
12192
12193     this.selections = [];
12194     this.nodes = [];
12195     this.cmp = new Roo.CompositeElementLite([]);
12196     if(this.store){
12197         this.store = Roo.factory(this.store, Roo.data);
12198         this.setStore(this.store, true);
12199     }
12200     
12201     if ( this.footer && this.footer.xtype) {
12202            
12203          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12204         
12205         this.footer.dataSource = this.store
12206         this.footer.container = fctr;
12207         this.footer = Roo.factory(this.footer, Roo);
12208         fctr.insertFirst(this.el);
12209         
12210         // this is a bit insane - as the paging toolbar seems to detach the el..
12211 //        dom.parentNode.parentNode.parentNode
12212          // they get detached?
12213     }
12214     
12215     
12216     Roo.View.superclass.constructor.call(this);
12217     
12218     
12219 };
12220
12221 Roo.extend(Roo.View, Roo.util.Observable, {
12222     
12223      /**
12224      * @cfg {Roo.data.Store} store Data store to load data from.
12225      */
12226     store : false,
12227     
12228     /**
12229      * @cfg {String|Roo.Element} el The container element.
12230      */
12231     el : '',
12232     
12233     /**
12234      * @cfg {String|Roo.Template} tpl The template used by this View 
12235      */
12236     tpl : false,
12237     /**
12238      * @cfg {String} dataName the named area of the template to use as the data area
12239      *                          Works with domtemplates roo-name="name"
12240      */
12241     dataName: false,
12242     /**
12243      * @cfg {String} selectedClass The css class to add to selected nodes
12244      */
12245     selectedClass : "x-view-selected",
12246      /**
12247      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12248      */
12249     emptyText : "",
12250     
12251     /**
12252      * @cfg {String} text to display on mask (default Loading)
12253      */
12254     mask : false,
12255     /**
12256      * @cfg {Boolean} multiSelect Allow multiple selection
12257      */
12258     multiSelect : false,
12259     /**
12260      * @cfg {Boolean} singleSelect Allow single selection
12261      */
12262     singleSelect:  false,
12263     
12264     /**
12265      * @cfg {Boolean} toggleSelect - selecting 
12266      */
12267     toggleSelect : false,
12268     
12269     /**
12270      * @cfg {Boolean} tickable - selecting 
12271      */
12272     tickable : false,
12273     
12274     /**
12275      * Returns the element this view is bound to.
12276      * @return {Roo.Element}
12277      */
12278     getEl : function(){
12279         return this.wrapEl;
12280     },
12281     
12282     
12283
12284     /**
12285      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12286      */
12287     refresh : function(){
12288         Roo.log('refresh');
12289         var t = this.tpl;
12290         
12291         // if we are using something like 'domtemplate', then
12292         // the what gets used is:
12293         // t.applySubtemplate(NAME, data, wrapping data..)
12294         // the outer template then get' applied with
12295         //     the store 'extra data'
12296         // and the body get's added to the
12297         //      roo-name="data" node?
12298         //      <span class='roo-tpl-{name}'></span> ?????
12299         
12300         
12301         
12302         this.clearSelections();
12303         this.el.update("");
12304         var html = [];
12305         var records = this.store.getRange();
12306         if(records.length < 1) {
12307             
12308             // is this valid??  = should it render a template??
12309             
12310             this.el.update(this.emptyText);
12311             return;
12312         }
12313         var el = this.el;
12314         if (this.dataName) {
12315             this.el.update(t.apply(this.store.meta)); //????
12316             el = this.el.child('.roo-tpl-' + this.dataName);
12317         }
12318         
12319         for(var i = 0, len = records.length; i < len; i++){
12320             var data = this.prepareData(records[i].data, i, records[i]);
12321             this.fireEvent("preparedata", this, data, i, records[i]);
12322             
12323             var d = Roo.apply({}, data);
12324             
12325             if(this.tickable){
12326                 Roo.apply(d, {'roo-id' : Roo.id()});
12327                 
12328                 var _this = this;
12329             
12330                 Roo.each(this.parent.item, function(item){
12331                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12332                         return;
12333                     }
12334                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12335                 });
12336             }
12337             
12338             html[html.length] = Roo.util.Format.trim(
12339                 this.dataName ?
12340                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12341                     t.apply(d)
12342             );
12343         }
12344         
12345         
12346         
12347         el.update(html.join(""));
12348         this.nodes = el.dom.childNodes;
12349         this.updateIndexes(0);
12350     },
12351     
12352
12353     /**
12354      * Function to override to reformat the data that is sent to
12355      * the template for each node.
12356      * DEPRICATED - use the preparedata event handler.
12357      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12358      * a JSON object for an UpdateManager bound view).
12359      */
12360     prepareData : function(data, index, record)
12361     {
12362         this.fireEvent("preparedata", this, data, index, record);
12363         return data;
12364     },
12365
12366     onUpdate : function(ds, record){
12367          Roo.log('on update');   
12368         this.clearSelections();
12369         var index = this.store.indexOf(record);
12370         var n = this.nodes[index];
12371         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12372         n.parentNode.removeChild(n);
12373         this.updateIndexes(index, index);
12374     },
12375
12376     
12377     
12378 // --------- FIXME     
12379     onAdd : function(ds, records, index)
12380     {
12381         Roo.log(['on Add', ds, records, index] );        
12382         this.clearSelections();
12383         if(this.nodes.length == 0){
12384             this.refresh();
12385             return;
12386         }
12387         var n = this.nodes[index];
12388         for(var i = 0, len = records.length; i < len; i++){
12389             var d = this.prepareData(records[i].data, i, records[i]);
12390             if(n){
12391                 this.tpl.insertBefore(n, d);
12392             }else{
12393                 
12394                 this.tpl.append(this.el, d);
12395             }
12396         }
12397         this.updateIndexes(index);
12398     },
12399
12400     onRemove : function(ds, record, index){
12401         Roo.log('onRemove');
12402         this.clearSelections();
12403         var el = this.dataName  ?
12404             this.el.child('.roo-tpl-' + this.dataName) :
12405             this.el; 
12406         
12407         el.dom.removeChild(this.nodes[index]);
12408         this.updateIndexes(index);
12409     },
12410
12411     /**
12412      * Refresh an individual node.
12413      * @param {Number} index
12414      */
12415     refreshNode : function(index){
12416         this.onUpdate(this.store, this.store.getAt(index));
12417     },
12418
12419     updateIndexes : function(startIndex, endIndex){
12420         var ns = this.nodes;
12421         startIndex = startIndex || 0;
12422         endIndex = endIndex || ns.length - 1;
12423         for(var i = startIndex; i <= endIndex; i++){
12424             ns[i].nodeIndex = i;
12425         }
12426     },
12427
12428     /**
12429      * Changes the data store this view uses and refresh the view.
12430      * @param {Store} store
12431      */
12432     setStore : function(store, initial){
12433         if(!initial && this.store){
12434             this.store.un("datachanged", this.refresh);
12435             this.store.un("add", this.onAdd);
12436             this.store.un("remove", this.onRemove);
12437             this.store.un("update", this.onUpdate);
12438             this.store.un("clear", this.refresh);
12439             this.store.un("beforeload", this.onBeforeLoad);
12440             this.store.un("load", this.onLoad);
12441             this.store.un("loadexception", this.onLoad);
12442         }
12443         if(store){
12444           
12445             store.on("datachanged", this.refresh, this);
12446             store.on("add", this.onAdd, this);
12447             store.on("remove", this.onRemove, this);
12448             store.on("update", this.onUpdate, this);
12449             store.on("clear", this.refresh, this);
12450             store.on("beforeload", this.onBeforeLoad, this);
12451             store.on("load", this.onLoad, this);
12452             store.on("loadexception", this.onLoad, this);
12453         }
12454         
12455         if(store){
12456             this.refresh();
12457         }
12458     },
12459     /**
12460      * onbeforeLoad - masks the loading area.
12461      *
12462      */
12463     onBeforeLoad : function(store,opts)
12464     {
12465          Roo.log('onBeforeLoad');   
12466         if (!opts.add) {
12467             this.el.update("");
12468         }
12469         this.el.mask(this.mask ? this.mask : "Loading" ); 
12470     },
12471     onLoad : function ()
12472     {
12473         this.el.unmask();
12474     },
12475     
12476
12477     /**
12478      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12479      * @param {HTMLElement} node
12480      * @return {HTMLElement} The template node
12481      */
12482     findItemFromChild : function(node){
12483         var el = this.dataName  ?
12484             this.el.child('.roo-tpl-' + this.dataName,true) :
12485             this.el.dom; 
12486         
12487         if(!node || node.parentNode == el){
12488                     return node;
12489             }
12490             var p = node.parentNode;
12491             while(p && p != el){
12492             if(p.parentNode == el){
12493                 return p;
12494             }
12495             p = p.parentNode;
12496         }
12497             return null;
12498     },
12499
12500     /** @ignore */
12501     onClick : function(e){
12502         var item = this.findItemFromChild(e.getTarget());
12503         if(item){
12504             var index = this.indexOf(item);
12505             if(this.onItemClick(item, index, e) !== false){
12506                 this.fireEvent("click", this, index, item, e);
12507             }
12508         }else{
12509             this.clearSelections();
12510         }
12511     },
12512
12513     /** @ignore */
12514     onContextMenu : function(e){
12515         var item = this.findItemFromChild(e.getTarget());
12516         if(item){
12517             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12518         }
12519     },
12520
12521     /** @ignore */
12522     onDblClick : function(e){
12523         var item = this.findItemFromChild(e.getTarget());
12524         if(item){
12525             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12526         }
12527     },
12528
12529     onItemClick : function(item, index, e)
12530     {
12531         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12532             return false;
12533         }
12534         if (this.toggleSelect) {
12535             var m = this.isSelected(item) ? 'unselect' : 'select';
12536             Roo.log(m);
12537             var _t = this;
12538             _t[m](item, true, false);
12539             return true;
12540         }
12541         if(this.multiSelect || this.singleSelect){
12542             if(this.multiSelect && e.shiftKey && this.lastSelection){
12543                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12544             }else{
12545                 this.select(item, this.multiSelect && e.ctrlKey);
12546                 this.lastSelection = item;
12547             }
12548             
12549             if(!this.tickable){
12550                 e.preventDefault();
12551             }
12552             
12553         }
12554         return true;
12555     },
12556
12557     /**
12558      * Get the number of selected nodes.
12559      * @return {Number}
12560      */
12561     getSelectionCount : function(){
12562         return this.selections.length;
12563     },
12564
12565     /**
12566      * Get the currently selected nodes.
12567      * @return {Array} An array of HTMLElements
12568      */
12569     getSelectedNodes : function(){
12570         return this.selections;
12571     },
12572
12573     /**
12574      * Get the indexes of the selected nodes.
12575      * @return {Array}
12576      */
12577     getSelectedIndexes : function(){
12578         var indexes = [], s = this.selections;
12579         for(var i = 0, len = s.length; i < len; i++){
12580             indexes.push(s[i].nodeIndex);
12581         }
12582         return indexes;
12583     },
12584
12585     /**
12586      * Clear all selections
12587      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12588      */
12589     clearSelections : function(suppressEvent){
12590         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12591             this.cmp.elements = this.selections;
12592             this.cmp.removeClass(this.selectedClass);
12593             this.selections = [];
12594             if(!suppressEvent){
12595                 this.fireEvent("selectionchange", this, this.selections);
12596             }
12597         }
12598     },
12599
12600     /**
12601      * Returns true if the passed node is selected
12602      * @param {HTMLElement/Number} node The node or node index
12603      * @return {Boolean}
12604      */
12605     isSelected : function(node){
12606         var s = this.selections;
12607         if(s.length < 1){
12608             return false;
12609         }
12610         node = this.getNode(node);
12611         return s.indexOf(node) !== -1;
12612     },
12613
12614     /**
12615      * Selects nodes.
12616      * @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
12617      * @param {Boolean} keepExisting (optional) true to keep existing selections
12618      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12619      */
12620     select : function(nodeInfo, keepExisting, suppressEvent){
12621         if(nodeInfo instanceof Array){
12622             if(!keepExisting){
12623                 this.clearSelections(true);
12624             }
12625             for(var i = 0, len = nodeInfo.length; i < len; i++){
12626                 this.select(nodeInfo[i], true, true);
12627             }
12628             return;
12629         } 
12630         var node = this.getNode(nodeInfo);
12631         if(!node || this.isSelected(node)){
12632             return; // already selected.
12633         }
12634         if(!keepExisting){
12635             this.clearSelections(true);
12636         }
12637         
12638         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12639             Roo.fly(node).addClass(this.selectedClass);
12640             this.selections.push(node);
12641             if(!suppressEvent){
12642                 this.fireEvent("selectionchange", this, this.selections);
12643             }
12644         }
12645         
12646         
12647     },
12648       /**
12649      * Unselects nodes.
12650      * @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
12651      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12652      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12653      */
12654     unselect : function(nodeInfo, keepExisting, suppressEvent)
12655     {
12656         if(nodeInfo instanceof Array){
12657             Roo.each(this.selections, function(s) {
12658                 this.unselect(s, nodeInfo);
12659             }, this);
12660             return;
12661         }
12662         var node = this.getNode(nodeInfo);
12663         if(!node || !this.isSelected(node)){
12664             Roo.log("not selected");
12665             return; // not selected.
12666         }
12667         // fireevent???
12668         var ns = [];
12669         Roo.each(this.selections, function(s) {
12670             if (s == node ) {
12671                 Roo.fly(node).removeClass(this.selectedClass);
12672
12673                 return;
12674             }
12675             ns.push(s);
12676         },this);
12677         
12678         this.selections= ns;
12679         this.fireEvent("selectionchange", this, this.selections);
12680     },
12681
12682     /**
12683      * Gets a template node.
12684      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12685      * @return {HTMLElement} The node or null if it wasn't found
12686      */
12687     getNode : function(nodeInfo){
12688         if(typeof nodeInfo == "string"){
12689             return document.getElementById(nodeInfo);
12690         }else if(typeof nodeInfo == "number"){
12691             return this.nodes[nodeInfo];
12692         }
12693         return nodeInfo;
12694     },
12695
12696     /**
12697      * Gets a range template nodes.
12698      * @param {Number} startIndex
12699      * @param {Number} endIndex
12700      * @return {Array} An array of nodes
12701      */
12702     getNodes : function(start, end){
12703         var ns = this.nodes;
12704         start = start || 0;
12705         end = typeof end == "undefined" ? ns.length - 1 : end;
12706         var nodes = [];
12707         if(start <= end){
12708             for(var i = start; i <= end; i++){
12709                 nodes.push(ns[i]);
12710             }
12711         } else{
12712             for(var i = start; i >= end; i--){
12713                 nodes.push(ns[i]);
12714             }
12715         }
12716         return nodes;
12717     },
12718
12719     /**
12720      * Finds the index of the passed node
12721      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12722      * @return {Number} The index of the node or -1
12723      */
12724     indexOf : function(node){
12725         node = this.getNode(node);
12726         if(typeof node.nodeIndex == "number"){
12727             return node.nodeIndex;
12728         }
12729         var ns = this.nodes;
12730         for(var i = 0, len = ns.length; i < len; i++){
12731             if(ns[i] == node){
12732                 return i;
12733             }
12734         }
12735         return -1;
12736     }
12737 });
12738 /*
12739  * - LGPL
12740  *
12741  * based on jquery fullcalendar
12742  * 
12743  */
12744
12745 Roo.bootstrap = Roo.bootstrap || {};
12746 /**
12747  * @class Roo.bootstrap.Calendar
12748  * @extends Roo.bootstrap.Component
12749  * Bootstrap Calendar class
12750  * @cfg {Boolean} loadMask (true|false) default false
12751  * @cfg {Object} header generate the user specific header of the calendar, default false
12752
12753  * @constructor
12754  * Create a new Container
12755  * @param {Object} config The config object
12756  */
12757
12758
12759
12760 Roo.bootstrap.Calendar = function(config){
12761     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12762      this.addEvents({
12763         /**
12764              * @event select
12765              * Fires when a date is selected
12766              * @param {DatePicker} this
12767              * @param {Date} date The selected date
12768              */
12769         'select': true,
12770         /**
12771              * @event monthchange
12772              * Fires when the displayed month changes 
12773              * @param {DatePicker} this
12774              * @param {Date} date The selected month
12775              */
12776         'monthchange': true,
12777         /**
12778              * @event evententer
12779              * Fires when mouse over an event
12780              * @param {Calendar} this
12781              * @param {event} Event
12782              */
12783         'evententer': true,
12784         /**
12785              * @event eventleave
12786              * Fires when the mouse leaves an
12787              * @param {Calendar} this
12788              * @param {event}
12789              */
12790         'eventleave': true,
12791         /**
12792              * @event eventclick
12793              * Fires when the mouse click an
12794              * @param {Calendar} this
12795              * @param {event}
12796              */
12797         'eventclick': true
12798         
12799     });
12800
12801 };
12802
12803 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12804     
12805      /**
12806      * @cfg {Number} startDay
12807      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12808      */
12809     startDay : 0,
12810     
12811     loadMask : false,
12812     
12813     header : false,
12814       
12815     getAutoCreate : function(){
12816         
12817         
12818         var fc_button = function(name, corner, style, content ) {
12819             return Roo.apply({},{
12820                 tag : 'span',
12821                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12822                          (corner.length ?
12823                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12824                             ''
12825                         ),
12826                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12827                 unselectable: 'on'
12828             });
12829         };
12830         
12831         var header = {};
12832         
12833         if(!this.header){
12834             header = {
12835                 tag : 'table',
12836                 cls : 'fc-header',
12837                 style : 'width:100%',
12838                 cn : [
12839                     {
12840                         tag: 'tr',
12841                         cn : [
12842                             {
12843                                 tag : 'td',
12844                                 cls : 'fc-header-left',
12845                                 cn : [
12846                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12847                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12848                                     { tag: 'span', cls: 'fc-header-space' },
12849                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12850
12851
12852                                 ]
12853                             },
12854
12855                             {
12856                                 tag : 'td',
12857                                 cls : 'fc-header-center',
12858                                 cn : [
12859                                     {
12860                                         tag: 'span',
12861                                         cls: 'fc-header-title',
12862                                         cn : {
12863                                             tag: 'H2',
12864                                             html : 'month / year'
12865                                         }
12866                                     }
12867
12868                                 ]
12869                             },
12870                             {
12871                                 tag : 'td',
12872                                 cls : 'fc-header-right',
12873                                 cn : [
12874                               /*      fc_button('month', 'left', '', 'month' ),
12875                                     fc_button('week', '', '', 'week' ),
12876                                     fc_button('day', 'right', '', 'day' )
12877                                 */    
12878
12879                                 ]
12880                             }
12881
12882                         ]
12883                     }
12884                 ]
12885             };
12886         }
12887         
12888         header = this.header;
12889         
12890        
12891         var cal_heads = function() {
12892             var ret = [];
12893             // fixme - handle this.
12894             
12895             for (var i =0; i < Date.dayNames.length; i++) {
12896                 var d = Date.dayNames[i];
12897                 ret.push({
12898                     tag: 'th',
12899                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12900                     html : d.substring(0,3)
12901                 });
12902                 
12903             }
12904             ret[0].cls += ' fc-first';
12905             ret[6].cls += ' fc-last';
12906             return ret;
12907         };
12908         var cal_cell = function(n) {
12909             return  {
12910                 tag: 'td',
12911                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12912                 cn : [
12913                     {
12914                         cn : [
12915                             {
12916                                 cls: 'fc-day-number',
12917                                 html: 'D'
12918                             },
12919                             {
12920                                 cls: 'fc-day-content',
12921                              
12922                                 cn : [
12923                                      {
12924                                         style: 'position: relative;' // height: 17px;
12925                                     }
12926                                 ]
12927                             }
12928                             
12929                             
12930                         ]
12931                     }
12932                 ]
12933                 
12934             }
12935         };
12936         var cal_rows = function() {
12937             
12938             var ret = []
12939             for (var r = 0; r < 6; r++) {
12940                 var row= {
12941                     tag : 'tr',
12942                     cls : 'fc-week',
12943                     cn : []
12944                 };
12945                 
12946                 for (var i =0; i < Date.dayNames.length; i++) {
12947                     var d = Date.dayNames[i];
12948                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12949
12950                 }
12951                 row.cn[0].cls+=' fc-first';
12952                 row.cn[0].cn[0].style = 'min-height:90px';
12953                 row.cn[6].cls+=' fc-last';
12954                 ret.push(row);
12955                 
12956             }
12957             ret[0].cls += ' fc-first';
12958             ret[4].cls += ' fc-prev-last';
12959             ret[5].cls += ' fc-last';
12960             return ret;
12961             
12962         };
12963         
12964         var cal_table = {
12965             tag: 'table',
12966             cls: 'fc-border-separate',
12967             style : 'width:100%',
12968             cellspacing  : 0,
12969             cn : [
12970                 { 
12971                     tag: 'thead',
12972                     cn : [
12973                         { 
12974                             tag: 'tr',
12975                             cls : 'fc-first fc-last',
12976                             cn : cal_heads()
12977                         }
12978                     ]
12979                 },
12980                 { 
12981                     tag: 'tbody',
12982                     cn : cal_rows()
12983                 }
12984                   
12985             ]
12986         };
12987          
12988          var cfg = {
12989             cls : 'fc fc-ltr',
12990             cn : [
12991                 header,
12992                 {
12993                     cls : 'fc-content',
12994                     style : "position: relative;",
12995                     cn : [
12996                         {
12997                             cls : 'fc-view fc-view-month fc-grid',
12998                             style : 'position: relative',
12999                             unselectable : 'on',
13000                             cn : [
13001                                 {
13002                                     cls : 'fc-event-container',
13003                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13004                                 },
13005                                 cal_table
13006                             ]
13007                         }
13008                     ]
13009     
13010                 }
13011            ] 
13012             
13013         };
13014         
13015          
13016         
13017         return cfg;
13018     },
13019     
13020     
13021     initEvents : function()
13022     {
13023         if(!this.store){
13024             throw "can not find store for calendar";
13025         }
13026         
13027         var mark = {
13028             tag: "div",
13029             cls:"x-dlg-mask",
13030             style: "text-align:center",
13031             cn: [
13032                 {
13033                     tag: "div",
13034                     style: "background-color:white;width:50%;margin:250 auto",
13035                     cn: [
13036                         {
13037                             tag: "img",
13038                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13039                         },
13040                         {
13041                             tag: "span",
13042                             html: "Loading"
13043                         }
13044                         
13045                     ]
13046                 }
13047             ]
13048         }
13049         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13050         
13051         var size = this.el.select('.fc-content', true).first().getSize();
13052         this.maskEl.setSize(size.width, size.height);
13053         this.maskEl.enableDisplayMode("block");
13054         if(!this.loadMask){
13055             this.maskEl.hide();
13056         }
13057         
13058         this.store = Roo.factory(this.store, Roo.data);
13059         this.store.on('load', this.onLoad, this);
13060         this.store.on('beforeload', this.onBeforeLoad, this);
13061         
13062         this.resize();
13063         
13064         this.cells = this.el.select('.fc-day',true);
13065         //Roo.log(this.cells);
13066         this.textNodes = this.el.query('.fc-day-number');
13067         this.cells.addClassOnOver('fc-state-hover');
13068         
13069         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13070         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13071         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13072         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13073         
13074         this.on('monthchange', this.onMonthChange, this);
13075         
13076         this.update(new Date().clearTime());
13077     },
13078     
13079     resize : function() {
13080         var sz  = this.el.getSize();
13081         
13082         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13083         this.el.select('.fc-day-content div',true).setHeight(34);
13084     },
13085     
13086     
13087     // private
13088     showPrevMonth : function(e){
13089         this.update(this.activeDate.add("mo", -1));
13090     },
13091     showToday : function(e){
13092         this.update(new Date().clearTime());
13093     },
13094     // private
13095     showNextMonth : function(e){
13096         this.update(this.activeDate.add("mo", 1));
13097     },
13098
13099     // private
13100     showPrevYear : function(){
13101         this.update(this.activeDate.add("y", -1));
13102     },
13103
13104     // private
13105     showNextYear : function(){
13106         this.update(this.activeDate.add("y", 1));
13107     },
13108
13109     
13110    // private
13111     update : function(date)
13112     {
13113         var vd = this.activeDate;
13114         this.activeDate = date;
13115 //        if(vd && this.el){
13116 //            var t = date.getTime();
13117 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13118 //                Roo.log('using add remove');
13119 //                
13120 //                this.fireEvent('monthchange', this, date);
13121 //                
13122 //                this.cells.removeClass("fc-state-highlight");
13123 //                this.cells.each(function(c){
13124 //                   if(c.dateValue == t){
13125 //                       c.addClass("fc-state-highlight");
13126 //                       setTimeout(function(){
13127 //                            try{c.dom.firstChild.focus();}catch(e){}
13128 //                       }, 50);
13129 //                       return false;
13130 //                   }
13131 //                   return true;
13132 //                });
13133 //                return;
13134 //            }
13135 //        }
13136         
13137         var days = date.getDaysInMonth();
13138         
13139         var firstOfMonth = date.getFirstDateOfMonth();
13140         var startingPos = firstOfMonth.getDay()-this.startDay;
13141         
13142         if(startingPos < this.startDay){
13143             startingPos += 7;
13144         }
13145         
13146         var pm = date.add(Date.MONTH, -1);
13147         var prevStart = pm.getDaysInMonth()-startingPos;
13148 //        
13149         this.cells = this.el.select('.fc-day',true);
13150         this.textNodes = this.el.query('.fc-day-number');
13151         this.cells.addClassOnOver('fc-state-hover');
13152         
13153         var cells = this.cells.elements;
13154         var textEls = this.textNodes;
13155         
13156         Roo.each(cells, function(cell){
13157             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13158         });
13159         
13160         days += startingPos;
13161
13162         // convert everything to numbers so it's fast
13163         var day = 86400000;
13164         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13165         //Roo.log(d);
13166         //Roo.log(pm);
13167         //Roo.log(prevStart);
13168         
13169         var today = new Date().clearTime().getTime();
13170         var sel = date.clearTime().getTime();
13171         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13172         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13173         var ddMatch = this.disabledDatesRE;
13174         var ddText = this.disabledDatesText;
13175         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13176         var ddaysText = this.disabledDaysText;
13177         var format = this.format;
13178         
13179         var setCellClass = function(cal, cell){
13180             cell.row = 0;
13181             cell.events = [];
13182             cell.more = [];
13183             //Roo.log('set Cell Class');
13184             cell.title = "";
13185             var t = d.getTime();
13186             
13187             //Roo.log(d);
13188             
13189             cell.dateValue = t;
13190             if(t == today){
13191                 cell.className += " fc-today";
13192                 cell.className += " fc-state-highlight";
13193                 cell.title = cal.todayText;
13194             }
13195             if(t == sel){
13196                 // disable highlight in other month..
13197                 //cell.className += " fc-state-highlight";
13198                 
13199             }
13200             // disabling
13201             if(t < min) {
13202                 cell.className = " fc-state-disabled";
13203                 cell.title = cal.minText;
13204                 return;
13205             }
13206             if(t > max) {
13207                 cell.className = " fc-state-disabled";
13208                 cell.title = cal.maxText;
13209                 return;
13210             }
13211             if(ddays){
13212                 if(ddays.indexOf(d.getDay()) != -1){
13213                     cell.title = ddaysText;
13214                     cell.className = " fc-state-disabled";
13215                 }
13216             }
13217             if(ddMatch && format){
13218                 var fvalue = d.dateFormat(format);
13219                 if(ddMatch.test(fvalue)){
13220                     cell.title = ddText.replace("%0", fvalue);
13221                     cell.className = " fc-state-disabled";
13222                 }
13223             }
13224             
13225             if (!cell.initialClassName) {
13226                 cell.initialClassName = cell.dom.className;
13227             }
13228             
13229             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13230         };
13231
13232         var i = 0;
13233         
13234         for(; i < startingPos; i++) {
13235             textEls[i].innerHTML = (++prevStart);
13236             d.setDate(d.getDate()+1);
13237             
13238             cells[i].className = "fc-past fc-other-month";
13239             setCellClass(this, cells[i]);
13240         }
13241         
13242         var intDay = 0;
13243         
13244         for(; i < days; i++){
13245             intDay = i - startingPos + 1;
13246             textEls[i].innerHTML = (intDay);
13247             d.setDate(d.getDate()+1);
13248             
13249             cells[i].className = ''; // "x-date-active";
13250             setCellClass(this, cells[i]);
13251         }
13252         var extraDays = 0;
13253         
13254         for(; i < 42; i++) {
13255             textEls[i].innerHTML = (++extraDays);
13256             d.setDate(d.getDate()+1);
13257             
13258             cells[i].className = "fc-future fc-other-month";
13259             setCellClass(this, cells[i]);
13260         }
13261         
13262         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13263         
13264         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13265         
13266         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13267         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13268         
13269         if(totalRows != 6){
13270             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13271             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13272         }
13273         
13274         this.fireEvent('monthchange', this, date);
13275         
13276         
13277         /*
13278         if(!this.internalRender){
13279             var main = this.el.dom.firstChild;
13280             var w = main.offsetWidth;
13281             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13282             Roo.fly(main).setWidth(w);
13283             this.internalRender = true;
13284             // opera does not respect the auto grow header center column
13285             // then, after it gets a width opera refuses to recalculate
13286             // without a second pass
13287             if(Roo.isOpera && !this.secondPass){
13288                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13289                 this.secondPass = true;
13290                 this.update.defer(10, this, [date]);
13291             }
13292         }
13293         */
13294         
13295     },
13296     
13297     findCell : function(dt) {
13298         dt = dt.clearTime().getTime();
13299         var ret = false;
13300         this.cells.each(function(c){
13301             //Roo.log("check " +c.dateValue + '?=' + dt);
13302             if(c.dateValue == dt){
13303                 ret = c;
13304                 return false;
13305             }
13306             return true;
13307         });
13308         
13309         return ret;
13310     },
13311     
13312     findCells : function(ev) {
13313         var s = ev.start.clone().clearTime().getTime();
13314        // Roo.log(s);
13315         var e= ev.end.clone().clearTime().getTime();
13316        // Roo.log(e);
13317         var ret = [];
13318         this.cells.each(function(c){
13319              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13320             
13321             if(c.dateValue > e){
13322                 return ;
13323             }
13324             if(c.dateValue < s){
13325                 return ;
13326             }
13327             ret.push(c);
13328         });
13329         
13330         return ret;    
13331     },
13332     
13333 //    findBestRow: function(cells)
13334 //    {
13335 //        var ret = 0;
13336 //        
13337 //        for (var i =0 ; i < cells.length;i++) {
13338 //            ret  = Math.max(cells[i].rows || 0,ret);
13339 //        }
13340 //        return ret;
13341 //        
13342 //    },
13343     
13344     
13345     addItem : function(ev)
13346     {
13347         // look for vertical location slot in
13348         var cells = this.findCells(ev);
13349         
13350 //        ev.row = this.findBestRow(cells);
13351         
13352         // work out the location.
13353         
13354         var crow = false;
13355         var rows = [];
13356         for(var i =0; i < cells.length; i++) {
13357             
13358             cells[i].row = cells[0].row;
13359             
13360             if(i == 0){
13361                 cells[i].row = cells[i].row + 1;
13362             }
13363             
13364             if (!crow) {
13365                 crow = {
13366                     start : cells[i],
13367                     end :  cells[i]
13368                 };
13369                 continue;
13370             }
13371             if (crow.start.getY() == cells[i].getY()) {
13372                 // on same row.
13373                 crow.end = cells[i];
13374                 continue;
13375             }
13376             // different row.
13377             rows.push(crow);
13378             crow = {
13379                 start: cells[i],
13380                 end : cells[i]
13381             };
13382             
13383         }
13384         
13385         rows.push(crow);
13386         ev.els = [];
13387         ev.rows = rows;
13388         ev.cells = cells;
13389         
13390         cells[0].events.push(ev);
13391         
13392         this.calevents.push(ev);
13393     },
13394     
13395     clearEvents: function() {
13396         
13397         if(!this.calevents){
13398             return;
13399         }
13400         
13401         Roo.each(this.cells.elements, function(c){
13402             c.row = 0;
13403             c.events = [];
13404             c.more = [];
13405         });
13406         
13407         Roo.each(this.calevents, function(e) {
13408             Roo.each(e.els, function(el) {
13409                 el.un('mouseenter' ,this.onEventEnter, this);
13410                 el.un('mouseleave' ,this.onEventLeave, this);
13411                 el.remove();
13412             },this);
13413         },this);
13414         
13415         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13416             e.remove();
13417         });
13418         
13419     },
13420     
13421     renderEvents: function()
13422     {   
13423         var _this = this;
13424         
13425         this.cells.each(function(c) {
13426             
13427             if(c.row < 5){
13428                 return;
13429             }
13430             
13431             var ev = c.events;
13432             
13433             var r = 4;
13434             if(c.row != c.events.length){
13435                 r = 4 - (4 - (c.row - c.events.length));
13436             }
13437             
13438             c.events = ev.slice(0, r);
13439             c.more = ev.slice(r);
13440             
13441             if(c.more.length && c.more.length == 1){
13442                 c.events.push(c.more.pop());
13443             }
13444             
13445             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13446             
13447         });
13448             
13449         this.cells.each(function(c) {
13450             
13451             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13452             
13453             
13454             for (var e = 0; e < c.events.length; e++){
13455                 var ev = c.events[e];
13456                 var rows = ev.rows;
13457                 
13458                 for(var i = 0; i < rows.length; i++) {
13459                 
13460                     // how many rows should it span..
13461
13462                     var  cfg = {
13463                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13464                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13465
13466                         unselectable : "on",
13467                         cn : [
13468                             {
13469                                 cls: 'fc-event-inner',
13470                                 cn : [
13471     //                                {
13472     //                                  tag:'span',
13473     //                                  cls: 'fc-event-time',
13474     //                                  html : cells.length > 1 ? '' : ev.time
13475     //                                },
13476                                     {
13477                                       tag:'span',
13478                                       cls: 'fc-event-title',
13479                                       html : String.format('{0}', ev.title)
13480                                     }
13481
13482
13483                                 ]
13484                             },
13485                             {
13486                                 cls: 'ui-resizable-handle ui-resizable-e',
13487                                 html : '&nbsp;&nbsp;&nbsp'
13488                             }
13489
13490                         ]
13491                     };
13492
13493                     if (i == 0) {
13494                         cfg.cls += ' fc-event-start';
13495                     }
13496                     if ((i+1) == rows.length) {
13497                         cfg.cls += ' fc-event-end';
13498                     }
13499
13500                     var ctr = _this.el.select('.fc-event-container',true).first();
13501                     var cg = ctr.createChild(cfg);
13502
13503                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13504                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13505
13506                     var r = (c.more.length) ? 1 : 0;
13507                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13508                     cg.setWidth(ebox.right - sbox.x -2);
13509
13510                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13511                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13512                     cg.on('click', _this.onEventClick, _this, ev);
13513
13514                     ev.els.push(cg);
13515                     
13516                 }
13517                 
13518             }
13519             
13520             
13521             if(c.more.length){
13522                 var  cfg = {
13523                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13524                     style : 'position: absolute',
13525                     unselectable : "on",
13526                     cn : [
13527                         {
13528                             cls: 'fc-event-inner',
13529                             cn : [
13530                                 {
13531                                   tag:'span',
13532                                   cls: 'fc-event-title',
13533                                   html : 'More'
13534                                 }
13535
13536
13537                             ]
13538                         },
13539                         {
13540                             cls: 'ui-resizable-handle ui-resizable-e',
13541                             html : '&nbsp;&nbsp;&nbsp'
13542                         }
13543
13544                     ]
13545                 };
13546
13547                 var ctr = _this.el.select('.fc-event-container',true).first();
13548                 var cg = ctr.createChild(cfg);
13549
13550                 var sbox = c.select('.fc-day-content',true).first().getBox();
13551                 var ebox = c.select('.fc-day-content',true).first().getBox();
13552                 //Roo.log(cg);
13553                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13554                 cg.setWidth(ebox.right - sbox.x -2);
13555
13556                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13557                 
13558             }
13559             
13560         });
13561         
13562         
13563         
13564     },
13565     
13566     onEventEnter: function (e, el,event,d) {
13567         this.fireEvent('evententer', this, el, event);
13568     },
13569     
13570     onEventLeave: function (e, el,event,d) {
13571         this.fireEvent('eventleave', this, el, event);
13572     },
13573     
13574     onEventClick: function (e, el,event,d) {
13575         this.fireEvent('eventclick', this, el, event);
13576     },
13577     
13578     onMonthChange: function () {
13579         this.store.load();
13580     },
13581     
13582     onMoreEventClick: function(e, el, more)
13583     {
13584         var _this = this;
13585         
13586         this.calpopover.placement = 'right';
13587         this.calpopover.setTitle('More');
13588         
13589         this.calpopover.setContent('');
13590         
13591         var ctr = this.calpopover.el.select('.popover-content', true).first();
13592         
13593         Roo.each(more, function(m){
13594             var cfg = {
13595                 cls : 'fc-event-hori fc-event-draggable',
13596                 html : m.title
13597             }
13598             var cg = ctr.createChild(cfg);
13599             
13600             cg.on('click', _this.onEventClick, _this, m);
13601         });
13602         
13603         this.calpopover.show(el);
13604         
13605         
13606     },
13607     
13608     onLoad: function () 
13609     {   
13610         this.calevents = [];
13611         var cal = this;
13612         
13613         if(this.store.getCount() > 0){
13614             this.store.data.each(function(d){
13615                cal.addItem({
13616                     id : d.data.id,
13617                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13618                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13619                     time : d.data.start_time,
13620                     title : d.data.title,
13621                     description : d.data.description,
13622                     venue : d.data.venue
13623                 });
13624             });
13625         }
13626         
13627         this.renderEvents();
13628         
13629         if(this.calevents.length && this.loadMask){
13630             this.maskEl.hide();
13631         }
13632     },
13633     
13634     onBeforeLoad: function()
13635     {
13636         this.clearEvents();
13637         if(this.loadMask){
13638             this.maskEl.show();
13639         }
13640     }
13641 });
13642
13643  
13644  /*
13645  * - LGPL
13646  *
13647  * element
13648  * 
13649  */
13650
13651 /**
13652  * @class Roo.bootstrap.Popover
13653  * @extends Roo.bootstrap.Component
13654  * Bootstrap Popover class
13655  * @cfg {String} html contents of the popover   (or false to use children..)
13656  * @cfg {String} title of popover (or false to hide)
13657  * @cfg {String} placement how it is placed
13658  * @cfg {String} trigger click || hover (or false to trigger manually)
13659  * @cfg {String} over what (parent or false to trigger manually.)
13660  * @cfg {Number} delay - delay before showing
13661  
13662  * @constructor
13663  * Create a new Popover
13664  * @param {Object} config The config object
13665  */
13666
13667 Roo.bootstrap.Popover = function(config){
13668     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13669 };
13670
13671 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13672     
13673     title: 'Fill in a title',
13674     html: false,
13675     
13676     placement : 'right',
13677     trigger : 'hover', // hover
13678     
13679     delay : 0,
13680     
13681     over: 'parent',
13682     
13683     can_build_overlaid : false,
13684     
13685     getChildContainer : function()
13686     {
13687         return this.el.select('.popover-content',true).first();
13688     },
13689     
13690     getAutoCreate : function(){
13691          Roo.log('make popover?');
13692         var cfg = {
13693            cls : 'popover roo-dynamic',
13694            style: 'display:block',
13695            cn : [
13696                 {
13697                     cls : 'arrow'
13698                 },
13699                 {
13700                     cls : 'popover-inner',
13701                     cn : [
13702                         {
13703                             tag: 'h3',
13704                             cls: 'popover-title',
13705                             html : this.title
13706                         },
13707                         {
13708                             cls : 'popover-content',
13709                             html : this.html
13710                         }
13711                     ]
13712                     
13713                 }
13714            ]
13715         };
13716         
13717         return cfg;
13718     },
13719     setTitle: function(str)
13720     {
13721         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13722     },
13723     setContent: function(str)
13724     {
13725         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13726     },
13727     // as it get's added to the bottom of the page.
13728     onRender : function(ct, position)
13729     {
13730         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13731         if(!this.el){
13732             var cfg = Roo.apply({},  this.getAutoCreate());
13733             cfg.id = Roo.id();
13734             
13735             if (this.cls) {
13736                 cfg.cls += ' ' + this.cls;
13737             }
13738             if (this.style) {
13739                 cfg.style = this.style;
13740             }
13741             Roo.log("adding to ")
13742             this.el = Roo.get(document.body).createChild(cfg, position);
13743             Roo.log(this.el);
13744         }
13745         this.initEvents();
13746     },
13747     
13748     initEvents : function()
13749     {
13750         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13751         this.el.enableDisplayMode('block');
13752         this.el.hide();
13753         if (this.over === false) {
13754             return; 
13755         }
13756         if (this.triggers === false) {
13757             return;
13758         }
13759         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13760         var triggers = this.trigger ? this.trigger.split(' ') : [];
13761         Roo.each(triggers, function(trigger) {
13762         
13763             if (trigger == 'click') {
13764                 on_el.on('click', this.toggle, this);
13765             } else if (trigger != 'manual') {
13766                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13767                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13768       
13769                 on_el.on(eventIn  ,this.enter, this);
13770                 on_el.on(eventOut, this.leave, this);
13771             }
13772         }, this);
13773         
13774     },
13775     
13776     
13777     // private
13778     timeout : null,
13779     hoverState : null,
13780     
13781     toggle : function () {
13782         this.hoverState == 'in' ? this.leave() : this.enter();
13783     },
13784     
13785     enter : function () {
13786        
13787     
13788         clearTimeout(this.timeout);
13789     
13790         this.hoverState = 'in'
13791     
13792         if (!this.delay || !this.delay.show) {
13793             this.show();
13794             return 
13795         }
13796         var _t = this;
13797         this.timeout = setTimeout(function () {
13798             if (_t.hoverState == 'in') {
13799                 _t.show();
13800             }
13801         }, this.delay.show)
13802     },
13803     leave : function() {
13804         clearTimeout(this.timeout);
13805     
13806         this.hoverState = 'out'
13807     
13808         if (!this.delay || !this.delay.hide) {
13809             this.hide();
13810             return 
13811         }
13812         var _t = this;
13813         this.timeout = setTimeout(function () {
13814             if (_t.hoverState == 'out') {
13815                 _t.hide();
13816             }
13817         }, this.delay.hide)
13818     },
13819     
13820     show : function (on_el)
13821     {
13822         if (!on_el) {
13823             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13824         }
13825         // set content.
13826         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13827         if (this.html !== false) {
13828             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13829         }
13830         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13831         if (!this.title.length) {
13832             this.el.select('.popover-title',true).hide();
13833         }
13834         
13835         var placement = typeof this.placement == 'function' ?
13836             this.placement.call(this, this.el, on_el) :
13837             this.placement;
13838             
13839         var autoToken = /\s?auto?\s?/i;
13840         var autoPlace = autoToken.test(placement);
13841         if (autoPlace) {
13842             placement = placement.replace(autoToken, '') || 'top';
13843         }
13844         
13845         //this.el.detach()
13846         //this.el.setXY([0,0]);
13847         this.el.show();
13848         this.el.dom.style.display='block';
13849         this.el.addClass(placement);
13850         
13851         //this.el.appendTo(on_el);
13852         
13853         var p = this.getPosition();
13854         var box = this.el.getBox();
13855         
13856         if (autoPlace) {
13857             // fixme..
13858         }
13859         var align = Roo.bootstrap.Popover.alignment[placement]
13860         this.el.alignTo(on_el, align[0],align[1]);
13861         //var arrow = this.el.select('.arrow',true).first();
13862         //arrow.set(align[2], 
13863         
13864         this.el.addClass('in');
13865         this.hoverState = null;
13866         
13867         if (this.el.hasClass('fade')) {
13868             // fade it?
13869         }
13870         
13871     },
13872     hide : function()
13873     {
13874         this.el.setXY([0,0]);
13875         this.el.removeClass('in');
13876         this.el.hide();
13877         
13878     }
13879     
13880 });
13881
13882 Roo.bootstrap.Popover.alignment = {
13883     'left' : ['r-l', [-10,0], 'right'],
13884     'right' : ['l-r', [10,0], 'left'],
13885     'bottom' : ['t-b', [0,10], 'top'],
13886     'top' : [ 'b-t', [0,-10], 'bottom']
13887 };
13888
13889  /*
13890  * - LGPL
13891  *
13892  * Progress
13893  * 
13894  */
13895
13896 /**
13897  * @class Roo.bootstrap.Progress
13898  * @extends Roo.bootstrap.Component
13899  * Bootstrap Progress class
13900  * @cfg {Boolean} striped striped of the progress bar
13901  * @cfg {Boolean} active animated of the progress bar
13902  * 
13903  * 
13904  * @constructor
13905  * Create a new Progress
13906  * @param {Object} config The config object
13907  */
13908
13909 Roo.bootstrap.Progress = function(config){
13910     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13911 };
13912
13913 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13914     
13915     striped : false,
13916     active: false,
13917     
13918     getAutoCreate : function(){
13919         var cfg = {
13920             tag: 'div',
13921             cls: 'progress'
13922         };
13923         
13924         
13925         if(this.striped){
13926             cfg.cls += ' progress-striped';
13927         }
13928       
13929         if(this.active){
13930             cfg.cls += ' active';
13931         }
13932         
13933         
13934         return cfg;
13935     }
13936    
13937 });
13938
13939  
13940
13941  /*
13942  * - LGPL
13943  *
13944  * ProgressBar
13945  * 
13946  */
13947
13948 /**
13949  * @class Roo.bootstrap.ProgressBar
13950  * @extends Roo.bootstrap.Component
13951  * Bootstrap ProgressBar class
13952  * @cfg {Number} aria_valuenow aria-value now
13953  * @cfg {Number} aria_valuemin aria-value min
13954  * @cfg {Number} aria_valuemax aria-value max
13955  * @cfg {String} label label for the progress bar
13956  * @cfg {String} panel (success | info | warning | danger )
13957  * @cfg {String} role role of the progress bar
13958  * @cfg {String} sr_only text
13959  * 
13960  * 
13961  * @constructor
13962  * Create a new ProgressBar
13963  * @param {Object} config The config object
13964  */
13965
13966 Roo.bootstrap.ProgressBar = function(config){
13967     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13968 };
13969
13970 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13971     
13972     aria_valuenow : 0,
13973     aria_valuemin : 0,
13974     aria_valuemax : 100,
13975     label : false,
13976     panel : false,
13977     role : false,
13978     sr_only: false,
13979     
13980     getAutoCreate : function()
13981     {
13982         
13983         var cfg = {
13984             tag: 'div',
13985             cls: 'progress-bar',
13986             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13987         };
13988         
13989         if(this.sr_only){
13990             cfg.cn = {
13991                 tag: 'span',
13992                 cls: 'sr-only',
13993                 html: this.sr_only
13994             }
13995         }
13996         
13997         if(this.role){
13998             cfg.role = this.role;
13999         }
14000         
14001         if(this.aria_valuenow){
14002             cfg['aria-valuenow'] = this.aria_valuenow;
14003         }
14004         
14005         if(this.aria_valuemin){
14006             cfg['aria-valuemin'] = this.aria_valuemin;
14007         }
14008         
14009         if(this.aria_valuemax){
14010             cfg['aria-valuemax'] = this.aria_valuemax;
14011         }
14012         
14013         if(this.label && !this.sr_only){
14014             cfg.html = this.label;
14015         }
14016         
14017         if(this.panel){
14018             cfg.cls += ' progress-bar-' + this.panel;
14019         }
14020         
14021         return cfg;
14022     },
14023     
14024     update : function(aria_valuenow)
14025     {
14026         this.aria_valuenow = aria_valuenow;
14027         
14028         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14029     }
14030    
14031 });
14032
14033  
14034
14035  /*
14036  * - LGPL
14037  *
14038  * column
14039  * 
14040  */
14041
14042 /**
14043  * @class Roo.bootstrap.TabGroup
14044  * @extends Roo.bootstrap.Column
14045  * Bootstrap Column class
14046  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14047  * @cfg {Boolean} carousel true to make the group behave like a carousel
14048  * 
14049  * @constructor
14050  * Create a new TabGroup
14051  * @param {Object} config The config object
14052  */
14053
14054 Roo.bootstrap.TabGroup = function(config){
14055     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14056     if (!this.navId) {
14057         this.navId = Roo.id();
14058     }
14059     this.tabs = [];
14060     Roo.bootstrap.TabGroup.register(this);
14061     
14062 };
14063
14064 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14065     
14066     carousel : false,
14067     transition : false,
14068      
14069     getAutoCreate : function()
14070     {
14071         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14072         
14073         cfg.cls += ' tab-content';
14074         
14075         if (this.carousel) {
14076             cfg.cls += ' carousel slide';
14077             cfg.cn = [{
14078                cls : 'carousel-inner'
14079             }]
14080         }
14081         
14082         
14083         return cfg;
14084     },
14085     getChildContainer : function()
14086     {
14087         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14088     },
14089     
14090     /**
14091     * register a Navigation item
14092     * @param {Roo.bootstrap.NavItem} the navitem to add
14093     */
14094     register : function(item)
14095     {
14096         this.tabs.push( item);
14097         item.navId = this.navId; // not really needed..
14098     
14099     },
14100     
14101     getActivePanel : function()
14102     {
14103         var r = false;
14104         Roo.each(this.tabs, function(t) {
14105             if (t.active) {
14106                 r = t;
14107                 return false;
14108             }
14109             return null;
14110         });
14111         return r;
14112         
14113     },
14114     getPanelByName : function(n)
14115     {
14116         var r = false;
14117         Roo.each(this.tabs, function(t) {
14118             if (t.tabId == n) {
14119                 r = t;
14120                 return false;
14121             }
14122             return null;
14123         });
14124         return r;
14125     },
14126     indexOfPanel : function(p)
14127     {
14128         var r = false;
14129         Roo.each(this.tabs, function(t,i) {
14130             if (t.tabId == p.tabId) {
14131                 r = i;
14132                 return false;
14133             }
14134             return null;
14135         });
14136         return r;
14137     },
14138     /**
14139      * show a specific panel
14140      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14141      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14142      */
14143     showPanel : function (pan)
14144     {
14145         
14146         if (typeof(pan) == 'number') {
14147             pan = this.tabs[pan];
14148         }
14149         if (typeof(pan) == 'string') {
14150             pan = this.getPanelByName(pan);
14151         }
14152         if (pan.tabId == this.getActivePanel().tabId) {
14153             return true;
14154         }
14155         var cur = this.getActivePanel();
14156         
14157         if (false === cur.fireEvent('beforedeactivate')) {
14158             return false;
14159         }
14160         
14161         if (this.carousel) {
14162             this.transition = true;
14163             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14164             var lr = dir == 'next' ? 'left' : 'right';
14165             pan.el.addClass(dir); // or prev
14166             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14167             cur.el.addClass(lr); // or right
14168             pan.el.addClass(lr);
14169             
14170             var _this = this;
14171             cur.el.on('transitionend', function() {
14172                 Roo.log("trans end?");
14173                 
14174                 pan.el.removeClass([lr,dir]);
14175                 pan.setActive(true);
14176                 
14177                 cur.el.removeClass([lr]);
14178                 cur.setActive(false);
14179                 
14180                 _this.transition = false;
14181                 
14182             }, this, { single:  true } );
14183             return true;
14184         }
14185         
14186         cur.setActive(false);
14187         pan.setActive(true);
14188         return true;
14189         
14190     },
14191     showPanelNext : function()
14192     {
14193         var i = this.indexOfPanel(this.getActivePanel());
14194         if (i > this.tabs.length) {
14195             return;
14196         }
14197         this.showPanel(this.tabs[i+1]);
14198     },
14199     showPanelPrev : function()
14200     {
14201         var i = this.indexOfPanel(this.getActivePanel());
14202         if (i  < 1) {
14203             return;
14204         }
14205         this.showPanel(this.tabs[i-1]);
14206     }
14207     
14208     
14209   
14210 });
14211
14212  
14213
14214  
14215  
14216 Roo.apply(Roo.bootstrap.TabGroup, {
14217     
14218     groups: {},
14219      /**
14220     * register a Navigation Group
14221     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14222     */
14223     register : function(navgrp)
14224     {
14225         this.groups[navgrp.navId] = navgrp;
14226         
14227     },
14228     /**
14229     * fetch a Navigation Group based on the navigation ID
14230     * if one does not exist , it will get created.
14231     * @param {string} the navgroup to add
14232     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14233     */
14234     get: function(navId) {
14235         if (typeof(this.groups[navId]) == 'undefined') {
14236             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14237         }
14238         return this.groups[navId] ;
14239     }
14240     
14241     
14242     
14243 });
14244
14245  /*
14246  * - LGPL
14247  *
14248  * TabPanel
14249  * 
14250  */
14251
14252 /**
14253  * @class Roo.bootstrap.TabPanel
14254  * @extends Roo.bootstrap.Component
14255  * Bootstrap TabPanel class
14256  * @cfg {Boolean} active panel active
14257  * @cfg {String} html panel content
14258  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14259  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14260  * 
14261  * 
14262  * @constructor
14263  * Create a new TabPanel
14264  * @param {Object} config The config object
14265  */
14266
14267 Roo.bootstrap.TabPanel = function(config){
14268     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14269     this.addEvents({
14270         /**
14271              * @event changed
14272              * Fires when the active status changes
14273              * @param {Roo.bootstrap.TabPanel} this
14274              * @param {Boolean} state the new state
14275             
14276          */
14277         'changed': true,
14278         /**
14279              * @event beforedeactivate
14280              * Fires before a tab is de-activated - can be used to do validation on a form.
14281              * @param {Roo.bootstrap.TabPanel} this
14282              * @return {Boolean} false if there is an error
14283             
14284          */
14285         'beforedeactivate': true
14286      });
14287     
14288     this.tabId = this.tabId || Roo.id();
14289   
14290 };
14291
14292 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14293     
14294     active: false,
14295     html: false,
14296     tabId: false,
14297     navId : false,
14298     
14299     getAutoCreate : function(){
14300         var cfg = {
14301             tag: 'div',
14302             // item is needed for carousel - not sure if it has any effect otherwise
14303             cls: 'tab-pane item',
14304             html: this.html || ''
14305         };
14306         
14307         if(this.active){
14308             cfg.cls += ' active';
14309         }
14310         
14311         if(this.tabId){
14312             cfg.tabId = this.tabId;
14313         }
14314         
14315         
14316         return cfg;
14317     },
14318     
14319     initEvents:  function()
14320     {
14321         Roo.log('-------- init events on tab panel ---------');
14322         
14323         var p = this.parent();
14324         this.navId = this.navId || p.navId;
14325         
14326         if (typeof(this.navId) != 'undefined') {
14327             // not really needed.. but just in case.. parent should be a NavGroup.
14328             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14329             Roo.log(['register', tg, this]);
14330             tg.register(this);
14331         }
14332     },
14333     
14334     
14335     onRender : function(ct, position)
14336     {
14337        // Roo.log("Call onRender: " + this.xtype);
14338         
14339         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14340         
14341         
14342         
14343         
14344         
14345     },
14346     
14347     setActive: function(state)
14348     {
14349         Roo.log("panel - set active " + this.tabId + "=" + state);
14350         
14351         this.active = state;
14352         if (!state) {
14353             this.el.removeClass('active');
14354             
14355         } else  if (!this.el.hasClass('active')) {
14356             this.el.addClass('active');
14357         }
14358         this.fireEvent('changed', this, state);
14359     }
14360     
14361     
14362 });
14363  
14364
14365  
14366
14367  /*
14368  * - LGPL
14369  *
14370  * DateField
14371  * 
14372  */
14373
14374 /**
14375  * @class Roo.bootstrap.DateField
14376  * @extends Roo.bootstrap.Input
14377  * Bootstrap DateField class
14378  * @cfg {Number} weekStart default 0
14379  * @cfg {Number} weekStart default 0
14380  * @cfg {Number} viewMode default empty, (months|years)
14381  * @cfg {Number} minViewMode default empty, (months|years)
14382  * @cfg {Number} startDate default -Infinity
14383  * @cfg {Number} endDate default Infinity
14384  * @cfg {Boolean} todayHighlight default false
14385  * @cfg {Boolean} todayBtn default false
14386  * @cfg {Boolean} calendarWeeks default false
14387  * @cfg {Object} daysOfWeekDisabled default empty
14388  * 
14389  * @cfg {Boolean} keyboardNavigation default true
14390  * @cfg {String} language default en
14391  * 
14392  * @constructor
14393  * Create a new DateField
14394  * @param {Object} config The config object
14395  */
14396
14397 Roo.bootstrap.DateField = function(config){
14398     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14399      this.addEvents({
14400             /**
14401              * @event show
14402              * Fires when this field show.
14403              * @param {Roo.bootstrap.DateField} this
14404              * @param {Mixed} date The date value
14405              */
14406             show : true,
14407             /**
14408              * @event show
14409              * Fires when this field hide.
14410              * @param {Roo.bootstrap.DateField} this
14411              * @param {Mixed} date The date value
14412              */
14413             hide : true,
14414             /**
14415              * @event select
14416              * Fires when select a date.
14417              * @param {Roo.bootstrap.DateField} this
14418              * @param {Mixed} date The date value
14419              */
14420             select : true
14421         });
14422 };
14423
14424 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14425     
14426     /**
14427      * @cfg {String} format
14428      * The default date format string which can be overriden for localization support.  The format must be
14429      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14430      */
14431     format : "m/d/y",
14432     /**
14433      * @cfg {String} altFormats
14434      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14435      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14436      */
14437     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14438     
14439     weekStart : 0,
14440     
14441     viewMode : '',
14442     
14443     minViewMode : '',
14444     
14445     todayHighlight : false,
14446     
14447     todayBtn: false,
14448     
14449     language: 'en',
14450     
14451     keyboardNavigation: true,
14452     
14453     calendarWeeks: false,
14454     
14455     startDate: -Infinity,
14456     
14457     endDate: Infinity,
14458     
14459     daysOfWeekDisabled: [],
14460     
14461     _events: [],
14462     
14463     UTCDate: function()
14464     {
14465         return new Date(Date.UTC.apply(Date, arguments));
14466     },
14467     
14468     UTCToday: function()
14469     {
14470         var today = new Date();
14471         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14472     },
14473     
14474     getDate: function() {
14475             var d = this.getUTCDate();
14476             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14477     },
14478     
14479     getUTCDate: function() {
14480             return this.date;
14481     },
14482     
14483     setDate: function(d) {
14484             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14485     },
14486     
14487     setUTCDate: function(d) {
14488             this.date = d;
14489             this.setValue(this.formatDate(this.date));
14490     },
14491         
14492     onRender: function(ct, position)
14493     {
14494         
14495         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14496         
14497         this.language = this.language || 'en';
14498         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14499         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14500         
14501         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14502         this.format = this.format || 'm/d/y';
14503         this.isInline = false;
14504         this.isInput = true;
14505         this.component = this.el.select('.add-on', true).first() || false;
14506         this.component = (this.component && this.component.length === 0) ? false : this.component;
14507         this.hasInput = this.component && this.inputEL().length;
14508         
14509         if (typeof(this.minViewMode === 'string')) {
14510             switch (this.minViewMode) {
14511                 case 'months':
14512                     this.minViewMode = 1;
14513                     break;
14514                 case 'years':
14515                     this.minViewMode = 2;
14516                     break;
14517                 default:
14518                     this.minViewMode = 0;
14519                     break;
14520             }
14521         }
14522         
14523         if (typeof(this.viewMode === 'string')) {
14524             switch (this.viewMode) {
14525                 case 'months':
14526                     this.viewMode = 1;
14527                     break;
14528                 case 'years':
14529                     this.viewMode = 2;
14530                     break;
14531                 default:
14532                     this.viewMode = 0;
14533                     break;
14534             }
14535         }
14536                 
14537         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14538         
14539 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14540         
14541         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14542         
14543         this.picker().on('mousedown', this.onMousedown, this);
14544         this.picker().on('click', this.onClick, this);
14545         
14546         this.picker().addClass('datepicker-dropdown');
14547         
14548         this.startViewMode = this.viewMode;
14549         
14550         
14551         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14552             if(!this.calendarWeeks){
14553                 v.remove();
14554                 return;
14555             };
14556             
14557             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14558             v.attr('colspan', function(i, val){
14559                 return parseInt(val) + 1;
14560             });
14561         })
14562                         
14563         
14564         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14565         
14566         this.setStartDate(this.startDate);
14567         this.setEndDate(this.endDate);
14568         
14569         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14570         
14571         this.fillDow();
14572         this.fillMonths();
14573         this.update();
14574         this.showMode();
14575         
14576         if(this.isInline) {
14577             this.show();
14578         }
14579     },
14580     
14581     picker : function()
14582     {
14583         return this.pickerEl;
14584 //        return this.el.select('.datepicker', true).first();
14585     },
14586     
14587     fillDow: function()
14588     {
14589         var dowCnt = this.weekStart;
14590         
14591         var dow = {
14592             tag: 'tr',
14593             cn: [
14594                 
14595             ]
14596         };
14597         
14598         if(this.calendarWeeks){
14599             dow.cn.push({
14600                 tag: 'th',
14601                 cls: 'cw',
14602                 html: '&nbsp;'
14603             })
14604         }
14605         
14606         while (dowCnt < this.weekStart + 7) {
14607             dow.cn.push({
14608                 tag: 'th',
14609                 cls: 'dow',
14610                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14611             });
14612         }
14613         
14614         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14615     },
14616     
14617     fillMonths: function()
14618     {    
14619         var i = 0
14620         var months = this.picker().select('>.datepicker-months td', true).first();
14621         
14622         months.dom.innerHTML = '';
14623         
14624         while (i < 12) {
14625             var month = {
14626                 tag: 'span',
14627                 cls: 'month',
14628                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14629             }
14630             
14631             months.createChild(month);
14632         }
14633         
14634     },
14635     
14636     update: function()
14637     {
14638         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;
14639         
14640         if (this.date < this.startDate) {
14641             this.viewDate = new Date(this.startDate);
14642         } else if (this.date > this.endDate) {
14643             this.viewDate = new Date(this.endDate);
14644         } else {
14645             this.viewDate = new Date(this.date);
14646         }
14647         
14648         this.fill();
14649     },
14650     
14651     fill: function() 
14652     {
14653         var d = new Date(this.viewDate),
14654                 year = d.getUTCFullYear(),
14655                 month = d.getUTCMonth(),
14656                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14657                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14658                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14659                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14660                 currentDate = this.date && this.date.valueOf(),
14661                 today = this.UTCToday();
14662         
14663         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14664         
14665 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14666         
14667 //        this.picker.select('>tfoot th.today').
14668 //                                              .text(dates[this.language].today)
14669 //                                              .toggle(this.todayBtn !== false);
14670     
14671         this.updateNavArrows();
14672         this.fillMonths();
14673                                                 
14674         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14675         
14676         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14677          
14678         prevMonth.setUTCDate(day);
14679         
14680         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14681         
14682         var nextMonth = new Date(prevMonth);
14683         
14684         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14685         
14686         nextMonth = nextMonth.valueOf();
14687         
14688         var fillMonths = false;
14689         
14690         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14691         
14692         while(prevMonth.valueOf() < nextMonth) {
14693             var clsName = '';
14694             
14695             if (prevMonth.getUTCDay() === this.weekStart) {
14696                 if(fillMonths){
14697                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14698                 }
14699                     
14700                 fillMonths = {
14701                     tag: 'tr',
14702                     cn: []
14703                 };
14704                 
14705                 if(this.calendarWeeks){
14706                     // ISO 8601: First week contains first thursday.
14707                     // ISO also states week starts on Monday, but we can be more abstract here.
14708                     var
14709                     // Start of current week: based on weekstart/current date
14710                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14711                     // Thursday of this week
14712                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14713                     // First Thursday of year, year from thursday
14714                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14715                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14716                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14717                     
14718                     fillMonths.cn.push({
14719                         tag: 'td',
14720                         cls: 'cw',
14721                         html: calWeek
14722                     });
14723                 }
14724             }
14725             
14726             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14727                 clsName += ' old';
14728             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14729                 clsName += ' new';
14730             }
14731             if (this.todayHighlight &&
14732                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14733                 prevMonth.getUTCMonth() == today.getMonth() &&
14734                 prevMonth.getUTCDate() == today.getDate()) {
14735                 clsName += ' today';
14736             }
14737             
14738             if (currentDate && prevMonth.valueOf() === currentDate) {
14739                 clsName += ' active';
14740             }
14741             
14742             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14743                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14744                     clsName += ' disabled';
14745             }
14746             
14747             fillMonths.cn.push({
14748                 tag: 'td',
14749                 cls: 'day ' + clsName,
14750                 html: prevMonth.getDate()
14751             })
14752             
14753             prevMonth.setDate(prevMonth.getDate()+1);
14754         }
14755           
14756         var currentYear = this.date && this.date.getUTCFullYear();
14757         var currentMonth = this.date && this.date.getUTCMonth();
14758         
14759         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14760         
14761         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14762             v.removeClass('active');
14763             
14764             if(currentYear === year && k === currentMonth){
14765                 v.addClass('active');
14766             }
14767             
14768             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14769                 v.addClass('disabled');
14770             }
14771             
14772         });
14773         
14774         
14775         year = parseInt(year/10, 10) * 10;
14776         
14777         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14778         
14779         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14780         
14781         year -= 1;
14782         for (var i = -1; i < 11; i++) {
14783             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14784                 tag: 'span',
14785                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14786                 html: year
14787             })
14788             
14789             year += 1;
14790         }
14791     },
14792     
14793     showMode: function(dir) 
14794     {
14795         if (dir) {
14796             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14797         }
14798         Roo.each(this.picker().select('>div',true).elements, function(v){
14799             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14800             v.hide();
14801         });
14802         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14803     },
14804     
14805     place: function()
14806     {
14807         if(this.isInline) return;
14808         
14809         this.picker().removeClass(['bottom', 'top']);
14810         
14811         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14812             /*
14813              * place to the top of element!
14814              *
14815              */
14816             
14817             this.picker().addClass('top');
14818             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14819             
14820             return;
14821         }
14822         
14823         this.picker().addClass('bottom');
14824         
14825         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14826     },
14827     
14828     parseDate : function(value)
14829     {
14830         if(!value || value instanceof Date){
14831             return value;
14832         }
14833         var v = Date.parseDate(value, this.format);
14834         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14835             v = Date.parseDate(value, 'Y-m-d');
14836         }
14837         if(!v && this.altFormats){
14838             if(!this.altFormatsArray){
14839                 this.altFormatsArray = this.altFormats.split("|");
14840             }
14841             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14842                 v = Date.parseDate(value, this.altFormatsArray[i]);
14843             }
14844         }
14845         return v;
14846     },
14847     
14848     formatDate : function(date, fmt)
14849     {
14850         return (!date || !(date instanceof Date)) ?
14851         date : date.dateFormat(fmt || this.format);
14852     },
14853     
14854     onFocus : function()
14855     {
14856         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14857         this.show();
14858     },
14859     
14860     onBlur : function()
14861     {
14862         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14863         
14864         var d = this.inputEl().getValue();
14865         
14866         this.setValue(d);
14867                 
14868         this.hide();
14869     },
14870     
14871     show : function()
14872     {
14873         this.picker().show();
14874         this.update();
14875         this.place();
14876         
14877         this.fireEvent('show', this, this.date);
14878     },
14879     
14880     hide : function()
14881     {
14882         if(this.isInline) return;
14883         this.picker().hide();
14884         this.viewMode = this.startViewMode;
14885         this.showMode();
14886         
14887         this.fireEvent('hide', this, this.date);
14888         
14889     },
14890     
14891     onMousedown: function(e)
14892     {
14893         e.stopPropagation();
14894         e.preventDefault();
14895     },
14896     
14897     keyup: function(e)
14898     {
14899         Roo.bootstrap.DateField.superclass.keyup.call(this);
14900         this.update();
14901     },
14902
14903     setValue: function(v)
14904     {
14905         
14906         // v can be a string or a date..
14907         
14908         
14909         var d = new Date(this.parseDate(v) ).clearTime();
14910         
14911         Roo.log(d);
14912         Roo.log(d);
14913         if(isNaN(d.getTime())){
14914             this.date = this.viewDate = '';
14915             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14916             return;
14917         }
14918         
14919         v = this.formatDate(d);
14920         
14921         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14922         
14923         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14924      
14925         this.update();
14926
14927         this.fireEvent('select', this, this.date);
14928         
14929     },
14930     
14931     getValue: function()
14932     {
14933         return this.formatDate(this.date);
14934     },
14935     
14936     fireKey: function(e)
14937     {
14938         if (!this.picker().isVisible()){
14939             if (e.keyCode == 27) // allow escape to hide and re-show picker
14940                 this.show();
14941             return;
14942         }
14943         
14944         var dateChanged = false,
14945         dir, day, month,
14946         newDate, newViewDate;
14947         
14948         switch(e.keyCode){
14949             case 27: // escape
14950                 this.hide();
14951                 e.preventDefault();
14952                 break;
14953             case 37: // left
14954             case 39: // right
14955                 if (!this.keyboardNavigation) break;
14956                 dir = e.keyCode == 37 ? -1 : 1;
14957                 
14958                 if (e.ctrlKey){
14959                     newDate = this.moveYear(this.date, dir);
14960                     newViewDate = this.moveYear(this.viewDate, dir);
14961                 } else if (e.shiftKey){
14962                     newDate = this.moveMonth(this.date, dir);
14963                     newViewDate = this.moveMonth(this.viewDate, dir);
14964                 } else {
14965                     newDate = new Date(this.date);
14966                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14967                     newViewDate = new Date(this.viewDate);
14968                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14969                 }
14970                 if (this.dateWithinRange(newDate)){
14971                     this.date = newDate;
14972                     this.viewDate = newViewDate;
14973                     this.setValue(this.formatDate(this.date));
14974 //                    this.update();
14975                     e.preventDefault();
14976                     dateChanged = true;
14977                 }
14978                 break;
14979             case 38: // up
14980             case 40: // down
14981                 if (!this.keyboardNavigation) break;
14982                 dir = e.keyCode == 38 ? -1 : 1;
14983                 if (e.ctrlKey){
14984                     newDate = this.moveYear(this.date, dir);
14985                     newViewDate = this.moveYear(this.viewDate, dir);
14986                 } else if (e.shiftKey){
14987                     newDate = this.moveMonth(this.date, dir);
14988                     newViewDate = this.moveMonth(this.viewDate, dir);
14989                 } else {
14990                     newDate = new Date(this.date);
14991                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14992                     newViewDate = new Date(this.viewDate);
14993                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14994                 }
14995                 if (this.dateWithinRange(newDate)){
14996                     this.date = newDate;
14997                     this.viewDate = newViewDate;
14998                     this.setValue(this.formatDate(this.date));
14999 //                    this.update();
15000                     e.preventDefault();
15001                     dateChanged = true;
15002                 }
15003                 break;
15004             case 13: // enter
15005                 this.setValue(this.formatDate(this.date));
15006                 this.hide();
15007                 e.preventDefault();
15008                 break;
15009             case 9: // tab
15010                 this.setValue(this.formatDate(this.date));
15011                 this.hide();
15012                 break;
15013             case 16: // shift
15014             case 17: // ctrl
15015             case 18: // alt
15016                 break;
15017             default :
15018                 this.hide();
15019                 
15020         }
15021     },
15022     
15023     
15024     onClick: function(e) 
15025     {
15026         e.stopPropagation();
15027         e.preventDefault();
15028         
15029         var target = e.getTarget();
15030         
15031         if(target.nodeName.toLowerCase() === 'i'){
15032             target = Roo.get(target).dom.parentNode;
15033         }
15034         
15035         var nodeName = target.nodeName;
15036         var className = target.className;
15037         var html = target.innerHTML;
15038         //Roo.log(nodeName);
15039         
15040         switch(nodeName.toLowerCase()) {
15041             case 'th':
15042                 switch(className) {
15043                     case 'switch':
15044                         this.showMode(1);
15045                         break;
15046                     case 'prev':
15047                     case 'next':
15048                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15049                         switch(this.viewMode){
15050                                 case 0:
15051                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15052                                         break;
15053                                 case 1:
15054                                 case 2:
15055                                         this.viewDate = this.moveYear(this.viewDate, dir);
15056                                         break;
15057                         }
15058                         this.fill();
15059                         break;
15060                     case 'today':
15061                         var date = new Date();
15062                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15063 //                        this.fill()
15064                         this.setValue(this.formatDate(this.date));
15065                         
15066                         this.hide();
15067                         break;
15068                 }
15069                 break;
15070             case 'span':
15071                 if (className.indexOf('disabled') < 0) {
15072                     this.viewDate.setUTCDate(1);
15073                     if (className.indexOf('month') > -1) {
15074                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15075                     } else {
15076                         var year = parseInt(html, 10) || 0;
15077                         this.viewDate.setUTCFullYear(year);
15078                         
15079                     }
15080                     this.showMode(-1);
15081                     this.fill();
15082                 }
15083                 break;
15084                 
15085             case 'td':
15086                 //Roo.log(className);
15087                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15088                     var day = parseInt(html, 10) || 1;
15089                     var year = this.viewDate.getUTCFullYear(),
15090                         month = this.viewDate.getUTCMonth();
15091
15092                     if (className.indexOf('old') > -1) {
15093                         if(month === 0 ){
15094                             month = 11;
15095                             year -= 1;
15096                         }else{
15097                             month -= 1;
15098                         }
15099                     } else if (className.indexOf('new') > -1) {
15100                         if (month == 11) {
15101                             month = 0;
15102                             year += 1;
15103                         } else {
15104                             month += 1;
15105                         }
15106                     }
15107                     //Roo.log([year,month,day]);
15108                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15109                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15110 //                    this.fill();
15111                     //Roo.log(this.formatDate(this.date));
15112                     this.setValue(this.formatDate(this.date));
15113                     this.hide();
15114                 }
15115                 break;
15116         }
15117     },
15118     
15119     setStartDate: function(startDate)
15120     {
15121         this.startDate = startDate || -Infinity;
15122         if (this.startDate !== -Infinity) {
15123             this.startDate = this.parseDate(this.startDate);
15124         }
15125         this.update();
15126         this.updateNavArrows();
15127     },
15128
15129     setEndDate: function(endDate)
15130     {
15131         this.endDate = endDate || Infinity;
15132         if (this.endDate !== Infinity) {
15133             this.endDate = this.parseDate(this.endDate);
15134         }
15135         this.update();
15136         this.updateNavArrows();
15137     },
15138     
15139     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15140     {
15141         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15142         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15143             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15144         }
15145         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15146             return parseInt(d, 10);
15147         });
15148         this.update();
15149         this.updateNavArrows();
15150     },
15151     
15152     updateNavArrows: function() 
15153     {
15154         var d = new Date(this.viewDate),
15155         year = d.getUTCFullYear(),
15156         month = d.getUTCMonth();
15157         
15158         Roo.each(this.picker().select('.prev', true).elements, function(v){
15159             v.show();
15160             switch (this.viewMode) {
15161                 case 0:
15162
15163                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15164                         v.hide();
15165                     }
15166                     break;
15167                 case 1:
15168                 case 2:
15169                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15170                         v.hide();
15171                     }
15172                     break;
15173             }
15174         });
15175         
15176         Roo.each(this.picker().select('.next', true).elements, function(v){
15177             v.show();
15178             switch (this.viewMode) {
15179                 case 0:
15180
15181                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15182                         v.hide();
15183                     }
15184                     break;
15185                 case 1:
15186                 case 2:
15187                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15188                         v.hide();
15189                     }
15190                     break;
15191             }
15192         })
15193     },
15194     
15195     moveMonth: function(date, dir)
15196     {
15197         if (!dir) return date;
15198         var new_date = new Date(date.valueOf()),
15199         day = new_date.getUTCDate(),
15200         month = new_date.getUTCMonth(),
15201         mag = Math.abs(dir),
15202         new_month, test;
15203         dir = dir > 0 ? 1 : -1;
15204         if (mag == 1){
15205             test = dir == -1
15206             // If going back one month, make sure month is not current month
15207             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15208             ? function(){
15209                 return new_date.getUTCMonth() == month;
15210             }
15211             // If going forward one month, make sure month is as expected
15212             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15213             : function(){
15214                 return new_date.getUTCMonth() != new_month;
15215             };
15216             new_month = month + dir;
15217             new_date.setUTCMonth(new_month);
15218             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15219             if (new_month < 0 || new_month > 11)
15220                 new_month = (new_month + 12) % 12;
15221         } else {
15222             // For magnitudes >1, move one month at a time...
15223             for (var i=0; i<mag; i++)
15224                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15225                 new_date = this.moveMonth(new_date, dir);
15226             // ...then reset the day, keeping it in the new month
15227             new_month = new_date.getUTCMonth();
15228             new_date.setUTCDate(day);
15229             test = function(){
15230                 return new_month != new_date.getUTCMonth();
15231             };
15232         }
15233         // Common date-resetting loop -- if date is beyond end of month, make it
15234         // end of month
15235         while (test()){
15236             new_date.setUTCDate(--day);
15237             new_date.setUTCMonth(new_month);
15238         }
15239         return new_date;
15240     },
15241
15242     moveYear: function(date, dir)
15243     {
15244         return this.moveMonth(date, dir*12);
15245     },
15246
15247     dateWithinRange: function(date)
15248     {
15249         return date >= this.startDate && date <= this.endDate;
15250     },
15251
15252     
15253     remove: function() 
15254     {
15255         this.picker().remove();
15256     }
15257    
15258 });
15259
15260 Roo.apply(Roo.bootstrap.DateField,  {
15261     
15262     head : {
15263         tag: 'thead',
15264         cn: [
15265         {
15266             tag: 'tr',
15267             cn: [
15268             {
15269                 tag: 'th',
15270                 cls: 'prev',
15271                 html: '<i class="fa fa-arrow-left"/>'
15272             },
15273             {
15274                 tag: 'th',
15275                 cls: 'switch',
15276                 colspan: '5'
15277             },
15278             {
15279                 tag: 'th',
15280                 cls: 'next',
15281                 html: '<i class="fa fa-arrow-right"/>'
15282             }
15283
15284             ]
15285         }
15286         ]
15287     },
15288     
15289     content : {
15290         tag: 'tbody',
15291         cn: [
15292         {
15293             tag: 'tr',
15294             cn: [
15295             {
15296                 tag: 'td',
15297                 colspan: '7'
15298             }
15299             ]
15300         }
15301         ]
15302     },
15303     
15304     footer : {
15305         tag: 'tfoot',
15306         cn: [
15307         {
15308             tag: 'tr',
15309             cn: [
15310             {
15311                 tag: 'th',
15312                 colspan: '7',
15313                 cls: 'today'
15314             }
15315                     
15316             ]
15317         }
15318         ]
15319     },
15320     
15321     dates:{
15322         en: {
15323             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15324             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15325             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15326             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15327             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15328             today: "Today"
15329         }
15330     },
15331     
15332     modes: [
15333     {
15334         clsName: 'days',
15335         navFnc: 'Month',
15336         navStep: 1
15337     },
15338     {
15339         clsName: 'months',
15340         navFnc: 'FullYear',
15341         navStep: 1
15342     },
15343     {
15344         clsName: 'years',
15345         navFnc: 'FullYear',
15346         navStep: 10
15347     }]
15348 });
15349
15350 Roo.apply(Roo.bootstrap.DateField,  {
15351   
15352     template : {
15353         tag: 'div',
15354         cls: 'datepicker dropdown-menu',
15355         cn: [
15356         {
15357             tag: 'div',
15358             cls: 'datepicker-days',
15359             cn: [
15360             {
15361                 tag: 'table',
15362                 cls: 'table-condensed',
15363                 cn:[
15364                 Roo.bootstrap.DateField.head,
15365                 {
15366                     tag: 'tbody'
15367                 },
15368                 Roo.bootstrap.DateField.footer
15369                 ]
15370             }
15371             ]
15372         },
15373         {
15374             tag: 'div',
15375             cls: 'datepicker-months',
15376             cn: [
15377             {
15378                 tag: 'table',
15379                 cls: 'table-condensed',
15380                 cn:[
15381                 Roo.bootstrap.DateField.head,
15382                 Roo.bootstrap.DateField.content,
15383                 Roo.bootstrap.DateField.footer
15384                 ]
15385             }
15386             ]
15387         },
15388         {
15389             tag: 'div',
15390             cls: 'datepicker-years',
15391             cn: [
15392             {
15393                 tag: 'table',
15394                 cls: 'table-condensed',
15395                 cn:[
15396                 Roo.bootstrap.DateField.head,
15397                 Roo.bootstrap.DateField.content,
15398                 Roo.bootstrap.DateField.footer
15399                 ]
15400             }
15401             ]
15402         }
15403         ]
15404     }
15405 });
15406
15407  
15408
15409  /*
15410  * - LGPL
15411  *
15412  * TimeField
15413  * 
15414  */
15415
15416 /**
15417  * @class Roo.bootstrap.TimeField
15418  * @extends Roo.bootstrap.Input
15419  * Bootstrap DateField class
15420  * 
15421  * 
15422  * @constructor
15423  * Create a new TimeField
15424  * @param {Object} config The config object
15425  */
15426
15427 Roo.bootstrap.TimeField = function(config){
15428     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15429     this.addEvents({
15430             /**
15431              * @event show
15432              * Fires when this field show.
15433              * @param {Roo.bootstrap.DateField} this
15434              * @param {Mixed} date The date value
15435              */
15436             show : true,
15437             /**
15438              * @event show
15439              * Fires when this field hide.
15440              * @param {Roo.bootstrap.DateField} this
15441              * @param {Mixed} date The date value
15442              */
15443             hide : true,
15444             /**
15445              * @event select
15446              * Fires when select a date.
15447              * @param {Roo.bootstrap.DateField} this
15448              * @param {Mixed} date The date value
15449              */
15450             select : true
15451         });
15452 };
15453
15454 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15455     
15456     /**
15457      * @cfg {String} format
15458      * The default time format string which can be overriden for localization support.  The format must be
15459      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15460      */
15461     format : "H:i",
15462        
15463     onRender: function(ct, position)
15464     {
15465         
15466         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15467                 
15468         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15469         
15470         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15471         
15472         this.pop = this.picker().select('>.datepicker-time',true).first();
15473         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15474         
15475         this.picker().on('mousedown', this.onMousedown, this);
15476         this.picker().on('click', this.onClick, this);
15477         
15478         this.picker().addClass('datepicker-dropdown');
15479     
15480         this.fillTime();
15481         this.update();
15482             
15483         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15484         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15485         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15486         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15487         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15488         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15489
15490     },
15491     
15492     fireKey: function(e){
15493         if (!this.picker().isVisible()){
15494             if (e.keyCode == 27) // allow escape to hide and re-show picker
15495                 this.show();
15496             return;
15497         }
15498
15499         e.preventDefault();
15500         
15501         switch(e.keyCode){
15502             case 27: // escape
15503                 this.hide();
15504                 break;
15505             case 37: // left
15506             case 39: // right
15507                 this.onTogglePeriod();
15508                 break;
15509             case 38: // up
15510                 this.onIncrementMinutes();
15511                 break;
15512             case 40: // down
15513                 this.onDecrementMinutes();
15514                 break;
15515             case 13: // enter
15516             case 9: // tab
15517                 this.setTime();
15518                 break;
15519         }
15520     },
15521     
15522     onClick: function(e) {
15523         e.stopPropagation();
15524         e.preventDefault();
15525     },
15526     
15527     picker : function()
15528     {
15529         return this.el.select('.datepicker', true).first();
15530     },
15531     
15532     fillTime: function()
15533     {    
15534         var time = this.pop.select('tbody', true).first();
15535         
15536         time.dom.innerHTML = '';
15537         
15538         time.createChild({
15539             tag: 'tr',
15540             cn: [
15541                 {
15542                     tag: 'td',
15543                     cn: [
15544                         {
15545                             tag: 'a',
15546                             href: '#',
15547                             cls: 'btn',
15548                             cn: [
15549                                 {
15550                                     tag: 'span',
15551                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15552                                 }
15553                             ]
15554                         } 
15555                     ]
15556                 },
15557                 {
15558                     tag: 'td',
15559                     cls: 'separator'
15560                 },
15561                 {
15562                     tag: 'td',
15563                     cn: [
15564                         {
15565                             tag: 'a',
15566                             href: '#',
15567                             cls: 'btn',
15568                             cn: [
15569                                 {
15570                                     tag: 'span',
15571                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15572                                 }
15573                             ]
15574                         }
15575                     ]
15576                 },
15577                 {
15578                     tag: 'td',
15579                     cls: 'separator'
15580                 }
15581             ]
15582         });
15583         
15584         time.createChild({
15585             tag: 'tr',
15586             cn: [
15587                 {
15588                     tag: 'td',
15589                     cn: [
15590                         {
15591                             tag: 'span',
15592                             cls: 'timepicker-hour',
15593                             html: '00'
15594                         }  
15595                     ]
15596                 },
15597                 {
15598                     tag: 'td',
15599                     cls: 'separator',
15600                     html: ':'
15601                 },
15602                 {
15603                     tag: 'td',
15604                     cn: [
15605                         {
15606                             tag: 'span',
15607                             cls: 'timepicker-minute',
15608                             html: '00'
15609                         }  
15610                     ]
15611                 },
15612                 {
15613                     tag: 'td',
15614                     cls: 'separator'
15615                 },
15616                 {
15617                     tag: 'td',
15618                     cn: [
15619                         {
15620                             tag: 'button',
15621                             type: 'button',
15622                             cls: 'btn btn-primary period',
15623                             html: 'AM'
15624                             
15625                         }
15626                     ]
15627                 }
15628             ]
15629         });
15630         
15631         time.createChild({
15632             tag: 'tr',
15633             cn: [
15634                 {
15635                     tag: 'td',
15636                     cn: [
15637                         {
15638                             tag: 'a',
15639                             href: '#',
15640                             cls: 'btn',
15641                             cn: [
15642                                 {
15643                                     tag: 'span',
15644                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15645                                 }
15646                             ]
15647                         }
15648                     ]
15649                 },
15650                 {
15651                     tag: 'td',
15652                     cls: 'separator'
15653                 },
15654                 {
15655                     tag: 'td',
15656                     cn: [
15657                         {
15658                             tag: 'a',
15659                             href: '#',
15660                             cls: 'btn',
15661                             cn: [
15662                                 {
15663                                     tag: 'span',
15664                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15665                                 }
15666                             ]
15667                         }
15668                     ]
15669                 },
15670                 {
15671                     tag: 'td',
15672                     cls: 'separator'
15673                 }
15674             ]
15675         });
15676         
15677     },
15678     
15679     update: function()
15680     {
15681         
15682         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15683         
15684         this.fill();
15685     },
15686     
15687     fill: function() 
15688     {
15689         var hours = this.time.getHours();
15690         var minutes = this.time.getMinutes();
15691         var period = 'AM';
15692         
15693         if(hours > 11){
15694             period = 'PM';
15695         }
15696         
15697         if(hours == 0){
15698             hours = 12;
15699         }
15700         
15701         
15702         if(hours > 12){
15703             hours = hours - 12;
15704         }
15705         
15706         if(hours < 10){
15707             hours = '0' + hours;
15708         }
15709         
15710         if(minutes < 10){
15711             minutes = '0' + minutes;
15712         }
15713         
15714         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15715         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15716         this.pop.select('button', true).first().dom.innerHTML = period;
15717         
15718     },
15719     
15720     place: function()
15721     {   
15722         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15723         
15724         var cls = ['bottom'];
15725         
15726         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15727             cls.pop();
15728             cls.push('top');
15729         }
15730         
15731         cls.push('right');
15732         
15733         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15734             cls.pop();
15735             cls.push('left');
15736         }
15737         
15738         this.picker().addClass(cls.join('-'));
15739         
15740         var _this = this;
15741         
15742         Roo.each(cls, function(c){
15743             if(c == 'bottom'){
15744                 _this.picker().setTop(_this.inputEl().getHeight());
15745                 return;
15746             }
15747             if(c == 'top'){
15748                 _this.picker().setTop(0 - _this.picker().getHeight());
15749                 return;
15750             }
15751             
15752             if(c == 'left'){
15753                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15754                 return;
15755             }
15756             if(c == 'right'){
15757                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15758                 return;
15759             }
15760         });
15761         
15762     },
15763   
15764     onFocus : function()
15765     {
15766         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15767         this.show();
15768     },
15769     
15770     onBlur : function()
15771     {
15772         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15773         this.hide();
15774     },
15775     
15776     show : function()
15777     {
15778         this.picker().show();
15779         this.pop.show();
15780         this.update();
15781         this.place();
15782         
15783         this.fireEvent('show', this, this.date);
15784     },
15785     
15786     hide : function()
15787     {
15788         this.picker().hide();
15789         this.pop.hide();
15790         
15791         this.fireEvent('hide', this, this.date);
15792     },
15793     
15794     setTime : function()
15795     {
15796         this.hide();
15797         this.setValue(this.time.format(this.format));
15798         
15799         this.fireEvent('select', this, this.date);
15800         
15801         
15802     },
15803     
15804     onMousedown: function(e){
15805         e.stopPropagation();
15806         e.preventDefault();
15807     },
15808     
15809     onIncrementHours: function()
15810     {
15811         Roo.log('onIncrementHours');
15812         this.time = this.time.add(Date.HOUR, 1);
15813         this.update();
15814         
15815     },
15816     
15817     onDecrementHours: function()
15818     {
15819         Roo.log('onDecrementHours');
15820         this.time = this.time.add(Date.HOUR, -1);
15821         this.update();
15822     },
15823     
15824     onIncrementMinutes: function()
15825     {
15826         Roo.log('onIncrementMinutes');
15827         this.time = this.time.add(Date.MINUTE, 1);
15828         this.update();
15829     },
15830     
15831     onDecrementMinutes: function()
15832     {
15833         Roo.log('onDecrementMinutes');
15834         this.time = this.time.add(Date.MINUTE, -1);
15835         this.update();
15836     },
15837     
15838     onTogglePeriod: function()
15839     {
15840         Roo.log('onTogglePeriod');
15841         this.time = this.time.add(Date.HOUR, 12);
15842         this.update();
15843     }
15844     
15845    
15846 });
15847
15848 Roo.apply(Roo.bootstrap.TimeField,  {
15849     
15850     content : {
15851         tag: 'tbody',
15852         cn: [
15853             {
15854                 tag: 'tr',
15855                 cn: [
15856                 {
15857                     tag: 'td',
15858                     colspan: '7'
15859                 }
15860                 ]
15861             }
15862         ]
15863     },
15864     
15865     footer : {
15866         tag: 'tfoot',
15867         cn: [
15868             {
15869                 tag: 'tr',
15870                 cn: [
15871                 {
15872                     tag: 'th',
15873                     colspan: '7',
15874                     cls: '',
15875                     cn: [
15876                         {
15877                             tag: 'button',
15878                             cls: 'btn btn-info ok',
15879                             html: 'OK'
15880                         }
15881                     ]
15882                 }
15883
15884                 ]
15885             }
15886         ]
15887     }
15888 });
15889
15890 Roo.apply(Roo.bootstrap.TimeField,  {
15891   
15892     template : {
15893         tag: 'div',
15894         cls: 'datepicker dropdown-menu',
15895         cn: [
15896             {
15897                 tag: 'div',
15898                 cls: 'datepicker-time',
15899                 cn: [
15900                 {
15901                     tag: 'table',
15902                     cls: 'table-condensed',
15903                     cn:[
15904                     Roo.bootstrap.TimeField.content,
15905                     Roo.bootstrap.TimeField.footer
15906                     ]
15907                 }
15908                 ]
15909             }
15910         ]
15911     }
15912 });
15913
15914  
15915
15916  /*
15917  * - LGPL
15918  *
15919  * CheckBox
15920  * 
15921  */
15922
15923 /**
15924  * @class Roo.bootstrap.CheckBox
15925  * @extends Roo.bootstrap.Input
15926  * Bootstrap CheckBox class
15927  * 
15928  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15929  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15930  * @cfg {String} boxLabel The text that appears beside the checkbox
15931  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15932  * @cfg {Boolean} checked initnal the element
15933  * 
15934  * 
15935  * @constructor
15936  * Create a new CheckBox
15937  * @param {Object} config The config object
15938  */
15939
15940 Roo.bootstrap.CheckBox = function(config){
15941     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15942    
15943         this.addEvents({
15944             /**
15945             * @event check
15946             * Fires when the element is checked or unchecked.
15947             * @param {Roo.bootstrap.CheckBox} this This input
15948             * @param {Boolean} checked The new checked value
15949             */
15950            check : true
15951         });
15952 };
15953
15954 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15955     
15956     inputType: 'checkbox',
15957     inputValue: 1,
15958     valueOff: 0,
15959     boxLabel: false,
15960     checked: false,
15961     weight : false,
15962     
15963     getAutoCreate : function()
15964     {
15965         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15966         
15967         var id = Roo.id();
15968         
15969         var cfg = {};
15970         
15971         cfg.cls = 'form-group checkbox' //input-group
15972         
15973         
15974         
15975         
15976         var input =  {
15977             tag: 'input',
15978             id : id,
15979             type : this.inputType,
15980             value : (!this.checked) ? this.valueOff : this.inputValue,
15981             cls : 'roo-checkbox', //'form-box',
15982             placeholder : this.placeholder || ''
15983             
15984         };
15985         
15986         if (this.weight) { // Validity check?
15987             cfg.cls += " checkbox-" + this.weight;
15988         }
15989         
15990         if (this.disabled) {
15991             input.disabled=true;
15992         }
15993         
15994         if(this.checked){
15995             input.checked = this.checked;
15996         }
15997         
15998         if (this.name) {
15999             input.name = this.name;
16000         }
16001         
16002         if (this.size) {
16003             input.cls += ' input-' + this.size;
16004         }
16005         
16006         var settings=this;
16007         ['xs','sm','md','lg'].map(function(size){
16008             if (settings[size]) {
16009                 cfg.cls += ' col-' + size + '-' + settings[size];
16010             }
16011         });
16012         
16013        
16014         
16015         var inputblock = input;
16016         
16017         
16018         
16019         
16020         if (this.before || this.after) {
16021             
16022             inputblock = {
16023                 cls : 'input-group',
16024                 cn :  [] 
16025             };
16026             if (this.before) {
16027                 inputblock.cn.push({
16028                     tag :'span',
16029                     cls : 'input-group-addon',
16030                     html : this.before
16031                 });
16032             }
16033             inputblock.cn.push(input);
16034             if (this.after) {
16035                 inputblock.cn.push({
16036                     tag :'span',
16037                     cls : 'input-group-addon',
16038                     html : this.after
16039                 });
16040             }
16041             
16042         };
16043         
16044         if (align ==='left' && this.fieldLabel.length) {
16045                 Roo.log("left and has label");
16046                 cfg.cn = [
16047                     
16048                     {
16049                         tag: 'label',
16050                         'for' :  id,
16051                         cls : 'control-label col-md-' + this.labelWidth,
16052                         html : this.fieldLabel
16053                         
16054                     },
16055                     {
16056                         cls : "col-md-" + (12 - this.labelWidth), 
16057                         cn: [
16058                             inputblock
16059                         ]
16060                     }
16061                     
16062                 ];
16063         } else if ( this.fieldLabel.length) {
16064                 Roo.log(" label");
16065                 cfg.cn = [
16066                    
16067                     {
16068                         tag: this.boxLabel ? 'span' : 'label',
16069                         'for': id,
16070                         cls: 'control-label box-input-label',
16071                         //cls : 'input-group-addon',
16072                         html : this.fieldLabel
16073                         
16074                     },
16075                     
16076                     inputblock
16077                     
16078                 ];
16079
16080         } else {
16081             
16082                 Roo.log(" no label && no align");
16083                 cfg.cn = [  inputblock ] ;
16084                 
16085                 
16086         };
16087          if(this.boxLabel){
16088             cfg.cn.push( {
16089                 tag: 'label',
16090                 'for': id,
16091                 cls: 'box-label',
16092                 html: this.boxLabel
16093                 
16094             });
16095         }
16096         
16097         
16098        
16099         return cfg;
16100         
16101     },
16102     
16103     /**
16104      * return the real input element.
16105      */
16106     inputEl: function ()
16107     {
16108         return this.el.select('input.roo-checkbox',true).first();
16109     },
16110     
16111     label: function()
16112     {
16113         return this.el.select('label.control-label',true).first();
16114     },
16115     
16116     initEvents : function()
16117     {
16118 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16119         
16120         this.inputEl().on('click', this.onClick,  this);
16121         
16122     },
16123     
16124     onClick : function()
16125     {   
16126         this.setChecked(!this.checked);
16127     },
16128     
16129     setChecked : function(state,suppressEvent)
16130     {
16131         this.checked = state;
16132         
16133         this.inputEl().dom.checked = state;
16134         
16135         if(suppressEvent !== true){
16136             this.fireEvent('check', this, state);
16137         }
16138         
16139         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16140         
16141     },
16142     
16143     setValue : function(v,suppressEvent)
16144     {
16145         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16146     }
16147     
16148 });
16149
16150  
16151 /*
16152  * - LGPL
16153  *
16154  * Radio
16155  * 
16156  */
16157
16158 /**
16159  * @class Roo.bootstrap.Radio
16160  * @extends Roo.bootstrap.CheckBox
16161  * Bootstrap Radio class
16162
16163  * @constructor
16164  * Create a new Radio
16165  * @param {Object} config The config object
16166  */
16167
16168 Roo.bootstrap.Radio = function(config){
16169     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16170    
16171 };
16172
16173 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16174     
16175     inputType: 'radio',
16176     inputValue: '',
16177     valueOff: '',
16178     
16179     getAutoCreate : function()
16180     {
16181         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16182         
16183         var id = Roo.id();
16184         
16185         var cfg = {};
16186         
16187         cfg.cls = 'form-group radio' //input-group
16188         
16189         var input =  {
16190             tag: 'input',
16191             id : id,
16192             type : this.inputType,
16193             value : (!this.checked) ? this.valueOff : this.inputValue,
16194             cls : 'roo-radio',
16195             placeholder : this.placeholder || ''
16196             
16197         };
16198           if (this.weight) { // Validity check?
16199             cfg.cls += " radio-" + this.weight;
16200         }
16201         if (this.disabled) {
16202             input.disabled=true;
16203         }
16204         
16205         if(this.checked){
16206             input.checked = this.checked;
16207         }
16208         
16209         if (this.name) {
16210             input.name = this.name;
16211         }
16212         
16213         if (this.size) {
16214             input.cls += ' input-' + this.size;
16215         }
16216         
16217         var settings=this;
16218         ['xs','sm','md','lg'].map(function(size){
16219             if (settings[size]) {
16220                 cfg.cls += ' col-' + size + '-' + settings[size];
16221             }
16222         });
16223         
16224         var inputblock = input;
16225         
16226         if (this.before || this.after) {
16227             
16228             inputblock = {
16229                 cls : 'input-group',
16230                 cn :  [] 
16231             };
16232             if (this.before) {
16233                 inputblock.cn.push({
16234                     tag :'span',
16235                     cls : 'input-group-addon',
16236                     html : this.before
16237                 });
16238             }
16239             inputblock.cn.push(input);
16240             if (this.after) {
16241                 inputblock.cn.push({
16242                     tag :'span',
16243                     cls : 'input-group-addon',
16244                     html : this.after
16245                 });
16246             }
16247             
16248         };
16249         
16250         if (align ==='left' && this.fieldLabel.length) {
16251                 Roo.log("left and has label");
16252                 cfg.cn = [
16253                     
16254                     {
16255                         tag: 'label',
16256                         'for' :  id,
16257                         cls : 'control-label col-md-' + this.labelWidth,
16258                         html : this.fieldLabel
16259                         
16260                     },
16261                     {
16262                         cls : "col-md-" + (12 - this.labelWidth), 
16263                         cn: [
16264                             inputblock
16265                         ]
16266                     }
16267                     
16268                 ];
16269         } else if ( this.fieldLabel.length) {
16270                 Roo.log(" label");
16271                  cfg.cn = [
16272                    
16273                     {
16274                         tag: 'label',
16275                         'for': id,
16276                         cls: 'control-label box-input-label',
16277                         //cls : 'input-group-addon',
16278                         html : this.fieldLabel
16279                         
16280                     },
16281                     
16282                     inputblock
16283                     
16284                 ];
16285
16286         } else {
16287             
16288                    Roo.log(" no label && no align");
16289                 cfg.cn = [
16290                     
16291                         inputblock
16292                     
16293                 ];
16294                 
16295                 
16296         };
16297         
16298         if(this.boxLabel){
16299             cfg.cn.push({
16300                 tag: 'label',
16301                 'for': id,
16302                 cls: 'box-label',
16303                 html: this.boxLabel
16304             })
16305         }
16306         
16307         return cfg;
16308         
16309     },
16310     inputEl: function ()
16311     {
16312         return this.el.select('input.roo-radio',true).first();
16313     },
16314     onClick : function()
16315     {   
16316         this.setChecked(true);
16317     },
16318     
16319     setChecked : function(state,suppressEvent)
16320     {
16321         if(state){
16322             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16323                 v.dom.checked = false;
16324             });
16325         }
16326         
16327         this.checked = state;
16328         this.inputEl().dom.checked = state;
16329         
16330         if(suppressEvent !== true){
16331             this.fireEvent('check', this, state);
16332         }
16333         
16334         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16335         
16336     },
16337     
16338     getGroupValue : function()
16339     {
16340         var value = ''
16341         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16342             if(v.dom.checked == true){
16343                 value = v.dom.value;
16344             }
16345         });
16346         
16347         return value;
16348     },
16349     
16350     /**
16351      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16352      * @return {Mixed} value The field value
16353      */
16354     getValue : function(){
16355         return this.getGroupValue();
16356     }
16357     
16358 });
16359
16360  
16361 //<script type="text/javascript">
16362
16363 /*
16364  * Based  Ext JS Library 1.1.1
16365  * Copyright(c) 2006-2007, Ext JS, LLC.
16366  * LGPL
16367  *
16368  */
16369  
16370 /**
16371  * @class Roo.HtmlEditorCore
16372  * @extends Roo.Component
16373  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16374  *
16375  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16376  */
16377
16378 Roo.HtmlEditorCore = function(config){
16379     
16380     
16381     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16382     
16383     
16384     this.addEvents({
16385         /**
16386          * @event initialize
16387          * Fires when the editor is fully initialized (including the iframe)
16388          * @param {Roo.HtmlEditorCore} this
16389          */
16390         initialize: true,
16391         /**
16392          * @event activate
16393          * Fires when the editor is first receives the focus. Any insertion must wait
16394          * until after this event.
16395          * @param {Roo.HtmlEditorCore} this
16396          */
16397         activate: true,
16398          /**
16399          * @event beforesync
16400          * Fires before the textarea is updated with content from the editor iframe. Return false
16401          * to cancel the sync.
16402          * @param {Roo.HtmlEditorCore} this
16403          * @param {String} html
16404          */
16405         beforesync: true,
16406          /**
16407          * @event beforepush
16408          * Fires before the iframe editor is updated with content from the textarea. Return false
16409          * to cancel the push.
16410          * @param {Roo.HtmlEditorCore} this
16411          * @param {String} html
16412          */
16413         beforepush: true,
16414          /**
16415          * @event sync
16416          * Fires when the textarea is updated with content from the editor iframe.
16417          * @param {Roo.HtmlEditorCore} this
16418          * @param {String} html
16419          */
16420         sync: true,
16421          /**
16422          * @event push
16423          * Fires when the iframe editor is updated with content from the textarea.
16424          * @param {Roo.HtmlEditorCore} this
16425          * @param {String} html
16426          */
16427         push: true,
16428         
16429         /**
16430          * @event editorevent
16431          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16432          * @param {Roo.HtmlEditorCore} this
16433          */
16434         editorevent: true
16435     });
16436     
16437     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16438     
16439     // defaults : white / black...
16440     this.applyBlacklists();
16441     
16442     
16443     
16444 };
16445
16446
16447 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16448
16449
16450      /**
16451      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16452      */
16453     
16454     owner : false,
16455     
16456      /**
16457      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16458      *                        Roo.resizable.
16459      */
16460     resizable : false,
16461      /**
16462      * @cfg {Number} height (in pixels)
16463      */   
16464     height: 300,
16465    /**
16466      * @cfg {Number} width (in pixels)
16467      */   
16468     width: 500,
16469     
16470     /**
16471      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16472      * 
16473      */
16474     stylesheets: false,
16475     
16476     // id of frame..
16477     frameId: false,
16478     
16479     // private properties
16480     validationEvent : false,
16481     deferHeight: true,
16482     initialized : false,
16483     activated : false,
16484     sourceEditMode : false,
16485     onFocus : Roo.emptyFn,
16486     iframePad:3,
16487     hideMode:'offsets',
16488     
16489     clearUp: true,
16490     
16491     // blacklist + whitelisted elements..
16492     black: false,
16493     white: false,
16494      
16495     
16496
16497     /**
16498      * Protected method that will not generally be called directly. It
16499      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16500      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16501      */
16502     getDocMarkup : function(){
16503         // body styles..
16504         var st = '';
16505         Roo.log(this.stylesheets);
16506         
16507         // inherit styels from page...?? 
16508         if (this.stylesheets === false) {
16509             
16510             Roo.get(document.head).select('style').each(function(node) {
16511                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16512             });
16513             
16514             Roo.get(document.head).select('link').each(function(node) { 
16515                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16516             });
16517             
16518         } else if (!this.stylesheets.length) {
16519                 // simple..
16520                 st = '<style type="text/css">' +
16521                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16522                    '</style>';
16523         } else {
16524             Roo.each(this.stylesheets, function(s) {
16525                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16526             });
16527             
16528         }
16529         
16530         st +=  '<style type="text/css">' +
16531             'IMG { cursor: pointer } ' +
16532         '</style>';
16533
16534         
16535         return '<html><head>' + st  +
16536             //<style type="text/css">' +
16537             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16538             //'</style>' +
16539             ' </head><body class="roo-htmleditor-body"></body></html>';
16540     },
16541
16542     // private
16543     onRender : function(ct, position)
16544     {
16545         var _t = this;
16546         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16547         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16548         
16549         
16550         this.el.dom.style.border = '0 none';
16551         this.el.dom.setAttribute('tabIndex', -1);
16552         this.el.addClass('x-hidden hide');
16553         
16554         
16555         
16556         if(Roo.isIE){ // fix IE 1px bogus margin
16557             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16558         }
16559        
16560         
16561         this.frameId = Roo.id();
16562         
16563          
16564         
16565         var iframe = this.owner.wrap.createChild({
16566             tag: 'iframe',
16567             cls: 'form-control', // bootstrap..
16568             id: this.frameId,
16569             name: this.frameId,
16570             frameBorder : 'no',
16571             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16572         }, this.el
16573         );
16574         
16575         
16576         this.iframe = iframe.dom;
16577
16578          this.assignDocWin();
16579         
16580         this.doc.designMode = 'on';
16581        
16582         this.doc.open();
16583         this.doc.write(this.getDocMarkup());
16584         this.doc.close();
16585
16586         
16587         var task = { // must defer to wait for browser to be ready
16588             run : function(){
16589                 //console.log("run task?" + this.doc.readyState);
16590                 this.assignDocWin();
16591                 if(this.doc.body || this.doc.readyState == 'complete'){
16592                     try {
16593                         this.doc.designMode="on";
16594                     } catch (e) {
16595                         return;
16596                     }
16597                     Roo.TaskMgr.stop(task);
16598                     this.initEditor.defer(10, this);
16599                 }
16600             },
16601             interval : 10,
16602             duration: 10000,
16603             scope: this
16604         };
16605         Roo.TaskMgr.start(task);
16606
16607         
16608          
16609     },
16610
16611     // private
16612     onResize : function(w, h)
16613     {
16614          Roo.log('resize: ' +w + ',' + h );
16615         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16616         if(!this.iframe){
16617             return;
16618         }
16619         if(typeof w == 'number'){
16620             
16621             this.iframe.style.width = w + 'px';
16622         }
16623         if(typeof h == 'number'){
16624             
16625             this.iframe.style.height = h + 'px';
16626             if(this.doc){
16627                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16628             }
16629         }
16630         
16631     },
16632
16633     /**
16634      * Toggles the editor between standard and source edit mode.
16635      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16636      */
16637     toggleSourceEdit : function(sourceEditMode){
16638         
16639         this.sourceEditMode = sourceEditMode === true;
16640         
16641         if(this.sourceEditMode){
16642  
16643             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16644             
16645         }else{
16646             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16647             //this.iframe.className = '';
16648             this.deferFocus();
16649         }
16650         //this.setSize(this.owner.wrap.getSize());
16651         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16652     },
16653
16654     
16655   
16656
16657     /**
16658      * Protected method that will not generally be called directly. If you need/want
16659      * custom HTML cleanup, this is the method you should override.
16660      * @param {String} html The HTML to be cleaned
16661      * return {String} The cleaned HTML
16662      */
16663     cleanHtml : function(html){
16664         html = String(html);
16665         if(html.length > 5){
16666             if(Roo.isSafari){ // strip safari nonsense
16667                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16668             }
16669         }
16670         if(html == '&nbsp;'){
16671             html = '';
16672         }
16673         return html;
16674     },
16675
16676     /**
16677      * HTML Editor -> Textarea
16678      * Protected method that will not generally be called directly. Syncs the contents
16679      * of the editor iframe with the textarea.
16680      */
16681     syncValue : function(){
16682         if(this.initialized){
16683             var bd = (this.doc.body || this.doc.documentElement);
16684             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16685             var html = bd.innerHTML;
16686             if(Roo.isSafari){
16687                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16688                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16689                 if(m && m[1]){
16690                     html = '<div style="'+m[0]+'">' + html + '</div>';
16691                 }
16692             }
16693             html = this.cleanHtml(html);
16694             // fix up the special chars.. normaly like back quotes in word...
16695             // however we do not want to do this with chinese..
16696             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16697                 var cc = b.charCodeAt();
16698                 if (
16699                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16700                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16701                     (cc >= 0xf900 && cc < 0xfb00 )
16702                 ) {
16703                         return b;
16704                 }
16705                 return "&#"+cc+";" 
16706             });
16707             if(this.owner.fireEvent('beforesync', this, html) !== false){
16708                 this.el.dom.value = html;
16709                 this.owner.fireEvent('sync', this, html);
16710             }
16711         }
16712     },
16713
16714     /**
16715      * Protected method that will not generally be called directly. Pushes the value of the textarea
16716      * into the iframe editor.
16717      */
16718     pushValue : function(){
16719         if(this.initialized){
16720             var v = this.el.dom.value.trim();
16721             
16722 //            if(v.length < 1){
16723 //                v = '&#160;';
16724 //            }
16725             
16726             if(this.owner.fireEvent('beforepush', this, v) !== false){
16727                 var d = (this.doc.body || this.doc.documentElement);
16728                 d.innerHTML = v;
16729                 this.cleanUpPaste();
16730                 this.el.dom.value = d.innerHTML;
16731                 this.owner.fireEvent('push', this, v);
16732             }
16733         }
16734     },
16735
16736     // private
16737     deferFocus : function(){
16738         this.focus.defer(10, this);
16739     },
16740
16741     // doc'ed in Field
16742     focus : function(){
16743         if(this.win && !this.sourceEditMode){
16744             this.win.focus();
16745         }else{
16746             this.el.focus();
16747         }
16748     },
16749     
16750     assignDocWin: function()
16751     {
16752         var iframe = this.iframe;
16753         
16754          if(Roo.isIE){
16755             this.doc = iframe.contentWindow.document;
16756             this.win = iframe.contentWindow;
16757         } else {
16758 //            if (!Roo.get(this.frameId)) {
16759 //                return;
16760 //            }
16761 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16762 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16763             
16764             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16765                 return;
16766             }
16767             
16768             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16769             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16770         }
16771     },
16772     
16773     // private
16774     initEditor : function(){
16775         //console.log("INIT EDITOR");
16776         this.assignDocWin();
16777         
16778         
16779         
16780         this.doc.designMode="on";
16781         this.doc.open();
16782         this.doc.write(this.getDocMarkup());
16783         this.doc.close();
16784         
16785         var dbody = (this.doc.body || this.doc.documentElement);
16786         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16787         // this copies styles from the containing element into thsi one..
16788         // not sure why we need all of this..
16789         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16790         
16791         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16792         //ss['background-attachment'] = 'fixed'; // w3c
16793         dbody.bgProperties = 'fixed'; // ie
16794         //Roo.DomHelper.applyStyles(dbody, ss);
16795         Roo.EventManager.on(this.doc, {
16796             //'mousedown': this.onEditorEvent,
16797             'mouseup': this.onEditorEvent,
16798             'dblclick': this.onEditorEvent,
16799             'click': this.onEditorEvent,
16800             'keyup': this.onEditorEvent,
16801             buffer:100,
16802             scope: this
16803         });
16804         if(Roo.isGecko){
16805             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16806         }
16807         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16808             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16809         }
16810         this.initialized = true;
16811
16812         this.owner.fireEvent('initialize', this);
16813         this.pushValue();
16814     },
16815
16816     // private
16817     onDestroy : function(){
16818         
16819         
16820         
16821         if(this.rendered){
16822             
16823             //for (var i =0; i < this.toolbars.length;i++) {
16824             //    // fixme - ask toolbars for heights?
16825             //    this.toolbars[i].onDestroy();
16826            // }
16827             
16828             //this.wrap.dom.innerHTML = '';
16829             //this.wrap.remove();
16830         }
16831     },
16832
16833     // private
16834     onFirstFocus : function(){
16835         
16836         this.assignDocWin();
16837         
16838         
16839         this.activated = true;
16840          
16841     
16842         if(Roo.isGecko){ // prevent silly gecko errors
16843             this.win.focus();
16844             var s = this.win.getSelection();
16845             if(!s.focusNode || s.focusNode.nodeType != 3){
16846                 var r = s.getRangeAt(0);
16847                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16848                 r.collapse(true);
16849                 this.deferFocus();
16850             }
16851             try{
16852                 this.execCmd('useCSS', true);
16853                 this.execCmd('styleWithCSS', false);
16854             }catch(e){}
16855         }
16856         this.owner.fireEvent('activate', this);
16857     },
16858
16859     // private
16860     adjustFont: function(btn){
16861         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16862         //if(Roo.isSafari){ // safari
16863         //    adjust *= 2;
16864        // }
16865         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16866         if(Roo.isSafari){ // safari
16867             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16868             v =  (v < 10) ? 10 : v;
16869             v =  (v > 48) ? 48 : v;
16870             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16871             
16872         }
16873         
16874         
16875         v = Math.max(1, v+adjust);
16876         
16877         this.execCmd('FontSize', v  );
16878     },
16879
16880     onEditorEvent : function(e){
16881         this.owner.fireEvent('editorevent', this, e);
16882       //  this.updateToolbar();
16883         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16884     },
16885
16886     insertTag : function(tg)
16887     {
16888         // could be a bit smarter... -> wrap the current selected tRoo..
16889         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16890             
16891             range = this.createRange(this.getSelection());
16892             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16893             wrappingNode.appendChild(range.extractContents());
16894             range.insertNode(wrappingNode);
16895
16896             return;
16897             
16898             
16899             
16900         }
16901         this.execCmd("formatblock",   tg);
16902         
16903     },
16904     
16905     insertText : function(txt)
16906     {
16907         
16908         
16909         var range = this.createRange();
16910         range.deleteContents();
16911                //alert(Sender.getAttribute('label'));
16912                
16913         range.insertNode(this.doc.createTextNode(txt));
16914     } ,
16915     
16916      
16917
16918     /**
16919      * Executes a Midas editor command on the editor document and performs necessary focus and
16920      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16921      * @param {String} cmd The Midas command
16922      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16923      */
16924     relayCmd : function(cmd, value){
16925         this.win.focus();
16926         this.execCmd(cmd, value);
16927         this.owner.fireEvent('editorevent', this);
16928         //this.updateToolbar();
16929         this.owner.deferFocus();
16930     },
16931
16932     /**
16933      * Executes a Midas editor command directly on the editor document.
16934      * For visual commands, you should use {@link #relayCmd} instead.
16935      * <b>This should only be called after the editor is initialized.</b>
16936      * @param {String} cmd The Midas command
16937      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16938      */
16939     execCmd : function(cmd, value){
16940         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16941         this.syncValue();
16942     },
16943  
16944  
16945    
16946     /**
16947      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16948      * to insert tRoo.
16949      * @param {String} text | dom node.. 
16950      */
16951     insertAtCursor : function(text)
16952     {
16953         
16954         
16955         
16956         if(!this.activated){
16957             return;
16958         }
16959         /*
16960         if(Roo.isIE){
16961             this.win.focus();
16962             var r = this.doc.selection.createRange();
16963             if(r){
16964                 r.collapse(true);
16965                 r.pasteHTML(text);
16966                 this.syncValue();
16967                 this.deferFocus();
16968             
16969             }
16970             return;
16971         }
16972         */
16973         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16974             this.win.focus();
16975             
16976             
16977             // from jquery ui (MIT licenced)
16978             var range, node;
16979             var win = this.win;
16980             
16981             if (win.getSelection && win.getSelection().getRangeAt) {
16982                 range = win.getSelection().getRangeAt(0);
16983                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16984                 range.insertNode(node);
16985             } else if (win.document.selection && win.document.selection.createRange) {
16986                 // no firefox support
16987                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16988                 win.document.selection.createRange().pasteHTML(txt);
16989             } else {
16990                 // no firefox support
16991                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16992                 this.execCmd('InsertHTML', txt);
16993             } 
16994             
16995             this.syncValue();
16996             
16997             this.deferFocus();
16998         }
16999     },
17000  // private
17001     mozKeyPress : function(e){
17002         if(e.ctrlKey){
17003             var c = e.getCharCode(), cmd;
17004           
17005             if(c > 0){
17006                 c = String.fromCharCode(c).toLowerCase();
17007                 switch(c){
17008                     case 'b':
17009                         cmd = 'bold';
17010                         break;
17011                     case 'i':
17012                         cmd = 'italic';
17013                         break;
17014                     
17015                     case 'u':
17016                         cmd = 'underline';
17017                         break;
17018                     
17019                     case 'v':
17020                         this.cleanUpPaste.defer(100, this);
17021                         return;
17022                         
17023                 }
17024                 if(cmd){
17025                     this.win.focus();
17026                     this.execCmd(cmd);
17027                     this.deferFocus();
17028                     e.preventDefault();
17029                 }
17030                 
17031             }
17032         }
17033     },
17034
17035     // private
17036     fixKeys : function(){ // load time branching for fastest keydown performance
17037         if(Roo.isIE){
17038             return function(e){
17039                 var k = e.getKey(), r;
17040                 if(k == e.TAB){
17041                     e.stopEvent();
17042                     r = this.doc.selection.createRange();
17043                     if(r){
17044                         r.collapse(true);
17045                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17046                         this.deferFocus();
17047                     }
17048                     return;
17049                 }
17050                 
17051                 if(k == e.ENTER){
17052                     r = this.doc.selection.createRange();
17053                     if(r){
17054                         var target = r.parentElement();
17055                         if(!target || target.tagName.toLowerCase() != 'li'){
17056                             e.stopEvent();
17057                             r.pasteHTML('<br />');
17058                             r.collapse(false);
17059                             r.select();
17060                         }
17061                     }
17062                 }
17063                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17064                     this.cleanUpPaste.defer(100, this);
17065                     return;
17066                 }
17067                 
17068                 
17069             };
17070         }else if(Roo.isOpera){
17071             return function(e){
17072                 var k = e.getKey();
17073                 if(k == e.TAB){
17074                     e.stopEvent();
17075                     this.win.focus();
17076                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17077                     this.deferFocus();
17078                 }
17079                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17080                     this.cleanUpPaste.defer(100, this);
17081                     return;
17082                 }
17083                 
17084             };
17085         }else if(Roo.isSafari){
17086             return function(e){
17087                 var k = e.getKey();
17088                 
17089                 if(k == e.TAB){
17090                     e.stopEvent();
17091                     this.execCmd('InsertText','\t');
17092                     this.deferFocus();
17093                     return;
17094                 }
17095                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17096                     this.cleanUpPaste.defer(100, this);
17097                     return;
17098                 }
17099                 
17100              };
17101         }
17102     }(),
17103     
17104     getAllAncestors: function()
17105     {
17106         var p = this.getSelectedNode();
17107         var a = [];
17108         if (!p) {
17109             a.push(p); // push blank onto stack..
17110             p = this.getParentElement();
17111         }
17112         
17113         
17114         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17115             a.push(p);
17116             p = p.parentNode;
17117         }
17118         a.push(this.doc.body);
17119         return a;
17120     },
17121     lastSel : false,
17122     lastSelNode : false,
17123     
17124     
17125     getSelection : function() 
17126     {
17127         this.assignDocWin();
17128         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17129     },
17130     
17131     getSelectedNode: function() 
17132     {
17133         // this may only work on Gecko!!!
17134         
17135         // should we cache this!!!!
17136         
17137         
17138         
17139          
17140         var range = this.createRange(this.getSelection()).cloneRange();
17141         
17142         if (Roo.isIE) {
17143             var parent = range.parentElement();
17144             while (true) {
17145                 var testRange = range.duplicate();
17146                 testRange.moveToElementText(parent);
17147                 if (testRange.inRange(range)) {
17148                     break;
17149                 }
17150                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17151                     break;
17152                 }
17153                 parent = parent.parentElement;
17154             }
17155             return parent;
17156         }
17157         
17158         // is ancestor a text element.
17159         var ac =  range.commonAncestorContainer;
17160         if (ac.nodeType == 3) {
17161             ac = ac.parentNode;
17162         }
17163         
17164         var ar = ac.childNodes;
17165          
17166         var nodes = [];
17167         var other_nodes = [];
17168         var has_other_nodes = false;
17169         for (var i=0;i<ar.length;i++) {
17170             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17171                 continue;
17172             }
17173             // fullly contained node.
17174             
17175             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17176                 nodes.push(ar[i]);
17177                 continue;
17178             }
17179             
17180             // probably selected..
17181             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17182                 other_nodes.push(ar[i]);
17183                 continue;
17184             }
17185             // outer..
17186             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17187                 continue;
17188             }
17189             
17190             
17191             has_other_nodes = true;
17192         }
17193         if (!nodes.length && other_nodes.length) {
17194             nodes= other_nodes;
17195         }
17196         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17197             return false;
17198         }
17199         
17200         return nodes[0];
17201     },
17202     createRange: function(sel)
17203     {
17204         // this has strange effects when using with 
17205         // top toolbar - not sure if it's a great idea.
17206         //this.editor.contentWindow.focus();
17207         if (typeof sel != "undefined") {
17208             try {
17209                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17210             } catch(e) {
17211                 return this.doc.createRange();
17212             }
17213         } else {
17214             return this.doc.createRange();
17215         }
17216     },
17217     getParentElement: function()
17218     {
17219         
17220         this.assignDocWin();
17221         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17222         
17223         var range = this.createRange(sel);
17224          
17225         try {
17226             var p = range.commonAncestorContainer;
17227             while (p.nodeType == 3) { // text node
17228                 p = p.parentNode;
17229             }
17230             return p;
17231         } catch (e) {
17232             return null;
17233         }
17234     
17235     },
17236     /***
17237      *
17238      * Range intersection.. the hard stuff...
17239      *  '-1' = before
17240      *  '0' = hits..
17241      *  '1' = after.
17242      *         [ -- selected range --- ]
17243      *   [fail]                        [fail]
17244      *
17245      *    basically..
17246      *      if end is before start or  hits it. fail.
17247      *      if start is after end or hits it fail.
17248      *
17249      *   if either hits (but other is outside. - then it's not 
17250      *   
17251      *    
17252      **/
17253     
17254     
17255     // @see http://www.thismuchiknow.co.uk/?p=64.
17256     rangeIntersectsNode : function(range, node)
17257     {
17258         var nodeRange = node.ownerDocument.createRange();
17259         try {
17260             nodeRange.selectNode(node);
17261         } catch (e) {
17262             nodeRange.selectNodeContents(node);
17263         }
17264     
17265         var rangeStartRange = range.cloneRange();
17266         rangeStartRange.collapse(true);
17267     
17268         var rangeEndRange = range.cloneRange();
17269         rangeEndRange.collapse(false);
17270     
17271         var nodeStartRange = nodeRange.cloneRange();
17272         nodeStartRange.collapse(true);
17273     
17274         var nodeEndRange = nodeRange.cloneRange();
17275         nodeEndRange.collapse(false);
17276     
17277         return rangeStartRange.compareBoundaryPoints(
17278                  Range.START_TO_START, nodeEndRange) == -1 &&
17279                rangeEndRange.compareBoundaryPoints(
17280                  Range.START_TO_START, nodeStartRange) == 1;
17281         
17282          
17283     },
17284     rangeCompareNode : function(range, node)
17285     {
17286         var nodeRange = node.ownerDocument.createRange();
17287         try {
17288             nodeRange.selectNode(node);
17289         } catch (e) {
17290             nodeRange.selectNodeContents(node);
17291         }
17292         
17293         
17294         range.collapse(true);
17295     
17296         nodeRange.collapse(true);
17297      
17298         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17299         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17300          
17301         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17302         
17303         var nodeIsBefore   =  ss == 1;
17304         var nodeIsAfter    = ee == -1;
17305         
17306         if (nodeIsBefore && nodeIsAfter)
17307             return 0; // outer
17308         if (!nodeIsBefore && nodeIsAfter)
17309             return 1; //right trailed.
17310         
17311         if (nodeIsBefore && !nodeIsAfter)
17312             return 2;  // left trailed.
17313         // fully contined.
17314         return 3;
17315     },
17316
17317     // private? - in a new class?
17318     cleanUpPaste :  function()
17319     {
17320         // cleans up the whole document..
17321         Roo.log('cleanuppaste');
17322         
17323         this.cleanUpChildren(this.doc.body);
17324         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17325         if (clean != this.doc.body.innerHTML) {
17326             this.doc.body.innerHTML = clean;
17327         }
17328         
17329     },
17330     
17331     cleanWordChars : function(input) {// change the chars to hex code
17332         var he = Roo.HtmlEditorCore;
17333         
17334         var output = input;
17335         Roo.each(he.swapCodes, function(sw) { 
17336             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17337             
17338             output = output.replace(swapper, sw[1]);
17339         });
17340         
17341         return output;
17342     },
17343     
17344     
17345     cleanUpChildren : function (n)
17346     {
17347         if (!n.childNodes.length) {
17348             return;
17349         }
17350         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17351            this.cleanUpChild(n.childNodes[i]);
17352         }
17353     },
17354     
17355     
17356         
17357     
17358     cleanUpChild : function (node)
17359     {
17360         var ed = this;
17361         //console.log(node);
17362         if (node.nodeName == "#text") {
17363             // clean up silly Windows -- stuff?
17364             return; 
17365         }
17366         if (node.nodeName == "#comment") {
17367             node.parentNode.removeChild(node);
17368             // clean up silly Windows -- stuff?
17369             return; 
17370         }
17371         var lcname = node.tagName.toLowerCase();
17372         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17373         // whitelist of tags..
17374         
17375         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17376             // remove node.
17377             node.parentNode.removeChild(node);
17378             return;
17379             
17380         }
17381         
17382         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17383         
17384         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17385         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17386         
17387         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17388         //    remove_keep_children = true;
17389         //}
17390         
17391         if (remove_keep_children) {
17392             this.cleanUpChildren(node);
17393             // inserts everything just before this node...
17394             while (node.childNodes.length) {
17395                 var cn = node.childNodes[0];
17396                 node.removeChild(cn);
17397                 node.parentNode.insertBefore(cn, node);
17398             }
17399             node.parentNode.removeChild(node);
17400             return;
17401         }
17402         
17403         if (!node.attributes || !node.attributes.length) {
17404             this.cleanUpChildren(node);
17405             return;
17406         }
17407         
17408         function cleanAttr(n,v)
17409         {
17410             
17411             if (v.match(/^\./) || v.match(/^\//)) {
17412                 return;
17413             }
17414             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17415                 return;
17416             }
17417             if (v.match(/^#/)) {
17418                 return;
17419             }
17420 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17421             node.removeAttribute(n);
17422             
17423         }
17424         
17425         var cwhite = this.cwhite;
17426         var cblack = this.cblack;
17427             
17428         function cleanStyle(n,v)
17429         {
17430             if (v.match(/expression/)) { //XSS?? should we even bother..
17431                 node.removeAttribute(n);
17432                 return;
17433             }
17434             
17435             var parts = v.split(/;/);
17436             var clean = [];
17437             
17438             Roo.each(parts, function(p) {
17439                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17440                 if (!p.length) {
17441                     return true;
17442                 }
17443                 var l = p.split(':').shift().replace(/\s+/g,'');
17444                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17445                 
17446                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17447 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17448                     //node.removeAttribute(n);
17449                     return true;
17450                 }
17451                 //Roo.log()
17452                 // only allow 'c whitelisted system attributes'
17453                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17454 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17455                     //node.removeAttribute(n);
17456                     return true;
17457                 }
17458                 
17459                 
17460                  
17461                 
17462                 clean.push(p);
17463                 return true;
17464             });
17465             if (clean.length) { 
17466                 node.setAttribute(n, clean.join(';'));
17467             } else {
17468                 node.removeAttribute(n);
17469             }
17470             
17471         }
17472         
17473         
17474         for (var i = node.attributes.length-1; i > -1 ; i--) {
17475             var a = node.attributes[i];
17476             //console.log(a);
17477             
17478             if (a.name.toLowerCase().substr(0,2)=='on')  {
17479                 node.removeAttribute(a.name);
17480                 continue;
17481             }
17482             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17483                 node.removeAttribute(a.name);
17484                 continue;
17485             }
17486             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17487                 cleanAttr(a.name,a.value); // fixme..
17488                 continue;
17489             }
17490             if (a.name == 'style') {
17491                 cleanStyle(a.name,a.value);
17492                 continue;
17493             }
17494             /// clean up MS crap..
17495             // tecnically this should be a list of valid class'es..
17496             
17497             
17498             if (a.name == 'class') {
17499                 if (a.value.match(/^Mso/)) {
17500                     node.className = '';
17501                 }
17502                 
17503                 if (a.value.match(/body/)) {
17504                     node.className = '';
17505                 }
17506                 continue;
17507             }
17508             
17509             // style cleanup!?
17510             // class cleanup?
17511             
17512         }
17513         
17514         
17515         this.cleanUpChildren(node);
17516         
17517         
17518     },
17519     /**
17520      * Clean up MS wordisms...
17521      */
17522     cleanWord : function(node)
17523     {
17524         var _t = this;
17525         var cleanWordChildren = function()
17526         {
17527             if (!node.childNodes.length) {
17528                 return;
17529             }
17530             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17531                _t.cleanWord(node.childNodes[i]);
17532             }
17533         }
17534         
17535         
17536         if (!node) {
17537             this.cleanWord(this.doc.body);
17538             return;
17539         }
17540         if (node.nodeName == "#text") {
17541             // clean up silly Windows -- stuff?
17542             return; 
17543         }
17544         if (node.nodeName == "#comment") {
17545             node.parentNode.removeChild(node);
17546             // clean up silly Windows -- stuff?
17547             return; 
17548         }
17549         
17550         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17551             node.parentNode.removeChild(node);
17552             return;
17553         }
17554         
17555         // remove - but keep children..
17556         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17557             while (node.childNodes.length) {
17558                 var cn = node.childNodes[0];
17559                 node.removeChild(cn);
17560                 node.parentNode.insertBefore(cn, node);
17561             }
17562             node.parentNode.removeChild(node);
17563             cleanWordChildren();
17564             return;
17565         }
17566         // clean styles
17567         if (node.className.length) {
17568             
17569             var cn = node.className.split(/\W+/);
17570             var cna = [];
17571             Roo.each(cn, function(cls) {
17572                 if (cls.match(/Mso[a-zA-Z]+/)) {
17573                     return;
17574                 }
17575                 cna.push(cls);
17576             });
17577             node.className = cna.length ? cna.join(' ') : '';
17578             if (!cna.length) {
17579                 node.removeAttribute("class");
17580             }
17581         }
17582         
17583         if (node.hasAttribute("lang")) {
17584             node.removeAttribute("lang");
17585         }
17586         
17587         if (node.hasAttribute("style")) {
17588             
17589             var styles = node.getAttribute("style").split(";");
17590             var nstyle = [];
17591             Roo.each(styles, function(s) {
17592                 if (!s.match(/:/)) {
17593                     return;
17594                 }
17595                 var kv = s.split(":");
17596                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17597                     return;
17598                 }
17599                 // what ever is left... we allow.
17600                 nstyle.push(s);
17601             });
17602             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17603             if (!nstyle.length) {
17604                 node.removeAttribute('style');
17605             }
17606         }
17607         
17608         cleanWordChildren();
17609         
17610         
17611     },
17612     domToHTML : function(currentElement, depth, nopadtext) {
17613         
17614         depth = depth || 0;
17615         nopadtext = nopadtext || false;
17616     
17617         if (!currentElement) {
17618             return this.domToHTML(this.doc.body);
17619         }
17620         
17621         //Roo.log(currentElement);
17622         var j;
17623         var allText = false;
17624         var nodeName = currentElement.nodeName;
17625         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17626         
17627         if  (nodeName == '#text') {
17628             return currentElement.nodeValue;
17629         }
17630         
17631         
17632         var ret = '';
17633         if (nodeName != 'BODY') {
17634              
17635             var i = 0;
17636             // Prints the node tagName, such as <A>, <IMG>, etc
17637             if (tagName) {
17638                 var attr = [];
17639                 for(i = 0; i < currentElement.attributes.length;i++) {
17640                     // quoting?
17641                     var aname = currentElement.attributes.item(i).name;
17642                     if (!currentElement.attributes.item(i).value.length) {
17643                         continue;
17644                     }
17645                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17646                 }
17647                 
17648                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17649             } 
17650             else {
17651                 
17652                 // eack
17653             }
17654         } else {
17655             tagName = false;
17656         }
17657         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17658             return ret;
17659         }
17660         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17661             nopadtext = true;
17662         }
17663         
17664         
17665         // Traverse the tree
17666         i = 0;
17667         var currentElementChild = currentElement.childNodes.item(i);
17668         var allText = true;
17669         var innerHTML  = '';
17670         lastnode = '';
17671         while (currentElementChild) {
17672             // Formatting code (indent the tree so it looks nice on the screen)
17673             var nopad = nopadtext;
17674             if (lastnode == 'SPAN') {
17675                 nopad  = true;
17676             }
17677             // text
17678             if  (currentElementChild.nodeName == '#text') {
17679                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17680                 if (!nopad && toadd.length > 80) {
17681                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17682                 }
17683                 innerHTML  += toadd;
17684                 
17685                 i++;
17686                 currentElementChild = currentElement.childNodes.item(i);
17687                 lastNode = '';
17688                 continue;
17689             }
17690             allText = false;
17691             
17692             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17693                 
17694             // Recursively traverse the tree structure of the child node
17695             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17696             lastnode = currentElementChild.nodeName;
17697             i++;
17698             currentElementChild=currentElement.childNodes.item(i);
17699         }
17700         
17701         ret += innerHTML;
17702         
17703         if (!allText) {
17704                 // The remaining code is mostly for formatting the tree
17705             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17706         }
17707         
17708         
17709         if (tagName) {
17710             ret+= "</"+tagName+">";
17711         }
17712         return ret;
17713         
17714     },
17715         
17716     applyBlacklists : function()
17717     {
17718         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17719         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17720         
17721         this.white = [];
17722         this.black = [];
17723         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17724             if (b.indexOf(tag) > -1) {
17725                 return;
17726             }
17727             this.white.push(tag);
17728             
17729         }, this);
17730         
17731         Roo.each(w, function(tag) {
17732             if (b.indexOf(tag) > -1) {
17733                 return;
17734             }
17735             if (this.white.indexOf(tag) > -1) {
17736                 return;
17737             }
17738             this.white.push(tag);
17739             
17740         }, this);
17741         
17742         
17743         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17744             if (w.indexOf(tag) > -1) {
17745                 return;
17746             }
17747             this.black.push(tag);
17748             
17749         }, this);
17750         
17751         Roo.each(b, function(tag) {
17752             if (w.indexOf(tag) > -1) {
17753                 return;
17754             }
17755             if (this.black.indexOf(tag) > -1) {
17756                 return;
17757             }
17758             this.black.push(tag);
17759             
17760         }, this);
17761         
17762         
17763         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17764         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17765         
17766         this.cwhite = [];
17767         this.cblack = [];
17768         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17769             if (b.indexOf(tag) > -1) {
17770                 return;
17771             }
17772             this.cwhite.push(tag);
17773             
17774         }, this);
17775         
17776         Roo.each(w, function(tag) {
17777             if (b.indexOf(tag) > -1) {
17778                 return;
17779             }
17780             if (this.cwhite.indexOf(tag) > -1) {
17781                 return;
17782             }
17783             this.cwhite.push(tag);
17784             
17785         }, this);
17786         
17787         
17788         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17789             if (w.indexOf(tag) > -1) {
17790                 return;
17791             }
17792             this.cblack.push(tag);
17793             
17794         }, this);
17795         
17796         Roo.each(b, function(tag) {
17797             if (w.indexOf(tag) > -1) {
17798                 return;
17799             }
17800             if (this.cblack.indexOf(tag) > -1) {
17801                 return;
17802             }
17803             this.cblack.push(tag);
17804             
17805         }, this);
17806     }
17807     
17808     // hide stuff that is not compatible
17809     /**
17810      * @event blur
17811      * @hide
17812      */
17813     /**
17814      * @event change
17815      * @hide
17816      */
17817     /**
17818      * @event focus
17819      * @hide
17820      */
17821     /**
17822      * @event specialkey
17823      * @hide
17824      */
17825     /**
17826      * @cfg {String} fieldClass @hide
17827      */
17828     /**
17829      * @cfg {String} focusClass @hide
17830      */
17831     /**
17832      * @cfg {String} autoCreate @hide
17833      */
17834     /**
17835      * @cfg {String} inputType @hide
17836      */
17837     /**
17838      * @cfg {String} invalidClass @hide
17839      */
17840     /**
17841      * @cfg {String} invalidText @hide
17842      */
17843     /**
17844      * @cfg {String} msgFx @hide
17845      */
17846     /**
17847      * @cfg {String} validateOnBlur @hide
17848      */
17849 });
17850
17851 Roo.HtmlEditorCore.white = [
17852         'area', 'br', 'img', 'input', 'hr', 'wbr',
17853         
17854        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17855        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17856        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17857        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17858        'table',   'ul',         'xmp', 
17859        
17860        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17861       'thead',   'tr', 
17862      
17863       'dir', 'menu', 'ol', 'ul', 'dl',
17864        
17865       'embed',  'object'
17866 ];
17867
17868
17869 Roo.HtmlEditorCore.black = [
17870     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17871         'applet', // 
17872         'base',   'basefont', 'bgsound', 'blink',  'body', 
17873         'frame',  'frameset', 'head',    'html',   'ilayer', 
17874         'iframe', 'layer',  'link',     'meta',    'object',   
17875         'script', 'style' ,'title',  'xml' // clean later..
17876 ];
17877 Roo.HtmlEditorCore.clean = [
17878     'script', 'style', 'title', 'xml'
17879 ];
17880 Roo.HtmlEditorCore.remove = [
17881     'font'
17882 ];
17883 // attributes..
17884
17885 Roo.HtmlEditorCore.ablack = [
17886     'on'
17887 ];
17888     
17889 Roo.HtmlEditorCore.aclean = [ 
17890     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17891 ];
17892
17893 // protocols..
17894 Roo.HtmlEditorCore.pwhite= [
17895         'http',  'https',  'mailto'
17896 ];
17897
17898 // white listed style attributes.
17899 Roo.HtmlEditorCore.cwhite= [
17900       //  'text-align', /// default is to allow most things..
17901       
17902          
17903 //        'font-size'//??
17904 ];
17905
17906 // black listed style attributes.
17907 Roo.HtmlEditorCore.cblack= [
17908       //  'font-size' -- this can be set by the project 
17909 ];
17910
17911
17912 Roo.HtmlEditorCore.swapCodes   =[ 
17913     [    8211, "--" ], 
17914     [    8212, "--" ], 
17915     [    8216,  "'" ],  
17916     [    8217, "'" ],  
17917     [    8220, '"' ],  
17918     [    8221, '"' ],  
17919     [    8226, "*" ],  
17920     [    8230, "..." ]
17921 ]; 
17922
17923     /*
17924  * - LGPL
17925  *
17926  * HtmlEditor
17927  * 
17928  */
17929
17930 /**
17931  * @class Roo.bootstrap.HtmlEditor
17932  * @extends Roo.bootstrap.TextArea
17933  * Bootstrap HtmlEditor class
17934
17935  * @constructor
17936  * Create a new HtmlEditor
17937  * @param {Object} config The config object
17938  */
17939
17940 Roo.bootstrap.HtmlEditor = function(config){
17941     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17942     if (!this.toolbars) {
17943         this.toolbars = [];
17944     }
17945     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17946     this.addEvents({
17947             /**
17948              * @event initialize
17949              * Fires when the editor is fully initialized (including the iframe)
17950              * @param {HtmlEditor} this
17951              */
17952             initialize: true,
17953             /**
17954              * @event activate
17955              * Fires when the editor is first receives the focus. Any insertion must wait
17956              * until after this event.
17957              * @param {HtmlEditor} this
17958              */
17959             activate: true,
17960              /**
17961              * @event beforesync
17962              * Fires before the textarea is updated with content from the editor iframe. Return false
17963              * to cancel the sync.
17964              * @param {HtmlEditor} this
17965              * @param {String} html
17966              */
17967             beforesync: true,
17968              /**
17969              * @event beforepush
17970              * Fires before the iframe editor is updated with content from the textarea. Return false
17971              * to cancel the push.
17972              * @param {HtmlEditor} this
17973              * @param {String} html
17974              */
17975             beforepush: true,
17976              /**
17977              * @event sync
17978              * Fires when the textarea is updated with content from the editor iframe.
17979              * @param {HtmlEditor} this
17980              * @param {String} html
17981              */
17982             sync: true,
17983              /**
17984              * @event push
17985              * Fires when the iframe editor is updated with content from the textarea.
17986              * @param {HtmlEditor} this
17987              * @param {String} html
17988              */
17989             push: true,
17990              /**
17991              * @event editmodechange
17992              * Fires when the editor switches edit modes
17993              * @param {HtmlEditor} this
17994              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17995              */
17996             editmodechange: true,
17997             /**
17998              * @event editorevent
17999              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18000              * @param {HtmlEditor} this
18001              */
18002             editorevent: true,
18003             /**
18004              * @event firstfocus
18005              * Fires when on first focus - needed by toolbars..
18006              * @param {HtmlEditor} this
18007              */
18008             firstfocus: true,
18009             /**
18010              * @event autosave
18011              * Auto save the htmlEditor value as a file into Events
18012              * @param {HtmlEditor} this
18013              */
18014             autosave: true,
18015             /**
18016              * @event savedpreview
18017              * preview the saved version of htmlEditor
18018              * @param {HtmlEditor} this
18019              */
18020             savedpreview: true
18021         });
18022 };
18023
18024
18025 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18026     
18027     
18028       /**
18029      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18030      */
18031     toolbars : false,
18032    
18033      /**
18034      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18035      *                        Roo.resizable.
18036      */
18037     resizable : false,
18038      /**
18039      * @cfg {Number} height (in pixels)
18040      */   
18041     height: 300,
18042    /**
18043      * @cfg {Number} width (in pixels)
18044      */   
18045     width: false,
18046     
18047     /**
18048      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18049      * 
18050      */
18051     stylesheets: false,
18052     
18053     // id of frame..
18054     frameId: false,
18055     
18056     // private properties
18057     validationEvent : false,
18058     deferHeight: true,
18059     initialized : false,
18060     activated : false,
18061     
18062     onFocus : Roo.emptyFn,
18063     iframePad:3,
18064     hideMode:'offsets',
18065     
18066     
18067     tbContainer : false,
18068     
18069     toolbarContainer :function() {
18070         return this.wrap.select('.x-html-editor-tb',true).first();
18071     },
18072
18073     /**
18074      * Protected method that will not generally be called directly. It
18075      * is called when the editor creates its toolbar. Override this method if you need to
18076      * add custom toolbar buttons.
18077      * @param {HtmlEditor} editor
18078      */
18079     createToolbar : function(){
18080         
18081         Roo.log("create toolbars");
18082         
18083         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18084         this.toolbars[0].render(this.toolbarContainer());
18085         
18086         return;
18087         
18088 //        if (!editor.toolbars || !editor.toolbars.length) {
18089 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18090 //        }
18091 //        
18092 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18093 //            editor.toolbars[i] = Roo.factory(
18094 //                    typeof(editor.toolbars[i]) == 'string' ?
18095 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18096 //                Roo.bootstrap.HtmlEditor);
18097 //            editor.toolbars[i].init(editor);
18098 //        }
18099     },
18100
18101      
18102     // private
18103     onRender : function(ct, position)
18104     {
18105        // Roo.log("Call onRender: " + this.xtype);
18106         var _t = this;
18107         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18108       
18109         this.wrap = this.inputEl().wrap({
18110             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18111         });
18112         
18113         this.editorcore.onRender(ct, position);
18114          
18115         if (this.resizable) {
18116             this.resizeEl = new Roo.Resizable(this.wrap, {
18117                 pinned : true,
18118                 wrap: true,
18119                 dynamic : true,
18120                 minHeight : this.height,
18121                 height: this.height,
18122                 handles : this.resizable,
18123                 width: this.width,
18124                 listeners : {
18125                     resize : function(r, w, h) {
18126                         _t.onResize(w,h); // -something
18127                     }
18128                 }
18129             });
18130             
18131         }
18132         this.createToolbar(this);
18133        
18134         
18135         if(!this.width && this.resizable){
18136             this.setSize(this.wrap.getSize());
18137         }
18138         if (this.resizeEl) {
18139             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18140             // should trigger onReize..
18141         }
18142         
18143     },
18144
18145     // private
18146     onResize : function(w, h)
18147     {
18148         Roo.log('resize: ' +w + ',' + h );
18149         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18150         var ew = false;
18151         var eh = false;
18152         
18153         if(this.inputEl() ){
18154             if(typeof w == 'number'){
18155                 var aw = w - this.wrap.getFrameWidth('lr');
18156                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18157                 ew = aw;
18158             }
18159             if(typeof h == 'number'){
18160                  var tbh = -11;  // fixme it needs to tool bar size!
18161                 for (var i =0; i < this.toolbars.length;i++) {
18162                     // fixme - ask toolbars for heights?
18163                     tbh += this.toolbars[i].el.getHeight();
18164                     //if (this.toolbars[i].footer) {
18165                     //    tbh += this.toolbars[i].footer.el.getHeight();
18166                     //}
18167                 }
18168               
18169                 
18170                 
18171                 
18172                 
18173                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18174                 ah -= 5; // knock a few pixes off for look..
18175                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18176                 var eh = ah;
18177             }
18178         }
18179         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18180         this.editorcore.onResize(ew,eh);
18181         
18182     },
18183
18184     /**
18185      * Toggles the editor between standard and source edit mode.
18186      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18187      */
18188     toggleSourceEdit : function(sourceEditMode)
18189     {
18190         this.editorcore.toggleSourceEdit(sourceEditMode);
18191         
18192         if(this.editorcore.sourceEditMode){
18193             Roo.log('editor - showing textarea');
18194             
18195 //            Roo.log('in');
18196 //            Roo.log(this.syncValue());
18197             this.syncValue();
18198             this.inputEl().removeClass(['hide', 'x-hidden']);
18199             this.inputEl().dom.removeAttribute('tabIndex');
18200             this.inputEl().focus();
18201         }else{
18202             Roo.log('editor - hiding textarea');
18203 //            Roo.log('out')
18204 //            Roo.log(this.pushValue()); 
18205             this.pushValue();
18206             
18207             this.inputEl().addClass(['hide', 'x-hidden']);
18208             this.inputEl().dom.setAttribute('tabIndex', -1);
18209             //this.deferFocus();
18210         }
18211          
18212         if(this.resizable){
18213             this.setSize(this.wrap.getSize());
18214         }
18215         
18216         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18217     },
18218  
18219     // private (for BoxComponent)
18220     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18221
18222     // private (for BoxComponent)
18223     getResizeEl : function(){
18224         return this.wrap;
18225     },
18226
18227     // private (for BoxComponent)
18228     getPositionEl : function(){
18229         return this.wrap;
18230     },
18231
18232     // private
18233     initEvents : function(){
18234         this.originalValue = this.getValue();
18235     },
18236
18237 //    /**
18238 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18239 //     * @method
18240 //     */
18241 //    markInvalid : Roo.emptyFn,
18242 //    /**
18243 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18244 //     * @method
18245 //     */
18246 //    clearInvalid : Roo.emptyFn,
18247
18248     setValue : function(v){
18249         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18250         this.editorcore.pushValue();
18251     },
18252
18253      
18254     // private
18255     deferFocus : function(){
18256         this.focus.defer(10, this);
18257     },
18258
18259     // doc'ed in Field
18260     focus : function(){
18261         this.editorcore.focus();
18262         
18263     },
18264       
18265
18266     // private
18267     onDestroy : function(){
18268         
18269         
18270         
18271         if(this.rendered){
18272             
18273             for (var i =0; i < this.toolbars.length;i++) {
18274                 // fixme - ask toolbars for heights?
18275                 this.toolbars[i].onDestroy();
18276             }
18277             
18278             this.wrap.dom.innerHTML = '';
18279             this.wrap.remove();
18280         }
18281     },
18282
18283     // private
18284     onFirstFocus : function(){
18285         //Roo.log("onFirstFocus");
18286         this.editorcore.onFirstFocus();
18287          for (var i =0; i < this.toolbars.length;i++) {
18288             this.toolbars[i].onFirstFocus();
18289         }
18290         
18291     },
18292     
18293     // private
18294     syncValue : function()
18295     {   
18296         this.editorcore.syncValue();
18297     },
18298     
18299     pushValue : function()
18300     {   
18301         this.editorcore.pushValue();
18302     }
18303      
18304     
18305     // hide stuff that is not compatible
18306     /**
18307      * @event blur
18308      * @hide
18309      */
18310     /**
18311      * @event change
18312      * @hide
18313      */
18314     /**
18315      * @event focus
18316      * @hide
18317      */
18318     /**
18319      * @event specialkey
18320      * @hide
18321      */
18322     /**
18323      * @cfg {String} fieldClass @hide
18324      */
18325     /**
18326      * @cfg {String} focusClass @hide
18327      */
18328     /**
18329      * @cfg {String} autoCreate @hide
18330      */
18331     /**
18332      * @cfg {String} inputType @hide
18333      */
18334     /**
18335      * @cfg {String} invalidClass @hide
18336      */
18337     /**
18338      * @cfg {String} invalidText @hide
18339      */
18340     /**
18341      * @cfg {String} msgFx @hide
18342      */
18343     /**
18344      * @cfg {String} validateOnBlur @hide
18345      */
18346 });
18347  
18348     
18349    
18350    
18351    
18352       
18353 Roo.namespace('Roo.bootstrap.htmleditor');
18354 /**
18355  * @class Roo.bootstrap.HtmlEditorToolbar1
18356  * Basic Toolbar
18357  * 
18358  * Usage:
18359  *
18360  new Roo.bootstrap.HtmlEditor({
18361     ....
18362     toolbars : [
18363         new Roo.bootstrap.HtmlEditorToolbar1({
18364             disable : { fonts: 1 , format: 1, ..., ... , ...],
18365             btns : [ .... ]
18366         })
18367     }
18368      
18369  * 
18370  * @cfg {Object} disable List of elements to disable..
18371  * @cfg {Array} btns List of additional buttons.
18372  * 
18373  * 
18374  * NEEDS Extra CSS? 
18375  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18376  */
18377  
18378 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18379 {
18380     
18381     Roo.apply(this, config);
18382     
18383     // default disabled, based on 'good practice'..
18384     this.disable = this.disable || {};
18385     Roo.applyIf(this.disable, {
18386         fontSize : true,
18387         colors : true,
18388         specialElements : true
18389     });
18390     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18391     
18392     this.editor = config.editor;
18393     this.editorcore = config.editor.editorcore;
18394     
18395     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18396     
18397     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18398     // dont call parent... till later.
18399 }
18400 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18401      
18402     bar : true,
18403     
18404     editor : false,
18405     editorcore : false,
18406     
18407     
18408     formats : [
18409         "p" ,  
18410         "h1","h2","h3","h4","h5","h6", 
18411         "pre", "code", 
18412         "abbr", "acronym", "address", "cite", "samp", "var",
18413         'div','span'
18414     ],
18415     
18416     onRender : function(ct, position)
18417     {
18418        // Roo.log("Call onRender: " + this.xtype);
18419         
18420        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18421        Roo.log(this.el);
18422        this.el.dom.style.marginBottom = '0';
18423        var _this = this;
18424        var editorcore = this.editorcore;
18425        var editor= this.editor;
18426        
18427        var children = [];
18428        var btn = function(id,cmd , toggle, handler){
18429        
18430             var  event = toggle ? 'toggle' : 'click';
18431        
18432             var a = {
18433                 size : 'sm',
18434                 xtype: 'Button',
18435                 xns: Roo.bootstrap,
18436                 glyphicon : id,
18437                 cmd : id || cmd,
18438                 enableToggle:toggle !== false,
18439                 //html : 'submit'
18440                 pressed : toggle ? false : null,
18441                 listeners : {}
18442             }
18443             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18444                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18445             }
18446             children.push(a);
18447             return a;
18448        }
18449         
18450         var style = {
18451                 xtype: 'Button',
18452                 size : 'sm',
18453                 xns: Roo.bootstrap,
18454                 glyphicon : 'font',
18455                 //html : 'submit'
18456                 menu : {
18457                     xtype: 'Menu',
18458                     xns: Roo.bootstrap,
18459                     items:  []
18460                 }
18461         };
18462         Roo.each(this.formats, function(f) {
18463             style.menu.items.push({
18464                 xtype :'MenuItem',
18465                 xns: Roo.bootstrap,
18466                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18467                 tagname : f,
18468                 listeners : {
18469                     click : function()
18470                     {
18471                         editorcore.insertTag(this.tagname);
18472                         editor.focus();
18473                     }
18474                 }
18475                 
18476             });
18477         });
18478          children.push(style);   
18479             
18480             
18481         btn('bold',false,true);
18482         btn('italic',false,true);
18483         btn('align-left', 'justifyleft',true);
18484         btn('align-center', 'justifycenter',true);
18485         btn('align-right' , 'justifyright',true);
18486         btn('link', false, false, function(btn) {
18487             //Roo.log("create link?");
18488             var url = prompt(this.createLinkText, this.defaultLinkValue);
18489             if(url && url != 'http:/'+'/'){
18490                 this.editorcore.relayCmd('createlink', url);
18491             }
18492         }),
18493         btn('list','insertunorderedlist',true);
18494         btn('pencil', false,true, function(btn){
18495                 Roo.log(this);
18496                 
18497                 this.toggleSourceEdit(btn.pressed);
18498         });
18499         /*
18500         var cog = {
18501                 xtype: 'Button',
18502                 size : 'sm',
18503                 xns: Roo.bootstrap,
18504                 glyphicon : 'cog',
18505                 //html : 'submit'
18506                 menu : {
18507                     xtype: 'Menu',
18508                     xns: Roo.bootstrap,
18509                     items:  []
18510                 }
18511         };
18512         
18513         cog.menu.items.push({
18514             xtype :'MenuItem',
18515             xns: Roo.bootstrap,
18516             html : Clean styles,
18517             tagname : f,
18518             listeners : {
18519                 click : function()
18520                 {
18521                     editorcore.insertTag(this.tagname);
18522                     editor.focus();
18523                 }
18524             }
18525             
18526         });
18527        */
18528         
18529          
18530        this.xtype = 'NavSimplebar';
18531         
18532         for(var i=0;i< children.length;i++) {
18533             
18534             this.buttons.add(this.addxtypeChild(children[i]));
18535             
18536         }
18537         
18538         editor.on('editorevent', this.updateToolbar, this);
18539     },
18540     onBtnClick : function(id)
18541     {
18542        this.editorcore.relayCmd(id);
18543        this.editorcore.focus();
18544     },
18545     
18546     /**
18547      * Protected method that will not generally be called directly. It triggers
18548      * a toolbar update by reading the markup state of the current selection in the editor.
18549      */
18550     updateToolbar: function(){
18551
18552         if(!this.editorcore.activated){
18553             this.editor.onFirstFocus(); // is this neeed?
18554             return;
18555         }
18556
18557         var btns = this.buttons; 
18558         var doc = this.editorcore.doc;
18559         btns.get('bold').setActive(doc.queryCommandState('bold'));
18560         btns.get('italic').setActive(doc.queryCommandState('italic'));
18561         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18562         
18563         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18564         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18565         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18566         
18567         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18568         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18569          /*
18570         
18571         var ans = this.editorcore.getAllAncestors();
18572         if (this.formatCombo) {
18573             
18574             
18575             var store = this.formatCombo.store;
18576             this.formatCombo.setValue("");
18577             for (var i =0; i < ans.length;i++) {
18578                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18579                     // select it..
18580                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18581                     break;
18582                 }
18583             }
18584         }
18585         
18586         
18587         
18588         // hides menus... - so this cant be on a menu...
18589         Roo.bootstrap.MenuMgr.hideAll();
18590         */
18591         Roo.bootstrap.MenuMgr.hideAll();
18592         //this.editorsyncValue();
18593     },
18594     onFirstFocus: function() {
18595         this.buttons.each(function(item){
18596            item.enable();
18597         });
18598     },
18599     toggleSourceEdit : function(sourceEditMode){
18600         
18601           
18602         if(sourceEditMode){
18603             Roo.log("disabling buttons");
18604            this.buttons.each( function(item){
18605                 if(item.cmd != 'pencil'){
18606                     item.disable();
18607                 }
18608             });
18609           
18610         }else{
18611             Roo.log("enabling buttons");
18612             if(this.editorcore.initialized){
18613                 this.buttons.each( function(item){
18614                     item.enable();
18615                 });
18616             }
18617             
18618         }
18619         Roo.log("calling toggole on editor");
18620         // tell the editor that it's been pressed..
18621         this.editor.toggleSourceEdit(sourceEditMode);
18622        
18623     }
18624 });
18625
18626
18627
18628
18629
18630 /**
18631  * @class Roo.bootstrap.Table.AbstractSelectionModel
18632  * @extends Roo.util.Observable
18633  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18634  * implemented by descendant classes.  This class should not be directly instantiated.
18635  * @constructor
18636  */
18637 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18638     this.locked = false;
18639     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18640 };
18641
18642
18643 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18644     /** @ignore Called by the grid automatically. Do not call directly. */
18645     init : function(grid){
18646         this.grid = grid;
18647         this.initEvents();
18648     },
18649
18650     /**
18651      * Locks the selections.
18652      */
18653     lock : function(){
18654         this.locked = true;
18655     },
18656
18657     /**
18658      * Unlocks the selections.
18659      */
18660     unlock : function(){
18661         this.locked = false;
18662     },
18663
18664     /**
18665      * Returns true if the selections are locked.
18666      * @return {Boolean}
18667      */
18668     isLocked : function(){
18669         return this.locked;
18670     }
18671 });
18672 /**
18673  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18674  * @class Roo.bootstrap.Table.RowSelectionModel
18675  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18676  * It supports multiple selections and keyboard selection/navigation. 
18677  * @constructor
18678  * @param {Object} config
18679  */
18680
18681 Roo.bootstrap.Table.RowSelectionModel = function(config){
18682     Roo.apply(this, config);
18683     this.selections = new Roo.util.MixedCollection(false, function(o){
18684         return o.id;
18685     });
18686
18687     this.last = false;
18688     this.lastActive = false;
18689
18690     this.addEvents({
18691         /**
18692              * @event selectionchange
18693              * Fires when the selection changes
18694              * @param {SelectionModel} this
18695              */
18696             "selectionchange" : true,
18697         /**
18698              * @event afterselectionchange
18699              * Fires after the selection changes (eg. by key press or clicking)
18700              * @param {SelectionModel} this
18701              */
18702             "afterselectionchange" : true,
18703         /**
18704              * @event beforerowselect
18705              * Fires when a row is selected being selected, return false to cancel.
18706              * @param {SelectionModel} this
18707              * @param {Number} rowIndex The selected index
18708              * @param {Boolean} keepExisting False if other selections will be cleared
18709              */
18710             "beforerowselect" : true,
18711         /**
18712              * @event rowselect
18713              * Fires when a row is selected.
18714              * @param {SelectionModel} this
18715              * @param {Number} rowIndex The selected index
18716              * @param {Roo.data.Record} r The record
18717              */
18718             "rowselect" : true,
18719         /**
18720              * @event rowdeselect
18721              * Fires when a row is deselected.
18722              * @param {SelectionModel} this
18723              * @param {Number} rowIndex The selected index
18724              */
18725         "rowdeselect" : true
18726     });
18727     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18728     this.locked = false;
18729 };
18730
18731 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18732     /**
18733      * @cfg {Boolean} singleSelect
18734      * True to allow selection of only one row at a time (defaults to false)
18735      */
18736     singleSelect : false,
18737
18738     // private
18739     initEvents : function(){
18740
18741         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18742             this.grid.on("mousedown", this.handleMouseDown, this);
18743         }else{ // allow click to work like normal
18744             this.grid.on("rowclick", this.handleDragableRowClick, this);
18745         }
18746
18747         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18748             "up" : function(e){
18749                 if(!e.shiftKey){
18750                     this.selectPrevious(e.shiftKey);
18751                 }else if(this.last !== false && this.lastActive !== false){
18752                     var last = this.last;
18753                     this.selectRange(this.last,  this.lastActive-1);
18754                     this.grid.getView().focusRow(this.lastActive);
18755                     if(last !== false){
18756                         this.last = last;
18757                     }
18758                 }else{
18759                     this.selectFirstRow();
18760                 }
18761                 this.fireEvent("afterselectionchange", this);
18762             },
18763             "down" : function(e){
18764                 if(!e.shiftKey){
18765                     this.selectNext(e.shiftKey);
18766                 }else if(this.last !== false && this.lastActive !== false){
18767                     var last = this.last;
18768                     this.selectRange(this.last,  this.lastActive+1);
18769                     this.grid.getView().focusRow(this.lastActive);
18770                     if(last !== false){
18771                         this.last = last;
18772                     }
18773                 }else{
18774                     this.selectFirstRow();
18775                 }
18776                 this.fireEvent("afterselectionchange", this);
18777             },
18778             scope: this
18779         });
18780
18781         var view = this.grid.view;
18782         view.on("refresh", this.onRefresh, this);
18783         view.on("rowupdated", this.onRowUpdated, this);
18784         view.on("rowremoved", this.onRemove, this);
18785     },
18786
18787     // private
18788     onRefresh : function(){
18789         var ds = this.grid.dataSource, i, v = this.grid.view;
18790         var s = this.selections;
18791         s.each(function(r){
18792             if((i = ds.indexOfId(r.id)) != -1){
18793                 v.onRowSelect(i);
18794             }else{
18795                 s.remove(r);
18796             }
18797         });
18798     },
18799
18800     // private
18801     onRemove : function(v, index, r){
18802         this.selections.remove(r);
18803     },
18804
18805     // private
18806     onRowUpdated : function(v, index, r){
18807         if(this.isSelected(r)){
18808             v.onRowSelect(index);
18809         }
18810     },
18811
18812     /**
18813      * Select records.
18814      * @param {Array} records The records to select
18815      * @param {Boolean} keepExisting (optional) True to keep existing selections
18816      */
18817     selectRecords : function(records, keepExisting){
18818         if(!keepExisting){
18819             this.clearSelections();
18820         }
18821         var ds = this.grid.dataSource;
18822         for(var i = 0, len = records.length; i < len; i++){
18823             this.selectRow(ds.indexOf(records[i]), true);
18824         }
18825     },
18826
18827     /**
18828      * Gets the number of selected rows.
18829      * @return {Number}
18830      */
18831     getCount : function(){
18832         return this.selections.length;
18833     },
18834
18835     /**
18836      * Selects the first row in the grid.
18837      */
18838     selectFirstRow : function(){
18839         this.selectRow(0);
18840     },
18841
18842     /**
18843      * Select the last row.
18844      * @param {Boolean} keepExisting (optional) True to keep existing selections
18845      */
18846     selectLastRow : function(keepExisting){
18847         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18848     },
18849
18850     /**
18851      * Selects the row immediately following the last selected row.
18852      * @param {Boolean} keepExisting (optional) True to keep existing selections
18853      */
18854     selectNext : function(keepExisting){
18855         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18856             this.selectRow(this.last+1, keepExisting);
18857             this.grid.getView().focusRow(this.last);
18858         }
18859     },
18860
18861     /**
18862      * Selects the row that precedes the last selected row.
18863      * @param {Boolean} keepExisting (optional) True to keep existing selections
18864      */
18865     selectPrevious : function(keepExisting){
18866         if(this.last){
18867             this.selectRow(this.last-1, keepExisting);
18868             this.grid.getView().focusRow(this.last);
18869         }
18870     },
18871
18872     /**
18873      * Returns the selected records
18874      * @return {Array} Array of selected records
18875      */
18876     getSelections : function(){
18877         return [].concat(this.selections.items);
18878     },
18879
18880     /**
18881      * Returns the first selected record.
18882      * @return {Record}
18883      */
18884     getSelected : function(){
18885         return this.selections.itemAt(0);
18886     },
18887
18888
18889     /**
18890      * Clears all selections.
18891      */
18892     clearSelections : function(fast){
18893         if(this.locked) return;
18894         if(fast !== true){
18895             var ds = this.grid.dataSource;
18896             var s = this.selections;
18897             s.each(function(r){
18898                 this.deselectRow(ds.indexOfId(r.id));
18899             }, this);
18900             s.clear();
18901         }else{
18902             this.selections.clear();
18903         }
18904         this.last = false;
18905     },
18906
18907
18908     /**
18909      * Selects all rows.
18910      */
18911     selectAll : function(){
18912         if(this.locked) return;
18913         this.selections.clear();
18914         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18915             this.selectRow(i, true);
18916         }
18917     },
18918
18919     /**
18920      * Returns True if there is a selection.
18921      * @return {Boolean}
18922      */
18923     hasSelection : function(){
18924         return this.selections.length > 0;
18925     },
18926
18927     /**
18928      * Returns True if the specified row is selected.
18929      * @param {Number/Record} record The record or index of the record to check
18930      * @return {Boolean}
18931      */
18932     isSelected : function(index){
18933         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18934         return (r && this.selections.key(r.id) ? true : false);
18935     },
18936
18937     /**
18938      * Returns True if the specified record id is selected.
18939      * @param {String} id The id of record to check
18940      * @return {Boolean}
18941      */
18942     isIdSelected : function(id){
18943         return (this.selections.key(id) ? true : false);
18944     },
18945
18946     // private
18947     handleMouseDown : function(e, t){
18948         var view = this.grid.getView(), rowIndex;
18949         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18950             return;
18951         };
18952         if(e.shiftKey && this.last !== false){
18953             var last = this.last;
18954             this.selectRange(last, rowIndex, e.ctrlKey);
18955             this.last = last; // reset the last
18956             view.focusRow(rowIndex);
18957         }else{
18958             var isSelected = this.isSelected(rowIndex);
18959             if(e.button !== 0 && isSelected){
18960                 view.focusRow(rowIndex);
18961             }else if(e.ctrlKey && isSelected){
18962                 this.deselectRow(rowIndex);
18963             }else if(!isSelected){
18964                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18965                 view.focusRow(rowIndex);
18966             }
18967         }
18968         this.fireEvent("afterselectionchange", this);
18969     },
18970     // private
18971     handleDragableRowClick :  function(grid, rowIndex, e) 
18972     {
18973         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18974             this.selectRow(rowIndex, false);
18975             grid.view.focusRow(rowIndex);
18976              this.fireEvent("afterselectionchange", this);
18977         }
18978     },
18979     
18980     /**
18981      * Selects multiple rows.
18982      * @param {Array} rows Array of the indexes of the row to select
18983      * @param {Boolean} keepExisting (optional) True to keep existing selections
18984      */
18985     selectRows : function(rows, keepExisting){
18986         if(!keepExisting){
18987             this.clearSelections();
18988         }
18989         for(var i = 0, len = rows.length; i < len; i++){
18990             this.selectRow(rows[i], true);
18991         }
18992     },
18993
18994     /**
18995      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18996      * @param {Number} startRow The index of the first row in the range
18997      * @param {Number} endRow The index of the last row in the range
18998      * @param {Boolean} keepExisting (optional) True to retain existing selections
18999      */
19000     selectRange : function(startRow, endRow, keepExisting){
19001         if(this.locked) return;
19002         if(!keepExisting){
19003             this.clearSelections();
19004         }
19005         if(startRow <= endRow){
19006             for(var i = startRow; i <= endRow; i++){
19007                 this.selectRow(i, true);
19008             }
19009         }else{
19010             for(var i = startRow; i >= endRow; i--){
19011                 this.selectRow(i, true);
19012             }
19013         }
19014     },
19015
19016     /**
19017      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19018      * @param {Number} startRow The index of the first row in the range
19019      * @param {Number} endRow The index of the last row in the range
19020      */
19021     deselectRange : function(startRow, endRow, preventViewNotify){
19022         if(this.locked) return;
19023         for(var i = startRow; i <= endRow; i++){
19024             this.deselectRow(i, preventViewNotify);
19025         }
19026     },
19027
19028     /**
19029      * Selects a row.
19030      * @param {Number} row The index of the row to select
19031      * @param {Boolean} keepExisting (optional) True to keep existing selections
19032      */
19033     selectRow : function(index, keepExisting, preventViewNotify){
19034         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19035         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19036             if(!keepExisting || this.singleSelect){
19037                 this.clearSelections();
19038             }
19039             var r = this.grid.dataSource.getAt(index);
19040             this.selections.add(r);
19041             this.last = this.lastActive = index;
19042             if(!preventViewNotify){
19043                 this.grid.getView().onRowSelect(index);
19044             }
19045             this.fireEvent("rowselect", this, index, r);
19046             this.fireEvent("selectionchange", this);
19047         }
19048     },
19049
19050     /**
19051      * Deselects a row.
19052      * @param {Number} row The index of the row to deselect
19053      */
19054     deselectRow : function(index, preventViewNotify){
19055         if(this.locked) return;
19056         if(this.last == index){
19057             this.last = false;
19058         }
19059         if(this.lastActive == index){
19060             this.lastActive = false;
19061         }
19062         var r = this.grid.dataSource.getAt(index);
19063         this.selections.remove(r);
19064         if(!preventViewNotify){
19065             this.grid.getView().onRowDeselect(index);
19066         }
19067         this.fireEvent("rowdeselect", this, index);
19068         this.fireEvent("selectionchange", this);
19069     },
19070
19071     // private
19072     restoreLast : function(){
19073         if(this._last){
19074             this.last = this._last;
19075         }
19076     },
19077
19078     // private
19079     acceptsNav : function(row, col, cm){
19080         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19081     },
19082
19083     // private
19084     onEditorKey : function(field, e){
19085         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19086         if(k == e.TAB){
19087             e.stopEvent();
19088             ed.completeEdit();
19089             if(e.shiftKey){
19090                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19091             }else{
19092                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19093             }
19094         }else if(k == e.ENTER && !e.ctrlKey){
19095             e.stopEvent();
19096             ed.completeEdit();
19097             if(e.shiftKey){
19098                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19099             }else{
19100                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19101             }
19102         }else if(k == e.ESC){
19103             ed.cancelEdit();
19104         }
19105         if(newCell){
19106             g.startEditing(newCell[0], newCell[1]);
19107         }
19108     }
19109 });/*
19110  * Based on:
19111  * Ext JS Library 1.1.1
19112  * Copyright(c) 2006-2007, Ext JS, LLC.
19113  *
19114  * Originally Released Under LGPL - original licence link has changed is not relivant.
19115  *
19116  * Fork - LGPL
19117  * <script type="text/javascript">
19118  */
19119  
19120 /**
19121  * @class Roo.bootstrap.PagingToolbar
19122  * @extends Roo.Row
19123  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19124  * @constructor
19125  * Create a new PagingToolbar
19126  * @param {Object} config The config object
19127  */
19128 Roo.bootstrap.PagingToolbar = function(config)
19129 {
19130     // old args format still supported... - xtype is prefered..
19131         // created from xtype...
19132     var ds = config.dataSource;
19133     this.toolbarItems = [];
19134     if (config.items) {
19135         this.toolbarItems = config.items;
19136 //        config.items = [];
19137     }
19138     
19139     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19140     this.ds = ds;
19141     this.cursor = 0;
19142     if (ds) { 
19143         this.bind(ds);
19144     }
19145     
19146     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19147     
19148 };
19149
19150 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19151     /**
19152      * @cfg {Roo.data.Store} dataSource
19153      * The underlying data store providing the paged data
19154      */
19155     /**
19156      * @cfg {String/HTMLElement/Element} container
19157      * container The id or element that will contain the toolbar
19158      */
19159     /**
19160      * @cfg {Boolean} displayInfo
19161      * True to display the displayMsg (defaults to false)
19162      */
19163     /**
19164      * @cfg {Number} pageSize
19165      * The number of records to display per page (defaults to 20)
19166      */
19167     pageSize: 20,
19168     /**
19169      * @cfg {String} displayMsg
19170      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19171      */
19172     displayMsg : 'Displaying {0} - {1} of {2}',
19173     /**
19174      * @cfg {String} emptyMsg
19175      * The message to display when no records are found (defaults to "No data to display")
19176      */
19177     emptyMsg : 'No data to display',
19178     /**
19179      * Customizable piece of the default paging text (defaults to "Page")
19180      * @type String
19181      */
19182     beforePageText : "Page",
19183     /**
19184      * Customizable piece of the default paging text (defaults to "of %0")
19185      * @type String
19186      */
19187     afterPageText : "of {0}",
19188     /**
19189      * Customizable piece of the default paging text (defaults to "First Page")
19190      * @type String
19191      */
19192     firstText : "First Page",
19193     /**
19194      * Customizable piece of the default paging text (defaults to "Previous Page")
19195      * @type String
19196      */
19197     prevText : "Previous Page",
19198     /**
19199      * Customizable piece of the default paging text (defaults to "Next Page")
19200      * @type String
19201      */
19202     nextText : "Next Page",
19203     /**
19204      * Customizable piece of the default paging text (defaults to "Last Page")
19205      * @type String
19206      */
19207     lastText : "Last Page",
19208     /**
19209      * Customizable piece of the default paging text (defaults to "Refresh")
19210      * @type String
19211      */
19212     refreshText : "Refresh",
19213
19214     buttons : false,
19215     // private
19216     onRender : function(ct, position) 
19217     {
19218         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19219         this.navgroup.parentId = this.id;
19220         this.navgroup.onRender(this.el, null);
19221         // add the buttons to the navgroup
19222         
19223         if(this.displayInfo){
19224             Roo.log(this.el.select('ul.navbar-nav',true).first());
19225             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19226             this.displayEl = this.el.select('.x-paging-info', true).first();
19227 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19228 //            this.displayEl = navel.el.select('span',true).first();
19229         }
19230         
19231         var _this = this;
19232         
19233         if(this.buttons){
19234             Roo.each(_this.buttons, function(e){
19235                Roo.factory(e).onRender(_this.el, null);
19236             });
19237         }
19238             
19239         Roo.each(_this.toolbarItems, function(e) {
19240             _this.navgroup.addItem(e);
19241         });
19242         
19243         this.first = this.navgroup.addItem({
19244             tooltip: this.firstText,
19245             cls: "prev",
19246             icon : 'fa fa-backward',
19247             disabled: true,
19248             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19249         });
19250         
19251         this.prev =  this.navgroup.addItem({
19252             tooltip: this.prevText,
19253             cls: "prev",
19254             icon : 'fa fa-step-backward',
19255             disabled: true,
19256             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19257         });
19258     //this.addSeparator();
19259         
19260         
19261         var field = this.navgroup.addItem( {
19262             tagtype : 'span',
19263             cls : 'x-paging-position',
19264             
19265             html : this.beforePageText  +
19266                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19267                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19268          } ); //?? escaped?
19269         
19270         this.field = field.el.select('input', true).first();
19271         this.field.on("keydown", this.onPagingKeydown, this);
19272         this.field.on("focus", function(){this.dom.select();});
19273     
19274     
19275         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19276         //this.field.setHeight(18);
19277         //this.addSeparator();
19278         this.next = this.navgroup.addItem({
19279             tooltip: this.nextText,
19280             cls: "next",
19281             html : ' <i class="fa fa-step-forward">',
19282             disabled: true,
19283             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19284         });
19285         this.last = this.navgroup.addItem({
19286             tooltip: this.lastText,
19287             icon : 'fa fa-forward',
19288             cls: "next",
19289             disabled: true,
19290             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19291         });
19292     //this.addSeparator();
19293         this.loading = this.navgroup.addItem({
19294             tooltip: this.refreshText,
19295             icon: 'fa fa-refresh',
19296             
19297             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19298         });
19299
19300     },
19301
19302     // private
19303     updateInfo : function(){
19304         if(this.displayEl){
19305             var count = this.ds.getCount();
19306             var msg = count == 0 ?
19307                 this.emptyMsg :
19308                 String.format(
19309                     this.displayMsg,
19310                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19311                 );
19312             this.displayEl.update(msg);
19313         }
19314     },
19315
19316     // private
19317     onLoad : function(ds, r, o){
19318        this.cursor = o.params ? o.params.start : 0;
19319        var d = this.getPageData(),
19320             ap = d.activePage,
19321             ps = d.pages;
19322         
19323        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19324        this.field.dom.value = ap;
19325        this.first.setDisabled(ap == 1);
19326        this.prev.setDisabled(ap == 1);
19327        this.next.setDisabled(ap == ps);
19328        this.last.setDisabled(ap == ps);
19329        this.loading.enable();
19330        this.updateInfo();
19331     },
19332
19333     // private
19334     getPageData : function(){
19335         var total = this.ds.getTotalCount();
19336         return {
19337             total : total,
19338             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19339             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19340         };
19341     },
19342
19343     // private
19344     onLoadError : function(){
19345         this.loading.enable();
19346     },
19347
19348     // private
19349     onPagingKeydown : function(e){
19350         var k = e.getKey();
19351         var d = this.getPageData();
19352         if(k == e.RETURN){
19353             var v = this.field.dom.value, pageNum;
19354             if(!v || isNaN(pageNum = parseInt(v, 10))){
19355                 this.field.dom.value = d.activePage;
19356                 return;
19357             }
19358             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19359             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19360             e.stopEvent();
19361         }
19362         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))
19363         {
19364           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19365           this.field.dom.value = pageNum;
19366           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19367           e.stopEvent();
19368         }
19369         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19370         {
19371           var v = this.field.dom.value, pageNum; 
19372           var increment = (e.shiftKey) ? 10 : 1;
19373           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19374             increment *= -1;
19375           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19376             this.field.dom.value = d.activePage;
19377             return;
19378           }
19379           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19380           {
19381             this.field.dom.value = parseInt(v, 10) + increment;
19382             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19383             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19384           }
19385           e.stopEvent();
19386         }
19387     },
19388
19389     // private
19390     beforeLoad : function(){
19391         if(this.loading){
19392             this.loading.disable();
19393         }
19394     },
19395
19396     // private
19397     onClick : function(which){
19398         var ds = this.ds;
19399         if (!ds) {
19400             return;
19401         }
19402         switch(which){
19403             case "first":
19404                 ds.load({params:{start: 0, limit: this.pageSize}});
19405             break;
19406             case "prev":
19407                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19408             break;
19409             case "next":
19410                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19411             break;
19412             case "last":
19413                 var total = ds.getTotalCount();
19414                 var extra = total % this.pageSize;
19415                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19416                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19417             break;
19418             case "refresh":
19419                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19420             break;
19421         }
19422     },
19423
19424     /**
19425      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19426      * @param {Roo.data.Store} store The data store to unbind
19427      */
19428     unbind : function(ds){
19429         ds.un("beforeload", this.beforeLoad, this);
19430         ds.un("load", this.onLoad, this);
19431         ds.un("loadexception", this.onLoadError, this);
19432         ds.un("remove", this.updateInfo, this);
19433         ds.un("add", this.updateInfo, this);
19434         this.ds = undefined;
19435     },
19436
19437     /**
19438      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19439      * @param {Roo.data.Store} store The data store to bind
19440      */
19441     bind : function(ds){
19442         ds.on("beforeload", this.beforeLoad, this);
19443         ds.on("load", this.onLoad, this);
19444         ds.on("loadexception", this.onLoadError, this);
19445         ds.on("remove", this.updateInfo, this);
19446         ds.on("add", this.updateInfo, this);
19447         this.ds = ds;
19448     }
19449 });/*
19450  * - LGPL
19451  *
19452  * element
19453  * 
19454  */
19455
19456 /**
19457  * @class Roo.bootstrap.MessageBar
19458  * @extends Roo.bootstrap.Component
19459  * Bootstrap MessageBar class
19460  * @cfg {String} html contents of the MessageBar
19461  * @cfg {String} weight (info | success | warning | danger) default info
19462  * @cfg {String} beforeClass insert the bar before the given class
19463  * @cfg {Boolean} closable (true | false) default false
19464  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19465  * 
19466  * @constructor
19467  * Create a new Element
19468  * @param {Object} config The config object
19469  */
19470
19471 Roo.bootstrap.MessageBar = function(config){
19472     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19473 };
19474
19475 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19476     
19477     html: '',
19478     weight: 'info',
19479     closable: false,
19480     fixed: false,
19481     beforeClass: 'bootstrap-sticky-wrap',
19482     
19483     getAutoCreate : function(){
19484         
19485         var cfg = {
19486             tag: 'div',
19487             cls: 'alert alert-dismissable alert-' + this.weight,
19488             cn: [
19489                 {
19490                     tag: 'span',
19491                     cls: 'message',
19492                     html: this.html || ''
19493                 }
19494             ]
19495         }
19496         
19497         if(this.fixed){
19498             cfg.cls += ' alert-messages-fixed';
19499         }
19500         
19501         if(this.closable){
19502             cfg.cn.push({
19503                 tag: 'button',
19504                 cls: 'close',
19505                 html: 'x'
19506             });
19507         }
19508         
19509         return cfg;
19510     },
19511     
19512     onRender : function(ct, position)
19513     {
19514         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19515         
19516         if(!this.el){
19517             var cfg = Roo.apply({},  this.getAutoCreate());
19518             cfg.id = Roo.id();
19519             
19520             if (this.cls) {
19521                 cfg.cls += ' ' + this.cls;
19522             }
19523             if (this.style) {
19524                 cfg.style = this.style;
19525             }
19526             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19527             
19528             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19529         }
19530         
19531         this.el.select('>button.close').on('click', this.hide, this);
19532         
19533     },
19534     
19535     show : function()
19536     {
19537         if (!this.rendered) {
19538             this.render();
19539         }
19540         
19541         this.el.show();
19542         
19543         this.fireEvent('show', this);
19544         
19545     },
19546     
19547     hide : function()
19548     {
19549         if (!this.rendered) {
19550             this.render();
19551         }
19552         
19553         this.el.hide();
19554         
19555         this.fireEvent('hide', this);
19556     },
19557     
19558     update : function()
19559     {
19560 //        var e = this.el.dom.firstChild;
19561 //        
19562 //        if(this.closable){
19563 //            e = e.nextSibling;
19564 //        }
19565 //        
19566 //        e.data = this.html || '';
19567
19568         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19569     }
19570    
19571 });
19572
19573  
19574
19575      /*
19576  * - LGPL
19577  *
19578  * Graph
19579  * 
19580  */
19581
19582
19583 /**
19584  * @class Roo.bootstrap.Graph
19585  * @extends Roo.bootstrap.Component
19586  * Bootstrap Graph class
19587 > Prameters
19588  -sm {number} sm 4
19589  -md {number} md 5
19590  @cfg {String} graphtype  bar | vbar | pie
19591  @cfg {number} g_x coodinator | centre x (pie)
19592  @cfg {number} g_y coodinator | centre y (pie)
19593  @cfg {number} g_r radius (pie)
19594  @cfg {number} g_height height of the chart (respected by all elements in the set)
19595  @cfg {number} g_width width of the chart (respected by all elements in the set)
19596  @cfg {Object} title The title of the chart
19597     
19598  -{Array}  values
19599  -opts (object) options for the chart 
19600      o {
19601      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19602      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19603      o vgutter (number)
19604      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.
19605      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19606      o to
19607      o stretch (boolean)
19608      o }
19609  -opts (object) options for the pie
19610      o{
19611      o cut
19612      o startAngle (number)
19613      o endAngle (number)
19614      } 
19615  *
19616  * @constructor
19617  * Create a new Input
19618  * @param {Object} config The config object
19619  */
19620
19621 Roo.bootstrap.Graph = function(config){
19622     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19623     
19624     this.addEvents({
19625         // img events
19626         /**
19627          * @event click
19628          * The img click event for the img.
19629          * @param {Roo.EventObject} e
19630          */
19631         "click" : true
19632     });
19633 };
19634
19635 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19636     
19637     sm: 4,
19638     md: 5,
19639     graphtype: 'bar',
19640     g_height: 250,
19641     g_width: 400,
19642     g_x: 50,
19643     g_y: 50,
19644     g_r: 30,
19645     opts:{
19646         //g_colors: this.colors,
19647         g_type: 'soft',
19648         g_gutter: '20%'
19649
19650     },
19651     title : false,
19652
19653     getAutoCreate : function(){
19654         
19655         var cfg = {
19656             tag: 'div',
19657             html : null
19658         }
19659         
19660         
19661         return  cfg;
19662     },
19663
19664     onRender : function(ct,position){
19665         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19666         this.raphael = Raphael(this.el.dom);
19667         
19668                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19669                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19670                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19671                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19672                 /*
19673                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19674                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19675                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19676                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19677                 
19678                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19679                 r.barchart(330, 10, 300, 220, data1);
19680                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19681                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19682                 */
19683                 
19684                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19685                 // r.barchart(30, 30, 560, 250,  xdata, {
19686                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19687                 //     axis : "0 0 1 1",
19688                 //     axisxlabels :  xdata
19689                 //     //yvalues : cols,
19690                    
19691                 // });
19692 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19693 //        
19694 //        this.load(null,xdata,{
19695 //                axis : "0 0 1 1",
19696 //                axisxlabels :  xdata
19697 //                });
19698
19699     },
19700
19701     load : function(graphtype,xdata,opts){
19702         this.raphael.clear();
19703         if(!graphtype) {
19704             graphtype = this.graphtype;
19705         }
19706         if(!opts){
19707             opts = this.opts;
19708         }
19709         var r = this.raphael,
19710             fin = function () {
19711                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19712             },
19713             fout = function () {
19714                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19715             },
19716             pfin = function() {
19717                 this.sector.stop();
19718                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19719
19720                 if (this.label) {
19721                     this.label[0].stop();
19722                     this.label[0].attr({ r: 7.5 });
19723                     this.label[1].attr({ "font-weight": 800 });
19724                 }
19725             },
19726             pfout = function() {
19727                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19728
19729                 if (this.label) {
19730                     this.label[0].animate({ r: 5 }, 500, "bounce");
19731                     this.label[1].attr({ "font-weight": 400 });
19732                 }
19733             };
19734
19735         switch(graphtype){
19736             case 'bar':
19737                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19738                 break;
19739             case 'hbar':
19740                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19741                 break;
19742             case 'pie':
19743 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19744 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19745 //            
19746                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19747                 
19748                 break;
19749
19750         }
19751         
19752         if(this.title){
19753             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19754         }
19755         
19756     },
19757     
19758     setTitle: function(o)
19759     {
19760         this.title = o;
19761     },
19762     
19763     initEvents: function() {
19764         
19765         if(!this.href){
19766             this.el.on('click', this.onClick, this);
19767         }
19768     },
19769     
19770     onClick : function(e)
19771     {
19772         Roo.log('img onclick');
19773         this.fireEvent('click', this, e);
19774     }
19775    
19776 });
19777
19778  
19779 /*
19780  * - LGPL
19781  *
19782  * numberBox
19783  * 
19784  */
19785 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19786
19787 /**
19788  * @class Roo.bootstrap.dash.NumberBox
19789  * @extends Roo.bootstrap.Component
19790  * Bootstrap NumberBox class
19791  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19792  * @cfg {String} headline Box headline
19793  * @cfg {String} content Box content
19794  * @cfg {String} icon Box icon
19795  * @cfg {String} footer Footer text
19796  * @cfg {String} fhref Footer href
19797  * 
19798  * @constructor
19799  * Create a new NumberBox
19800  * @param {Object} config The config object
19801  */
19802
19803
19804 Roo.bootstrap.dash.NumberBox = function(config){
19805     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19806     
19807 };
19808
19809 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19810     
19811     bgcolor : 'aqua',
19812     headline : '',
19813     content : '',
19814     icon : '',
19815     footer : '',
19816     fhref : '',
19817     ficon : '',
19818     
19819     getAutoCreate : function(){
19820         
19821         var cfg = {
19822             tag : 'div',
19823             cls : 'small-box bg-' + this.bgcolor,
19824             cn : [
19825                 {
19826                     tag : 'div',
19827                     cls : 'inner',
19828                     cn :[
19829                         {
19830                             tag : 'h3',
19831                             cls : 'roo-headline',
19832                             html : this.headline
19833                         },
19834                         {
19835                             tag : 'p',
19836                             cls : 'roo-content',
19837                             html : this.content
19838                         }
19839                     ]
19840                 }
19841             ]
19842         }
19843         
19844         if(this.icon){
19845             cfg.cn.push({
19846                 tag : 'div',
19847                 cls : 'icon',
19848                 cn :[
19849                     {
19850                         tag : 'i',
19851                         cls : 'ion ' + this.icon
19852                     }
19853                 ]
19854             });
19855         }
19856         
19857         if(this.footer){
19858             var footer = {
19859                 tag : 'a',
19860                 cls : 'small-box-footer',
19861                 href : this.fhref || '#',
19862                 html : this.footer
19863             };
19864             
19865             cfg.cn.push(footer);
19866             
19867         }
19868         
19869         return  cfg;
19870     },
19871
19872     onRender : function(ct,position){
19873         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19874
19875
19876        
19877                 
19878     },
19879
19880     setHeadline: function (value)
19881     {
19882         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19883     },
19884     
19885     setFooter: function (value, href)
19886     {
19887         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19888         
19889         if(href){
19890             this.el.select('a.small-box-footer',true).first().attr('href', href);
19891         }
19892         
19893     },
19894
19895     setContent: function (value)
19896     {
19897         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19898     },
19899
19900     initEvents: function() 
19901     {   
19902         
19903     }
19904     
19905 });
19906
19907  
19908 /*
19909  * - LGPL
19910  *
19911  * TabBox
19912  * 
19913  */
19914 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19915
19916 /**
19917  * @class Roo.bootstrap.dash.TabBox
19918  * @extends Roo.bootstrap.Component
19919  * Bootstrap TabBox class
19920  * @cfg {String} title Title of the TabBox
19921  * @cfg {String} icon Icon of the TabBox
19922  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19923  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19924  * 
19925  * @constructor
19926  * Create a new TabBox
19927  * @param {Object} config The config object
19928  */
19929
19930
19931 Roo.bootstrap.dash.TabBox = function(config){
19932     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19933     this.addEvents({
19934         // raw events
19935         /**
19936          * @event addpane
19937          * When a pane is added
19938          * @param {Roo.bootstrap.dash.TabPane} pane
19939          */
19940         "addpane" : true,
19941         /**
19942          * @event activatepane
19943          * When a pane is activated
19944          * @param {Roo.bootstrap.dash.TabPane} pane
19945          */
19946         "activatepane" : true
19947         
19948          
19949     });
19950     
19951     this.panes = [];
19952 };
19953
19954 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19955
19956     title : '',
19957     icon : false,
19958     showtabs : true,
19959     tabScrollable : false,
19960     
19961     getChildContainer : function()
19962     {
19963         return this.el.select('.tab-content', true).first();
19964     },
19965     
19966     getAutoCreate : function(){
19967         
19968         var header = {
19969             tag: 'li',
19970             cls: 'pull-left header',
19971             html: this.title,
19972             cn : []
19973         };
19974         
19975         if(this.icon){
19976             header.cn.push({
19977                 tag: 'i',
19978                 cls: 'fa ' + this.icon
19979             });
19980         }
19981         
19982         var h = {
19983             tag: 'ul',
19984             cls: 'nav nav-tabs pull-right',
19985             cn: [
19986                 header
19987             ]
19988         };
19989         
19990         if(this.tabScrollable){
19991             h = {
19992                 tag: 'div',
19993                 cls: 'tab-header',
19994                 cn: [
19995                     {
19996                         tag: 'ul',
19997                         cls: 'nav nav-tabs pull-right',
19998                         cn: [
19999                             header
20000                         ]
20001                     }
20002                 ]
20003             }
20004         }
20005         
20006         var cfg = {
20007             tag: 'div',
20008             cls: 'nav-tabs-custom',
20009             cn: [
20010                 h,
20011                 {
20012                     tag: 'div',
20013                     cls: 'tab-content no-padding',
20014                     cn: []
20015                 }
20016             ]
20017         }
20018
20019         return  cfg;
20020     },
20021     initEvents : function()
20022     {
20023         //Roo.log('add add pane handler');
20024         this.on('addpane', this.onAddPane, this);
20025     },
20026      /**
20027      * Updates the box title
20028      * @param {String} html to set the title to.
20029      */
20030     setTitle : function(value)
20031     {
20032         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20033     },
20034     onAddPane : function(pane)
20035     {
20036         this.panes.push(pane);
20037         //Roo.log('addpane');
20038         //Roo.log(pane);
20039         // tabs are rendere left to right..
20040         if(!this.showtabs){
20041             return;
20042         }
20043         
20044         var ctr = this.el.select('.nav-tabs', true).first();
20045          
20046          
20047         var existing = ctr.select('.nav-tab',true);
20048         var qty = existing.getCount();;
20049         
20050         
20051         var tab = ctr.createChild({
20052             tag : 'li',
20053             cls : 'nav-tab' + (qty ? '' : ' active'),
20054             cn : [
20055                 {
20056                     tag : 'a',
20057                     href:'#',
20058                     html : pane.title
20059                 }
20060             ]
20061         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20062         pane.tab = tab;
20063         
20064         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20065         if (!qty) {
20066             pane.el.addClass('active');
20067         }
20068         
20069                 
20070     },
20071     onTabClick : function(ev,un,ob,pane)
20072     {
20073         //Roo.log('tab - prev default');
20074         ev.preventDefault();
20075         
20076         
20077         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20078         pane.tab.addClass('active');
20079         //Roo.log(pane.title);
20080         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20081         // technically we should have a deactivate event.. but maybe add later.
20082         // and it should not de-activate the selected tab...
20083         this.fireEvent('activatepane', pane);
20084         pane.el.addClass('active');
20085         pane.fireEvent('activate');
20086         
20087         
20088     },
20089     
20090     getActivePane : function()
20091     {
20092         var r = false;
20093         Roo.each(this.panes, function(p) {
20094             if(p.el.hasClass('active')){
20095                 r = p;
20096                 return false;
20097             }
20098             
20099             return;
20100         });
20101         
20102         return r;
20103     }
20104     
20105     
20106 });
20107
20108  
20109 /*
20110  * - LGPL
20111  *
20112  * Tab pane
20113  * 
20114  */
20115 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20116 /**
20117  * @class Roo.bootstrap.TabPane
20118  * @extends Roo.bootstrap.Component
20119  * Bootstrap TabPane class
20120  * @cfg {Boolean} active (false | true) Default false
20121  * @cfg {String} title title of panel
20122
20123  * 
20124  * @constructor
20125  * Create a new TabPane
20126  * @param {Object} config The config object
20127  */
20128
20129 Roo.bootstrap.dash.TabPane = function(config){
20130     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20131     
20132     this.addEvents({
20133         // raw events
20134         /**
20135          * @event activate
20136          * When a pane is activated
20137          * @param {Roo.bootstrap.dash.TabPane} pane
20138          */
20139         "activate" : true
20140          
20141     });
20142 };
20143
20144 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20145     
20146     active : false,
20147     title : '',
20148     
20149     // the tabBox that this is attached to.
20150     tab : false,
20151      
20152     getAutoCreate : function() 
20153     {
20154         var cfg = {
20155             tag: 'div',
20156             cls: 'tab-pane'
20157         }
20158         
20159         if(this.active){
20160             cfg.cls += ' active';
20161         }
20162         
20163         return cfg;
20164     },
20165     initEvents  : function()
20166     {
20167         //Roo.log('trigger add pane handler');
20168         this.parent().fireEvent('addpane', this)
20169     },
20170     
20171      /**
20172      * Updates the tab title 
20173      * @param {String} html to set the title to.
20174      */
20175     setTitle: function(str)
20176     {
20177         if (!this.tab) {
20178             return;
20179         }
20180         this.title = str;
20181         this.tab.select('a', true).first().dom.innerHTML = str;
20182         
20183     }
20184     
20185     
20186     
20187 });
20188
20189  
20190
20191
20192  /*
20193  * - LGPL
20194  *
20195  * menu
20196  * 
20197  */
20198 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20199
20200 /**
20201  * @class Roo.bootstrap.menu.Menu
20202  * @extends Roo.bootstrap.Component
20203  * Bootstrap Menu class - container for Menu
20204  * @cfg {String} html Text of the menu
20205  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20206  * @cfg {String} icon Font awesome icon
20207  * @cfg {String} pos Menu align to (top | bottom) default bottom
20208  * 
20209  * 
20210  * @constructor
20211  * Create a new Menu
20212  * @param {Object} config The config object
20213  */
20214
20215
20216 Roo.bootstrap.menu.Menu = function(config){
20217     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20218     
20219     this.addEvents({
20220         /**
20221          * @event beforeshow
20222          * Fires before this menu is displayed
20223          * @param {Roo.bootstrap.menu.Menu} this
20224          */
20225         beforeshow : true,
20226         /**
20227          * @event beforehide
20228          * Fires before this menu is hidden
20229          * @param {Roo.bootstrap.menu.Menu} this
20230          */
20231         beforehide : true,
20232         /**
20233          * @event show
20234          * Fires after this menu is displayed
20235          * @param {Roo.bootstrap.menu.Menu} this
20236          */
20237         show : true,
20238         /**
20239          * @event hide
20240          * Fires after this menu is hidden
20241          * @param {Roo.bootstrap.menu.Menu} this
20242          */
20243         hide : true,
20244         /**
20245          * @event click
20246          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20247          * @param {Roo.bootstrap.menu.Menu} this
20248          * @param {Roo.EventObject} e
20249          */
20250         click : true
20251     });
20252     
20253 };
20254
20255 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20256     
20257     submenu : false,
20258     html : '',
20259     weight : 'default',
20260     icon : false,
20261     pos : 'bottom',
20262     
20263     
20264     getChildContainer : function() {
20265         if(this.isSubMenu){
20266             return this.el;
20267         }
20268         
20269         return this.el.select('ul.dropdown-menu', true).first();  
20270     },
20271     
20272     getAutoCreate : function()
20273     {
20274         var text = [
20275             {
20276                 tag : 'span',
20277                 cls : 'roo-menu-text',
20278                 html : this.html
20279             }
20280         ];
20281         
20282         if(this.icon){
20283             text.unshift({
20284                 tag : 'i',
20285                 cls : 'fa ' + this.icon
20286             })
20287         }
20288         
20289         
20290         var cfg = {
20291             tag : 'div',
20292             cls : 'btn-group',
20293             cn : [
20294                 {
20295                     tag : 'button',
20296                     cls : 'dropdown-button btn btn-' + this.weight,
20297                     cn : text
20298                 },
20299                 {
20300                     tag : 'button',
20301                     cls : 'dropdown-toggle btn btn-' + this.weight,
20302                     cn : [
20303                         {
20304                             tag : 'span',
20305                             cls : 'caret'
20306                         }
20307                     ]
20308                 },
20309                 {
20310                     tag : 'ul',
20311                     cls : 'dropdown-menu'
20312                 }
20313             ]
20314             
20315         };
20316         
20317         if(this.pos == 'top'){
20318             cfg.cls += ' dropup';
20319         }
20320         
20321         if(this.isSubMenu){
20322             cfg = {
20323                 tag : 'ul',
20324                 cls : 'dropdown-menu'
20325             }
20326         }
20327         
20328         return cfg;
20329     },
20330     
20331     onRender : function(ct, position)
20332     {
20333         this.isSubMenu = ct.hasClass('dropdown-submenu');
20334         
20335         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20336     },
20337     
20338     initEvents : function() 
20339     {
20340         if(this.isSubMenu){
20341             return;
20342         }
20343         
20344         this.hidden = true;
20345         
20346         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20347         this.triggerEl.on('click', this.onTriggerPress, this);
20348         
20349         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20350         this.buttonEl.on('click', this.onClick, this);
20351         
20352     },
20353     
20354     list : function()
20355     {
20356         if(this.isSubMenu){
20357             return this.el;
20358         }
20359         
20360         return this.el.select('ul.dropdown-menu', true).first();
20361     },
20362     
20363     onClick : function(e)
20364     {
20365         this.fireEvent("click", this, e);
20366     },
20367     
20368     onTriggerPress  : function(e)
20369     {   
20370         if (this.isVisible()) {
20371             this.hide();
20372         } else {
20373             this.show();
20374         }
20375     },
20376     
20377     isVisible : function(){
20378         return !this.hidden;
20379     },
20380     
20381     show : function()
20382     {
20383         this.fireEvent("beforeshow", this);
20384         
20385         this.hidden = false;
20386         this.el.addClass('open');
20387         
20388         Roo.get(document).on("mouseup", this.onMouseUp, this);
20389         
20390         this.fireEvent("show", this);
20391         
20392         
20393     },
20394     
20395     hide : function()
20396     {
20397         this.fireEvent("beforehide", this);
20398         
20399         this.hidden = true;
20400         this.el.removeClass('open');
20401         
20402         Roo.get(document).un("mouseup", this.onMouseUp);
20403         
20404         this.fireEvent("hide", this);
20405     },
20406     
20407     onMouseUp : function()
20408     {
20409         this.hide();
20410     }
20411     
20412 });
20413
20414  
20415  /*
20416  * - LGPL
20417  *
20418  * menu item
20419  * 
20420  */
20421 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20422
20423 /**
20424  * @class Roo.bootstrap.menu.Item
20425  * @extends Roo.bootstrap.Component
20426  * Bootstrap MenuItem class
20427  * @cfg {Boolean} submenu (true | false) default false
20428  * @cfg {String} html text of the item
20429  * @cfg {String} href the link
20430  * @cfg {Boolean} disable (true | false) default false
20431  * @cfg {Boolean} preventDefault (true | false) default true
20432  * @cfg {String} icon Font awesome icon
20433  * @cfg {String} pos Submenu align to (left | right) default right 
20434  * 
20435  * 
20436  * @constructor
20437  * Create a new Item
20438  * @param {Object} config The config object
20439  */
20440
20441
20442 Roo.bootstrap.menu.Item = function(config){
20443     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20444     this.addEvents({
20445         /**
20446          * @event mouseover
20447          * Fires when the mouse is hovering over this menu
20448          * @param {Roo.bootstrap.menu.Item} this
20449          * @param {Roo.EventObject} e
20450          */
20451         mouseover : true,
20452         /**
20453          * @event mouseout
20454          * Fires when the mouse exits this menu
20455          * @param {Roo.bootstrap.menu.Item} this
20456          * @param {Roo.EventObject} e
20457          */
20458         mouseout : true,
20459         // raw events
20460         /**
20461          * @event click
20462          * The raw click event for the entire grid.
20463          * @param {Roo.EventObject} e
20464          */
20465         click : true
20466     });
20467 };
20468
20469 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20470     
20471     submenu : false,
20472     href : '',
20473     html : '',
20474     preventDefault: true,
20475     disable : false,
20476     icon : false,
20477     pos : 'right',
20478     
20479     getAutoCreate : function()
20480     {
20481         var text = [
20482             {
20483                 tag : 'span',
20484                 cls : 'roo-menu-item-text',
20485                 html : this.html
20486             }
20487         ];
20488         
20489         if(this.icon){
20490             text.unshift({
20491                 tag : 'i',
20492                 cls : 'fa ' + this.icon
20493             })
20494         }
20495         
20496         var cfg = {
20497             tag : 'li',
20498             cn : [
20499                 {
20500                     tag : 'a',
20501                     href : this.href || '#',
20502                     cn : text
20503                 }
20504             ]
20505         };
20506         
20507         if(this.disable){
20508             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20509         }
20510         
20511         if(this.submenu){
20512             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20513             
20514             if(this.pos == 'left'){
20515                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20516             }
20517         }
20518         
20519         return cfg;
20520     },
20521     
20522     initEvents : function() 
20523     {
20524         this.el.on('mouseover', this.onMouseOver, this);
20525         this.el.on('mouseout', this.onMouseOut, this);
20526         
20527         this.el.select('a', true).first().on('click', this.onClick, this);
20528         
20529     },
20530     
20531     onClick : function(e)
20532     {
20533         if(this.preventDefault){
20534             e.preventDefault();
20535         }
20536         
20537         this.fireEvent("click", this, e);
20538     },
20539     
20540     onMouseOver : function(e)
20541     {
20542         if(this.submenu && this.pos == 'left'){
20543             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20544         }
20545         
20546         this.fireEvent("mouseover", this, e);
20547     },
20548     
20549     onMouseOut : function(e)
20550     {
20551         this.fireEvent("mouseout", this, e);
20552     }
20553 });
20554
20555  
20556
20557  /*
20558  * - LGPL
20559  *
20560  * menu separator
20561  * 
20562  */
20563 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20564
20565 /**
20566  * @class Roo.bootstrap.menu.Separator
20567  * @extends Roo.bootstrap.Component
20568  * Bootstrap Separator class
20569  * 
20570  * @constructor
20571  * Create a new Separator
20572  * @param {Object} config The config object
20573  */
20574
20575
20576 Roo.bootstrap.menu.Separator = function(config){
20577     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20578 };
20579
20580 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20581     
20582     getAutoCreate : function(){
20583         var cfg = {
20584             tag : 'li',
20585             cls: 'divider'
20586         };
20587         
20588         return cfg;
20589     }
20590    
20591 });
20592
20593  
20594
20595  /*
20596  * - LGPL
20597  *
20598  * Tooltip
20599  * 
20600  */
20601
20602 /**
20603  * @class Roo.bootstrap.Tooltip
20604  * Bootstrap Tooltip class
20605  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20606  * to determine which dom element triggers the tooltip.
20607  * 
20608  * It needs to add support for additional attributes like tooltip-position
20609  * 
20610  * @constructor
20611  * Create a new Toolti
20612  * @param {Object} config The config object
20613  */
20614
20615 Roo.bootstrap.Tooltip = function(config){
20616     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20617 };
20618
20619 Roo.apply(Roo.bootstrap.Tooltip, {
20620     /**
20621      * @function init initialize tooltip monitoring.
20622      * @static
20623      */
20624     currentEl : false,
20625     currentTip : false,
20626     currentRegion : false,
20627     
20628     //  init : delay?
20629     
20630     init : function()
20631     {
20632         Roo.get(document).on('mouseover', this.enter ,this);
20633         Roo.get(document).on('mouseout', this.leave, this);
20634          
20635         
20636         this.currentTip = new Roo.bootstrap.Tooltip();
20637     },
20638     
20639     enter : function(ev)
20640     {
20641         var dom = ev.getTarget();
20642         //Roo.log(['enter',dom]);
20643         var el = Roo.fly(dom);
20644         if (this.currentEl) {
20645             //Roo.log(dom);
20646             //Roo.log(this.currentEl);
20647             //Roo.log(this.currentEl.contains(dom));
20648             if (this.currentEl == el) {
20649                 return;
20650             }
20651             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20652                 return;
20653             }
20654
20655         }
20656         
20657         
20658         
20659         if (this.currentTip.el) {
20660             this.currentTip.el.hide(); // force hiding...
20661         }    
20662         //Roo.log(el);
20663         if (!el.attr('tooltip')) { // parents who have tip?
20664             return;
20665         }
20666         this.currentEl = el;
20667         this.currentTip.bind(el);
20668         this.currentRegion = Roo.lib.Region.getRegion(dom);
20669         this.currentTip.enter();
20670         
20671     },
20672     leave : function(ev)
20673     {
20674         var dom = ev.getTarget();
20675         //Roo.log(['leave',dom]);
20676         if (!this.currentEl) {
20677             return;
20678         }
20679         
20680         
20681         if (dom != this.currentEl.dom) {
20682             return;
20683         }
20684         var xy = ev.getXY();
20685         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
20686             return;
20687         }
20688         // only activate leave if mouse cursor is outside... bounding box..
20689         
20690         
20691         
20692         
20693         if (this.currentTip) {
20694             this.currentTip.leave();
20695         }
20696         //Roo.log('clear currentEl');
20697         this.currentEl = false;
20698         
20699         
20700     },
20701     alignment : {
20702         'left' : ['r-l', [-2,0], 'right'],
20703         'right' : ['l-r', [2,0], 'left'],
20704         'bottom' : ['t-b', [0,2], 'top'],
20705         'top' : [ 'b-t', [0,-2], 'bottom']
20706     }
20707     
20708 });
20709
20710
20711 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
20712     
20713     
20714     bindEl : false,
20715     
20716     delay : null, // can be { show : 300 , hide: 500}
20717     
20718     timeout : null,
20719     
20720     hoverState : null, //???
20721     
20722     placement : 'bottom', 
20723     
20724     getAutoCreate : function(){
20725     
20726         var cfg = {
20727            cls : 'tooltip',
20728            role : 'tooltip',
20729            cn : [
20730                 {
20731                     cls : 'tooltip-arrow'
20732                 },
20733                 {
20734                     cls : 'tooltip-inner'
20735                 }
20736            ]
20737         };
20738         
20739         return cfg;
20740     },
20741     bind : function(el)
20742     {
20743         this.bindEl = el;
20744     },
20745       
20746     
20747     enter : function () {
20748        
20749         if (this.timeout != null) {
20750             clearTimeout(this.timeout);
20751         }
20752         
20753         this.hoverState = 'in'
20754          //Roo.log("enter - show");
20755         if (!this.delay || !this.delay.show) {
20756             this.show();
20757             return;
20758         }
20759         var _t = this;
20760         this.timeout = setTimeout(function () {
20761             if (_t.hoverState == 'in') {
20762                 _t.show();
20763             }
20764         }, this.delay.show);
20765     },
20766     leave : function()
20767     {
20768         clearTimeout(this.timeout);
20769     
20770         this.hoverState = 'out'
20771          if (!this.delay || !this.delay.hide) {
20772             this.hide();
20773             return 
20774         }
20775        
20776         var _t = this;
20777         this.timeout = setTimeout(function () {
20778             //Roo.log("leave - timeout");
20779             
20780             if (_t.hoverState == 'out') {
20781                 _t.hide();
20782                 Roo.bootstrap.Tooltip.currentEl = false;
20783             }
20784         }, delay)
20785     },
20786     
20787     show : function ()
20788     {
20789         if (!this.el) {
20790             this.render(document.body);
20791         }
20792         // set content.
20793         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
20794         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
20795         
20796         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
20797         
20798         var placement = typeof this.placement == 'function' ?
20799             this.placement.call(this, this.el, on_el) :
20800             this.placement;
20801             
20802         var autoToken = /\s?auto?\s?/i;
20803         var autoPlace = autoToken.test(placement);
20804         if (autoPlace) {
20805             placement = placement.replace(autoToken, '') || 'top';
20806         }
20807         
20808         //this.el.detach()
20809         //this.el.setXY([0,0]);
20810         this.el.show();
20811         //this.el.dom.style.display='block';
20812         this.el.addClass(placement);
20813         
20814         //this.el.appendTo(on_el);
20815         
20816         var p = this.getPosition();
20817         var box = this.el.getBox();
20818         
20819         if (autoPlace) {
20820             // fixme..
20821         }
20822         var align = Roo.bootstrap.Tooltip.alignment[placement]
20823         this.el.alignTo(this.bindEl, align[0],align[1]);
20824         //var arrow = this.el.select('.arrow',true).first();
20825         //arrow.set(align[2], 
20826         
20827         this.el.addClass('in fade');
20828         this.hoverState = null;
20829         
20830         if (this.el.hasClass('fade')) {
20831             // fade it?
20832         }
20833         
20834     },
20835     hide : function()
20836     {
20837          
20838         if (!this.el) {
20839             return;
20840         }
20841         //this.el.setXY([0,0]);
20842         this.el.removeClass('in');
20843         //this.el.hide();
20844         
20845     }
20846     
20847 });
20848  
20849
20850