Roo/data/JsonReader.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      * @cfg {String} cursor (Optional)
4393      */
4394     /**
4395      * Returns the id of the column at the specified index.
4396      * @param {Number} index The column index
4397      * @return {String} the id
4398      */
4399     getColumnId : function(index){
4400         return this.config[index].id;
4401     },
4402
4403     /**
4404      * Returns the column for a specified id.
4405      * @param {String} id The column id
4406      * @return {Object} the column
4407      */
4408     getColumnById : function(id){
4409         return this.lookup[id];
4410     },
4411
4412     
4413     /**
4414      * Returns the column for a specified dataIndex.
4415      * @param {String} dataIndex The column dataIndex
4416      * @return {Object|Boolean} the column or false if not found
4417      */
4418     getColumnByDataIndex: function(dataIndex){
4419         var index = this.findColumnIndex(dataIndex);
4420         return index > -1 ? this.config[index] : false;
4421     },
4422     
4423     /**
4424      * Returns the index for a specified column id.
4425      * @param {String} id The column id
4426      * @return {Number} the index, or -1 if not found
4427      */
4428     getIndexById : function(id){
4429         for(var i = 0, len = this.config.length; i < len; i++){
4430             if(this.config[i].id == id){
4431                 return i;
4432             }
4433         }
4434         return -1;
4435     },
4436     
4437     /**
4438      * Returns the index for a specified column dataIndex.
4439      * @param {String} dataIndex The column dataIndex
4440      * @return {Number} the index, or -1 if not found
4441      */
4442     
4443     findColumnIndex : function(dataIndex){
4444         for(var i = 0, len = this.config.length; i < len; i++){
4445             if(this.config[i].dataIndex == dataIndex){
4446                 return i;
4447             }
4448         }
4449         return -1;
4450     },
4451     
4452     
4453     moveColumn : function(oldIndex, newIndex){
4454         var c = this.config[oldIndex];
4455         this.config.splice(oldIndex, 1);
4456         this.config.splice(newIndex, 0, c);
4457         this.dataMap = null;
4458         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4459     },
4460
4461     isLocked : function(colIndex){
4462         return this.config[colIndex].locked === true;
4463     },
4464
4465     setLocked : function(colIndex, value, suppressEvent){
4466         if(this.isLocked(colIndex) == value){
4467             return;
4468         }
4469         this.config[colIndex].locked = value;
4470         if(!suppressEvent){
4471             this.fireEvent("columnlockchange", this, colIndex, value);
4472         }
4473     },
4474
4475     getTotalLockedWidth : function(){
4476         var totalWidth = 0;
4477         for(var i = 0; i < this.config.length; i++){
4478             if(this.isLocked(i) && !this.isHidden(i)){
4479                 this.totalWidth += this.getColumnWidth(i);
4480             }
4481         }
4482         return totalWidth;
4483     },
4484
4485     getLockedCount : function(){
4486         for(var i = 0, len = this.config.length; i < len; i++){
4487             if(!this.isLocked(i)){
4488                 return i;
4489             }
4490         }
4491     },
4492
4493     /**
4494      * Returns the number of columns.
4495      * @return {Number}
4496      */
4497     getColumnCount : function(visibleOnly){
4498         if(visibleOnly === true){
4499             var c = 0;
4500             for(var i = 0, len = this.config.length; i < len; i++){
4501                 if(!this.isHidden(i)){
4502                     c++;
4503                 }
4504             }
4505             return c;
4506         }
4507         return this.config.length;
4508     },
4509
4510     /**
4511      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4512      * @param {Function} fn
4513      * @param {Object} scope (optional)
4514      * @return {Array} result
4515      */
4516     getColumnsBy : function(fn, scope){
4517         var r = [];
4518         for(var i = 0, len = this.config.length; i < len; i++){
4519             var c = this.config[i];
4520             if(fn.call(scope||this, c, i) === true){
4521                 r[r.length] = c;
4522             }
4523         }
4524         return r;
4525     },
4526
4527     /**
4528      * Returns true if the specified column is sortable.
4529      * @param {Number} col The column index
4530      * @return {Boolean}
4531      */
4532     isSortable : function(col){
4533         if(typeof this.config[col].sortable == "undefined"){
4534             return this.defaultSortable;
4535         }
4536         return this.config[col].sortable;
4537     },
4538
4539     /**
4540      * Returns the rendering (formatting) function defined for the column.
4541      * @param {Number} col The column index.
4542      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4543      */
4544     getRenderer : function(col){
4545         if(!this.config[col].renderer){
4546             return Roo.grid.ColumnModel.defaultRenderer;
4547         }
4548         return this.config[col].renderer;
4549     },
4550
4551     /**
4552      * Sets the rendering (formatting) function for a column.
4553      * @param {Number} col The column index
4554      * @param {Function} fn The function to use to process the cell's raw data
4555      * to return HTML markup for the grid view. The render function is called with
4556      * the following parameters:<ul>
4557      * <li>Data value.</li>
4558      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4559      * <li>css A CSS style string to apply to the table cell.</li>
4560      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4561      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4562      * <li>Row index</li>
4563      * <li>Column index</li>
4564      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4565      */
4566     setRenderer : function(col, fn){
4567         this.config[col].renderer = fn;
4568     },
4569
4570     /**
4571      * Returns the width for the specified column.
4572      * @param {Number} col The column index
4573      * @return {Number}
4574      */
4575     getColumnWidth : function(col){
4576         return this.config[col].width * 1 || this.defaultWidth;
4577     },
4578
4579     /**
4580      * Sets the width for a column.
4581      * @param {Number} col The column index
4582      * @param {Number} width The new width
4583      */
4584     setColumnWidth : function(col, width, suppressEvent){
4585         this.config[col].width = width;
4586         this.totalWidth = null;
4587         if(!suppressEvent){
4588              this.fireEvent("widthchange", this, col, width);
4589         }
4590     },
4591
4592     /**
4593      * Returns the total width of all columns.
4594      * @param {Boolean} includeHidden True to include hidden column widths
4595      * @return {Number}
4596      */
4597     getTotalWidth : function(includeHidden){
4598         if(!this.totalWidth){
4599             this.totalWidth = 0;
4600             for(var i = 0, len = this.config.length; i < len; i++){
4601                 if(includeHidden || !this.isHidden(i)){
4602                     this.totalWidth += this.getColumnWidth(i);
4603                 }
4604             }
4605         }
4606         return this.totalWidth;
4607     },
4608
4609     /**
4610      * Returns the header for the specified column.
4611      * @param {Number} col The column index
4612      * @return {String}
4613      */
4614     getColumnHeader : function(col){
4615         return this.config[col].header;
4616     },
4617
4618     /**
4619      * Sets the header for a column.
4620      * @param {Number} col The column index
4621      * @param {String} header The new header
4622      */
4623     setColumnHeader : function(col, header){
4624         this.config[col].header = header;
4625         this.fireEvent("headerchange", this, col, header);
4626     },
4627
4628     /**
4629      * Returns the tooltip for the specified column.
4630      * @param {Number} col The column index
4631      * @return {String}
4632      */
4633     getColumnTooltip : function(col){
4634             return this.config[col].tooltip;
4635     },
4636     /**
4637      * Sets the tooltip for a column.
4638      * @param {Number} col The column index
4639      * @param {String} tooltip The new tooltip
4640      */
4641     setColumnTooltip : function(col, tooltip){
4642             this.config[col].tooltip = tooltip;
4643     },
4644
4645     /**
4646      * Returns the dataIndex for the specified column.
4647      * @param {Number} col The column index
4648      * @return {Number}
4649      */
4650     getDataIndex : function(col){
4651         return this.config[col].dataIndex;
4652     },
4653
4654     /**
4655      * Sets the dataIndex for a column.
4656      * @param {Number} col The column index
4657      * @param {Number} dataIndex The new dataIndex
4658      */
4659     setDataIndex : function(col, dataIndex){
4660         this.config[col].dataIndex = dataIndex;
4661     },
4662
4663     
4664     
4665     /**
4666      * Returns true if the cell is editable.
4667      * @param {Number} colIndex The column index
4668      * @param {Number} rowIndex The row index
4669      * @return {Boolean}
4670      */
4671     isCellEditable : function(colIndex, rowIndex){
4672         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4673     },
4674
4675     /**
4676      * Returns the editor defined for the cell/column.
4677      * return false or null to disable editing.
4678      * @param {Number} colIndex The column index
4679      * @param {Number} rowIndex The row index
4680      * @return {Object}
4681      */
4682     getCellEditor : function(colIndex, rowIndex){
4683         return this.config[colIndex].editor;
4684     },
4685
4686     /**
4687      * Sets if a column is editable.
4688      * @param {Number} col The column index
4689      * @param {Boolean} editable True if the column is editable
4690      */
4691     setEditable : function(col, editable){
4692         this.config[col].editable = editable;
4693     },
4694
4695
4696     /**
4697      * Returns true if the column is hidden.
4698      * @param {Number} colIndex The column index
4699      * @return {Boolean}
4700      */
4701     isHidden : function(colIndex){
4702         return this.config[colIndex].hidden;
4703     },
4704
4705
4706     /**
4707      * Returns true if the column width cannot be changed
4708      */
4709     isFixed : function(colIndex){
4710         return this.config[colIndex].fixed;
4711     },
4712
4713     /**
4714      * Returns true if the column can be resized
4715      * @return {Boolean}
4716      */
4717     isResizable : function(colIndex){
4718         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4719     },
4720     /**
4721      * Sets if a column is hidden.
4722      * @param {Number} colIndex The column index
4723      * @param {Boolean} hidden True if the column is hidden
4724      */
4725     setHidden : function(colIndex, hidden){
4726         this.config[colIndex].hidden = hidden;
4727         this.totalWidth = null;
4728         this.fireEvent("hiddenchange", this, colIndex, hidden);
4729     },
4730
4731     /**
4732      * Sets the editor for a column.
4733      * @param {Number} col The column index
4734      * @param {Object} editor The editor object
4735      */
4736     setEditor : function(col, editor){
4737         this.config[col].editor = editor;
4738     }
4739 });
4740
4741 Roo.grid.ColumnModel.defaultRenderer = function(value){
4742         if(typeof value == "string" && value.length < 1){
4743             return "&#160;";
4744         }
4745         return value;
4746 };
4747
4748 // Alias for backwards compatibility
4749 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4750 /*
4751  * Based on:
4752  * Ext JS Library 1.1.1
4753  * Copyright(c) 2006-2007, Ext JS, LLC.
4754  *
4755  * Originally Released Under LGPL - original licence link has changed is not relivant.
4756  *
4757  * Fork - LGPL
4758  * <script type="text/javascript">
4759  */
4760  
4761 /**
4762  * @class Roo.LoadMask
4763  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4764  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4765  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4766  * element's UpdateManager load indicator and will be destroyed after the initial load.
4767  * @constructor
4768  * Create a new LoadMask
4769  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4770  * @param {Object} config The config object
4771  */
4772 Roo.LoadMask = function(el, config){
4773     this.el = Roo.get(el);
4774     Roo.apply(this, config);
4775     if(this.store){
4776         this.store.on('beforeload', this.onBeforeLoad, this);
4777         this.store.on('load', this.onLoad, this);
4778         this.store.on('loadexception', this.onLoadException, this);
4779         this.removeMask = false;
4780     }else{
4781         var um = this.el.getUpdateManager();
4782         um.showLoadIndicator = false; // disable the default indicator
4783         um.on('beforeupdate', this.onBeforeLoad, this);
4784         um.on('update', this.onLoad, this);
4785         um.on('failure', this.onLoad, this);
4786         this.removeMask = true;
4787     }
4788 };
4789
4790 Roo.LoadMask.prototype = {
4791     /**
4792      * @cfg {Boolean} removeMask
4793      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4794      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4795      */
4796     /**
4797      * @cfg {String} msg
4798      * The text to display in a centered loading message box (defaults to 'Loading...')
4799      */
4800     msg : 'Loading...',
4801     /**
4802      * @cfg {String} msgCls
4803      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4804      */
4805     msgCls : 'x-mask-loading',
4806
4807     /**
4808      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4809      * @type Boolean
4810      */
4811     disabled: false,
4812
4813     /**
4814      * Disables the mask to prevent it from being displayed
4815      */
4816     disable : function(){
4817        this.disabled = true;
4818     },
4819
4820     /**
4821      * Enables the mask so that it can be displayed
4822      */
4823     enable : function(){
4824         this.disabled = false;
4825     },
4826     
4827     onLoadException : function()
4828     {
4829         Roo.log(arguments);
4830         
4831         if (typeof(arguments[3]) != 'undefined') {
4832             Roo.MessageBox.alert("Error loading",arguments[3]);
4833         } 
4834         /*
4835         try {
4836             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4837                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4838             }   
4839         } catch(e) {
4840             
4841         }
4842         */
4843     
4844         
4845         
4846         this.el.unmask(this.removeMask);
4847     },
4848     // private
4849     onLoad : function()
4850     {
4851         this.el.unmask(this.removeMask);
4852     },
4853
4854     // private
4855     onBeforeLoad : function(){
4856         if(!this.disabled){
4857             this.el.mask(this.msg, this.msgCls);
4858         }
4859     },
4860
4861     // private
4862     destroy : function(){
4863         if(this.store){
4864             this.store.un('beforeload', this.onBeforeLoad, this);
4865             this.store.un('load', this.onLoad, this);
4866             this.store.un('loadexception', this.onLoadException, this);
4867         }else{
4868             var um = this.el.getUpdateManager();
4869             um.un('beforeupdate', this.onBeforeLoad, this);
4870             um.un('update', this.onLoad, this);
4871             um.un('failure', this.onLoad, this);
4872         }
4873     }
4874 };/*
4875  * - LGPL
4876  *
4877  * table
4878  * 
4879  */
4880
4881 /**
4882  * @class Roo.bootstrap.Table
4883  * @extends Roo.bootstrap.Component
4884  * Bootstrap Table class
4885  * @cfg {String} cls table class
4886  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4887  * @cfg {String} bgcolor Specifies the background color for a table
4888  * @cfg {Number} border Specifies whether the table cells should have borders or not
4889  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4890  * @cfg {Number} cellspacing Specifies the space between cells
4891  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4892  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4893  * @cfg {String} sortable Specifies that the table should be sortable
4894  * @cfg {String} summary Specifies a summary of the content of a table
4895  * @cfg {Number} width Specifies the width of a table
4896  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4897  * 
4898  * @cfg {boolean} striped Should the rows be alternative striped
4899  * @cfg {boolean} bordered Add borders to the table
4900  * @cfg {boolean} hover Add hover highlighting
4901  * @cfg {boolean} condensed Format condensed
4902  * @cfg {boolean} responsive Format condensed
4903  * @cfg {Boolean} loadMask (true|false) default false
4904  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4905  * @cfg {Boolean} thead (true|false) generate thead, default true
4906  * @cfg {Boolean} RowSelection (true|false) default false
4907  * @cfg {Boolean} CellSelection (true|false) default false
4908  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4909  
4910  * 
4911  * @constructor
4912  * Create a new Table
4913  * @param {Object} config The config object
4914  */
4915
4916 Roo.bootstrap.Table = function(config){
4917     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4918     
4919     if (this.sm) {
4920         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4921         this.sm = this.selModel;
4922         this.sm.xmodule = this.xmodule || false;
4923     }
4924     if (this.cm && typeof(this.cm.config) == 'undefined') {
4925         this.colModel = new Roo.grid.ColumnModel(this.cm);
4926         this.cm = this.colModel;
4927         this.cm.xmodule = this.xmodule || false;
4928     }
4929     if (this.store) {
4930         this.store= Roo.factory(this.store, Roo.data);
4931         this.ds = this.store;
4932         this.ds.xmodule = this.xmodule || false;
4933          
4934     }
4935     if (this.footer && this.store) {
4936         this.footer.dataSource = this.ds;
4937         this.footer = Roo.factory(this.footer);
4938     }
4939     
4940     /** @private */
4941     this.addEvents({
4942         /**
4943          * @event cellclick
4944          * Fires when a cell is clicked
4945          * @param {Roo.bootstrap.Table} this
4946          * @param {Roo.Element} el
4947          * @param {Number} rowIndex
4948          * @param {Number} columnIndex
4949          * @param {Roo.EventObject} e
4950          */
4951         "cellclick" : true,
4952         /**
4953          * @event celldblclick
4954          * Fires when a cell is double clicked
4955          * @param {Roo.bootstrap.Table} this
4956          * @param {Roo.Element} el
4957          * @param {Number} rowIndex
4958          * @param {Number} columnIndex
4959          * @param {Roo.EventObject} e
4960          */
4961         "celldblclick" : true,
4962         /**
4963          * @event rowclick
4964          * Fires when a row is clicked
4965          * @param {Roo.bootstrap.Table} this
4966          * @param {Roo.Element} el
4967          * @param {Number} rowIndex
4968          * @param {Roo.EventObject} e
4969          */
4970         "rowclick" : true,
4971         /**
4972          * @event rowdblclick
4973          * Fires when a row is double clicked
4974          * @param {Roo.bootstrap.Table} this
4975          * @param {Roo.Element} el
4976          * @param {Number} rowIndex
4977          * @param {Roo.EventObject} e
4978          */
4979         "rowdblclick" : true,
4980         /**
4981          * @event mouseover
4982          * Fires when a mouseover occur
4983          * @param {Roo.bootstrap.Table} this
4984          * @param {Roo.Element} el
4985          * @param {Number} rowIndex
4986          * @param {Number} columnIndex
4987          * @param {Roo.EventObject} e
4988          */
4989         "mouseover" : true,
4990         /**
4991          * @event mouseout
4992          * Fires when a mouseout occur
4993          * @param {Roo.bootstrap.Table} this
4994          * @param {Roo.Element} el
4995          * @param {Number} rowIndex
4996          * @param {Number} columnIndex
4997          * @param {Roo.EventObject} e
4998          */
4999         "mouseout" : true,
5000         /**
5001          * @event rowclass
5002          * Fires when a row is rendered, so you can change add a style to it.
5003          * @param {Roo.bootstrap.Table} this
5004          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5005          */
5006         'rowclass' : true
5007         
5008     });
5009 };
5010
5011 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5012     
5013     cls: false,
5014     align: false,
5015     bgcolor: false,
5016     border: false,
5017     cellpadding: false,
5018     cellspacing: false,
5019     frame: false,
5020     rules: false,
5021     sortable: false,
5022     summary: false,
5023     width: false,
5024     striped : false,
5025     bordered: false,
5026     hover:  false,
5027     condensed : false,
5028     responsive : false,
5029     sm : false,
5030     cm : false,
5031     store : false,
5032     loadMask : false,
5033     tfoot : true,
5034     thead : true,
5035     RowSelection : false,
5036     CellSelection : false,
5037     layout : false,
5038     
5039     // Roo.Element - the tbody
5040     mainBody: false, 
5041     
5042     getAutoCreate : function(){
5043         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5044         
5045         cfg = {
5046             tag: 'table',
5047             cls : 'table',
5048             cn : []
5049         }
5050             
5051         if (this.striped) {
5052             cfg.cls += ' table-striped';
5053         }
5054         
5055         if (this.hover) {
5056             cfg.cls += ' table-hover';
5057         }
5058         if (this.bordered) {
5059             cfg.cls += ' table-bordered';
5060         }
5061         if (this.condensed) {
5062             cfg.cls += ' table-condensed';
5063         }
5064         if (this.responsive) {
5065             cfg.cls += ' table-responsive';
5066         }
5067         
5068         if (this.cls) {
5069             cfg.cls+=  ' ' +this.cls;
5070         }
5071         
5072         // this lot should be simplifed...
5073         
5074         if (this.align) {
5075             cfg.align=this.align;
5076         }
5077         if (this.bgcolor) {
5078             cfg.bgcolor=this.bgcolor;
5079         }
5080         if (this.border) {
5081             cfg.border=this.border;
5082         }
5083         if (this.cellpadding) {
5084             cfg.cellpadding=this.cellpadding;
5085         }
5086         if (this.cellspacing) {
5087             cfg.cellspacing=this.cellspacing;
5088         }
5089         if (this.frame) {
5090             cfg.frame=this.frame;
5091         }
5092         if (this.rules) {
5093             cfg.rules=this.rules;
5094         }
5095         if (this.sortable) {
5096             cfg.sortable=this.sortable;
5097         }
5098         if (this.summary) {
5099             cfg.summary=this.summary;
5100         }
5101         if (this.width) {
5102             cfg.width=this.width;
5103         }
5104         if (this.layout) {
5105             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5106         }
5107         
5108         if(this.store || this.cm){
5109             if(this.thead){
5110                 cfg.cn.push(this.renderHeader());
5111             }
5112             
5113             cfg.cn.push(this.renderBody());
5114             
5115             if(this.tfoot){
5116                 cfg.cn.push(this.renderFooter());
5117             }
5118             
5119             cfg.cls+=  ' TableGrid';
5120         }
5121         
5122         return { cn : [ cfg ] };
5123     },
5124     
5125     initEvents : function()
5126     {   
5127         if(!this.store || !this.cm){
5128             return;
5129         }
5130         
5131         //Roo.log('initEvents with ds!!!!');
5132         
5133         this.mainBody = this.el.select('tbody', true).first();
5134         
5135         
5136         var _this = this;
5137         
5138         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5139             e.on('click', _this.sort, _this);
5140         });
5141         
5142         this.el.on("click", this.onClick, this);
5143         this.el.on("dblclick", this.onDblClick, this);
5144         
5145         this.parent().el.setStyle('position', 'relative');
5146         if (this.footer) {
5147             this.footer.parentId = this.id;
5148             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5149         }
5150         
5151         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5152         
5153         this.store.on('load', this.onLoad, this);
5154         this.store.on('beforeload', this.onBeforeLoad, this);
5155         this.store.on('update', this.onUpdate, this);
5156         
5157     },
5158     
5159     onMouseover : function(e, el)
5160     {
5161         var cell = Roo.get(el);
5162         
5163         if(!cell){
5164             return;
5165         }
5166         
5167         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5168             cell = cell.findParent('td', false, true);
5169         }
5170         
5171         var row = cell.findParent('tr', false, true);
5172         var cellIndex = cell.dom.cellIndex;
5173         var rowIndex = row.dom.rowIndex - 1; // start from 0
5174         
5175         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5176         
5177     },
5178     
5179     onMouseout : function(e, el)
5180     {
5181         var cell = Roo.get(el);
5182         
5183         if(!cell){
5184             return;
5185         }
5186         
5187         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5188             cell = cell.findParent('td', false, true);
5189         }
5190         
5191         var row = cell.findParent('tr', false, true);
5192         var cellIndex = cell.dom.cellIndex;
5193         var rowIndex = row.dom.rowIndex - 1; // start from 0
5194         
5195         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5196         
5197     },
5198     
5199     onClick : function(e, el)
5200     {
5201         var cell = Roo.get(el);
5202         
5203         if(!cell || (!this.CellSelection && !this.RowSelection)){
5204             return;
5205         }
5206         
5207         
5208         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5209             cell = cell.findParent('td', false, true);
5210         }
5211         
5212         var row = cell.findParent('tr', false, true);
5213         var cellIndex = cell.dom.cellIndex;
5214         var rowIndex = row.dom.rowIndex - 1;
5215         
5216         if(this.CellSelection){
5217             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5218         }
5219         
5220         if(this.RowSelection){
5221             this.fireEvent('rowclick', this, row, rowIndex, e);
5222         }
5223         
5224         
5225     },
5226     
5227     onDblClick : function(e,el)
5228     {
5229         var cell = Roo.get(el);
5230         
5231         if(!cell || (!this.CellSelection && !this.RowSelection)){
5232             return;
5233         }
5234         
5235         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5236             cell = cell.findParent('td', false, true);
5237         }
5238         
5239         var row = cell.findParent('tr', false, true);
5240         var cellIndex = cell.dom.cellIndex;
5241         var rowIndex = row.dom.rowIndex - 1;
5242         
5243         if(this.CellSelection){
5244             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5245         }
5246         
5247         if(this.RowSelection){
5248             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5249         }
5250     },
5251     
5252     sort : function(e,el)
5253     {
5254         var col = Roo.get(el)
5255         
5256         if(!col.hasClass('sortable')){
5257             return;
5258         }
5259         
5260         var sort = col.attr('sort');
5261         var dir = 'ASC';
5262         
5263         if(col.hasClass('glyphicon-arrow-up')){
5264             dir = 'DESC';
5265         }
5266         
5267         this.store.sortInfo = {field : sort, direction : dir};
5268         
5269         if (this.footer) {
5270             Roo.log("calling footer first");
5271             this.footer.onClick('first');
5272         } else {
5273         
5274             this.store.load({ params : { start : 0 } });
5275         }
5276     },
5277     
5278     renderHeader : function()
5279     {
5280         var header = {
5281             tag: 'thead',
5282             cn : []
5283         };
5284         
5285         var cm = this.cm;
5286         
5287         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5288             
5289             var config = cm.config[i];
5290                     
5291             var c = {
5292                 tag: 'th',
5293                 style : '',
5294                 html: cm.getColumnHeader(i)
5295             };
5296             
5297             if(typeof(config.hidden) != 'undefined' && config.hidden){
5298                 c.style += ' display:none;';
5299             }
5300             
5301             if(typeof(config.dataIndex) != 'undefined'){
5302                 c.sort = config.dataIndex;
5303             }
5304             
5305             if(typeof(config.sortable) != 'undefined' && config.sortable){
5306                 c.cls = 'sortable';
5307             }
5308             
5309             if(typeof(config.align) != 'undefined' && config.align.length){
5310                 c.style += ' text-align:' + config.align + ';';
5311             }
5312             
5313             if(typeof(config.width) != 'undefined'){
5314                 c.style += ' width:' + config.width + 'px;';
5315             }
5316             
5317             header.cn.push(c)
5318         }
5319         
5320         return header;
5321     },
5322     
5323     renderBody : function()
5324     {
5325         var body = {
5326             tag: 'tbody',
5327             cn : [
5328                 {
5329                     tag: 'tr',
5330                     cn : [
5331                         {
5332                             tag : 'td',
5333                             colspan :  this.cm.getColumnCount()
5334                         }
5335                     ]
5336                 }
5337             ]
5338         };
5339         
5340         return body;
5341     },
5342     
5343     renderFooter : function()
5344     {
5345         var footer = {
5346             tag: 'tfoot',
5347             cn : [
5348                 {
5349                     tag: 'tr',
5350                     cn : [
5351                         {
5352                             tag : 'td',
5353                             colspan :  this.cm.getColumnCount()
5354                         }
5355                     ]
5356                 }
5357             ]
5358         };
5359         
5360         return footer;
5361     },
5362     
5363     
5364     
5365     onLoad : function()
5366     {
5367         Roo.log('ds onload');
5368         this.clear();
5369         
5370         var _this = this;
5371         var cm = this.cm;
5372         var ds = this.store;
5373         
5374         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5375             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5376             
5377             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5378                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5379             }
5380             
5381             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5382                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5383             }
5384         });
5385         
5386         var tbody =  this.mainBody;
5387               
5388         if(ds.getCount() > 0){
5389             ds.data.each(function(d,rowIndex){
5390                 var row =  this.renderRow(cm, ds, rowIndex);
5391                 
5392                 tbody.createChild(row);
5393                 
5394                 var _this = this;
5395                 
5396                 if(row.cellObjects.length){
5397                     Roo.each(row.cellObjects, function(r){
5398                         _this.renderCellObject(r);
5399                     })
5400                 }
5401                 
5402             }, this);
5403         }
5404         
5405         Roo.each(this.el.select('tbody td', true).elements, function(e){
5406             e.on('mouseover', _this.onMouseover, _this);
5407         });
5408         
5409         Roo.each(this.el.select('tbody td', true).elements, function(e){
5410             e.on('mouseout', _this.onMouseout, _this);
5411         });
5412
5413         //if(this.loadMask){
5414         //    this.maskEl.hide();
5415         //}
5416     },
5417     
5418     
5419     onUpdate : function(ds,record)
5420     {
5421         this.refreshRow(record);
5422     },
5423     onRemove : function(ds, record, index, isUpdate){
5424         if(isUpdate !== true){
5425             this.fireEvent("beforerowremoved", this, index, record);
5426         }
5427         var bt = this.mainBody.dom;
5428         if(bt.rows[index]){
5429             bt.removeChild(bt.rows[index]);
5430         }
5431         
5432         if(isUpdate !== true){
5433             //this.stripeRows(index);
5434             //this.syncRowHeights(index, index);
5435             //this.layout();
5436             this.fireEvent("rowremoved", this, index, record);
5437         }
5438     },
5439     
5440     
5441     refreshRow : function(record){
5442         var ds = this.store, index;
5443         if(typeof record == 'number'){
5444             index = record;
5445             record = ds.getAt(index);
5446         }else{
5447             index = ds.indexOf(record);
5448         }
5449         this.insertRow(ds, index, true);
5450         this.onRemove(ds, record, index+1, true);
5451         //this.syncRowHeights(index, index);
5452         //this.layout();
5453         this.fireEvent("rowupdated", this, index, record);
5454     },
5455     
5456     insertRow : function(dm, rowIndex, isUpdate){
5457         
5458         if(!isUpdate){
5459             this.fireEvent("beforerowsinserted", this, rowIndex);
5460         }
5461             //var s = this.getScrollState();
5462         var row = this.renderRow(this.cm, this.store, rowIndex);
5463         // insert before rowIndex..
5464         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5465         
5466         var _this = this;
5467                 
5468         if(row.cellObjects.length){
5469             Roo.each(row.cellObjects, function(r){
5470                 _this.renderCellObject(r);
5471             })
5472         }
5473             
5474         if(!isUpdate){
5475             this.fireEvent("rowsinserted", this, rowIndex);
5476             //this.syncRowHeights(firstRow, lastRow);
5477             //this.stripeRows(firstRow);
5478             //this.layout();
5479         }
5480         
5481     },
5482     
5483     
5484     getRowDom : function(rowIndex)
5485     {
5486         // not sure if I need to check this.. but let's do it anyway..
5487         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5488                 this.mainBody.dom.rows[rowIndex] : false
5489     },
5490     // returns the object tree for a tr..
5491   
5492     
5493     renderRow : function(cm, ds, rowIndex) {
5494         
5495         var d = ds.getAt(rowIndex);
5496         
5497         var row = {
5498             tag : 'tr',
5499             cn : []
5500         };
5501             
5502         var cellObjects = [];
5503         
5504         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5505             var config = cm.config[i];
5506             
5507             var renderer = cm.getRenderer(i);
5508             var value = '';
5509             var id = false;
5510             
5511             if(typeof(renderer) !== 'undefined'){
5512                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5513             }
5514             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5515             // and are rendered into the cells after the row is rendered - using the id for the element.
5516             
5517             if(typeof(value) === 'object'){
5518                 id = Roo.id();
5519                 cellObjects.push({
5520                     container : id,
5521                     cfg : value 
5522                 })
5523             }
5524             
5525             var rowcfg = {
5526                 record: d,
5527                 rowIndex : rowIndex,
5528                 colIndex : i,
5529                 rowClass : ''
5530             }
5531
5532             this.fireEvent('rowclass', this, rowcfg);
5533             
5534             var td = {
5535                 tag: 'td',
5536                 cls : rowcfg.rowClass,
5537                 style: '',
5538                 html: (typeof(value) === 'object') ? '' : value
5539             };
5540             
5541             if (id) {
5542                 td.id = id;
5543             }
5544             
5545             if(typeof(config.hidden) != 'undefined' && config.hidden){
5546                 td.style += ' display:none;';
5547             }
5548             
5549             if(typeof(config.align) != 'undefined' && config.align.length){
5550                 td.style += ' text-align:' + config.align + ';';
5551             }
5552             
5553             if(typeof(config.width) != 'undefined'){
5554                 td.style += ' width:' +  config.width + 'px;';
5555             }
5556             
5557             if(typeof(config.cursor) != 'undefined'){
5558                 td.style += ' cursor:' +  config.cursor + ';';
5559             }
5560              
5561             row.cn.push(td);
5562            
5563         }
5564         
5565         row.cellObjects = cellObjects;
5566         
5567         return row;
5568           
5569     },
5570     
5571     
5572     
5573     onBeforeLoad : function()
5574     {
5575         //Roo.log('ds onBeforeLoad');
5576         
5577         //this.clear();
5578         
5579         //if(this.loadMask){
5580         //    this.maskEl.show();
5581         //}
5582     },
5583     
5584     clear : function()
5585     {
5586         this.el.select('tbody', true).first().dom.innerHTML = '';
5587     },
5588     
5589     getSelectionModel : function(){
5590         if(!this.selModel){
5591             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5592         }
5593         return this.selModel;
5594     },
5595     /*
5596      * Render the Roo.bootstrap object from renderder
5597      */
5598     renderCellObject : function(r)
5599     {
5600         var _this = this;
5601         
5602         var t = r.cfg.render(r.container);
5603         
5604         if(r.cfg.cn){
5605             Roo.each(r.cfg.cn, function(c){
5606                 var child = {
5607                     container: t.getChildContainer(),
5608                     cfg: c
5609                 }
5610                 _this.renderCellObject(child);
5611             })
5612         }
5613     }
5614    
5615 });
5616
5617  
5618
5619  /*
5620  * - LGPL
5621  *
5622  * table cell
5623  * 
5624  */
5625
5626 /**
5627  * @class Roo.bootstrap.TableCell
5628  * @extends Roo.bootstrap.Component
5629  * Bootstrap TableCell class
5630  * @cfg {String} html cell contain text
5631  * @cfg {String} cls cell class
5632  * @cfg {String} tag cell tag (td|th) default td
5633  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5634  * @cfg {String} align Aligns the content in a cell
5635  * @cfg {String} axis Categorizes cells
5636  * @cfg {String} bgcolor Specifies the background color of a cell
5637  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5638  * @cfg {Number} colspan Specifies the number of columns a cell should span
5639  * @cfg {String} headers Specifies one or more header cells a cell is related to
5640  * @cfg {Number} height Sets the height of a cell
5641  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5642  * @cfg {Number} rowspan Sets the number of rows a cell should span
5643  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5644  * @cfg {String} valign Vertical aligns the content in a cell
5645  * @cfg {Number} width Specifies the width of a cell
5646  * 
5647  * @constructor
5648  * Create a new TableCell
5649  * @param {Object} config The config object
5650  */
5651
5652 Roo.bootstrap.TableCell = function(config){
5653     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5654 };
5655
5656 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5657     
5658     html: false,
5659     cls: false,
5660     tag: false,
5661     abbr: false,
5662     align: false,
5663     axis: false,
5664     bgcolor: false,
5665     charoff: false,
5666     colspan: false,
5667     headers: false,
5668     height: false,
5669     nowrap: false,
5670     rowspan: false,
5671     scope: false,
5672     valign: false,
5673     width: false,
5674     
5675     
5676     getAutoCreate : function(){
5677         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5678         
5679         cfg = {
5680             tag: 'td'
5681         }
5682         
5683         if(this.tag){
5684             cfg.tag = this.tag;
5685         }
5686         
5687         if (this.html) {
5688             cfg.html=this.html
5689         }
5690         if (this.cls) {
5691             cfg.cls=this.cls
5692         }
5693         if (this.abbr) {
5694             cfg.abbr=this.abbr
5695         }
5696         if (this.align) {
5697             cfg.align=this.align
5698         }
5699         if (this.axis) {
5700             cfg.axis=this.axis
5701         }
5702         if (this.bgcolor) {
5703             cfg.bgcolor=this.bgcolor
5704         }
5705         if (this.charoff) {
5706             cfg.charoff=this.charoff
5707         }
5708         if (this.colspan) {
5709             cfg.colspan=this.colspan
5710         }
5711         if (this.headers) {
5712             cfg.headers=this.headers
5713         }
5714         if (this.height) {
5715             cfg.height=this.height
5716         }
5717         if (this.nowrap) {
5718             cfg.nowrap=this.nowrap
5719         }
5720         if (this.rowspan) {
5721             cfg.rowspan=this.rowspan
5722         }
5723         if (this.scope) {
5724             cfg.scope=this.scope
5725         }
5726         if (this.valign) {
5727             cfg.valign=this.valign
5728         }
5729         if (this.width) {
5730             cfg.width=this.width
5731         }
5732         
5733         
5734         return cfg;
5735     }
5736    
5737 });
5738
5739  
5740
5741  /*
5742  * - LGPL
5743  *
5744  * table row
5745  * 
5746  */
5747
5748 /**
5749  * @class Roo.bootstrap.TableRow
5750  * @extends Roo.bootstrap.Component
5751  * Bootstrap TableRow class
5752  * @cfg {String} cls row class
5753  * @cfg {String} align Aligns the content in a table row
5754  * @cfg {String} bgcolor Specifies a background color for a table row
5755  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5756  * @cfg {String} valign Vertical aligns the content in a table row
5757  * 
5758  * @constructor
5759  * Create a new TableRow
5760  * @param {Object} config The config object
5761  */
5762
5763 Roo.bootstrap.TableRow = function(config){
5764     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5765 };
5766
5767 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5768     
5769     cls: false,
5770     align: false,
5771     bgcolor: false,
5772     charoff: false,
5773     valign: false,
5774     
5775     getAutoCreate : function(){
5776         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5777         
5778         cfg = {
5779             tag: 'tr'
5780         }
5781             
5782         if(this.cls){
5783             cfg.cls = this.cls;
5784         }
5785         if(this.align){
5786             cfg.align = this.align;
5787         }
5788         if(this.bgcolor){
5789             cfg.bgcolor = this.bgcolor;
5790         }
5791         if(this.charoff){
5792             cfg.charoff = this.charoff;
5793         }
5794         if(this.valign){
5795             cfg.valign = this.valign;
5796         }
5797         
5798         return cfg;
5799     }
5800    
5801 });
5802
5803  
5804
5805  /*
5806  * - LGPL
5807  *
5808  * table body
5809  * 
5810  */
5811
5812 /**
5813  * @class Roo.bootstrap.TableBody
5814  * @extends Roo.bootstrap.Component
5815  * Bootstrap TableBody class
5816  * @cfg {String} cls element class
5817  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5818  * @cfg {String} align Aligns the content inside the element
5819  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5820  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5821  * 
5822  * @constructor
5823  * Create a new TableBody
5824  * @param {Object} config The config object
5825  */
5826
5827 Roo.bootstrap.TableBody = function(config){
5828     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5829 };
5830
5831 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5832     
5833     cls: false,
5834     tag: false,
5835     align: false,
5836     charoff: false,
5837     valign: false,
5838     
5839     getAutoCreate : function(){
5840         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5841         
5842         cfg = {
5843             tag: 'tbody'
5844         }
5845             
5846         if (this.cls) {
5847             cfg.cls=this.cls
5848         }
5849         if(this.tag){
5850             cfg.tag = this.tag;
5851         }
5852         
5853         if(this.align){
5854             cfg.align = this.align;
5855         }
5856         if(this.charoff){
5857             cfg.charoff = this.charoff;
5858         }
5859         if(this.valign){
5860             cfg.valign = this.valign;
5861         }
5862         
5863         return cfg;
5864     }
5865     
5866     
5867 //    initEvents : function()
5868 //    {
5869 //        
5870 //        if(!this.store){
5871 //            return;
5872 //        }
5873 //        
5874 //        this.store = Roo.factory(this.store, Roo.data);
5875 //        this.store.on('load', this.onLoad, this);
5876 //        
5877 //        this.store.load();
5878 //        
5879 //    },
5880 //    
5881 //    onLoad: function () 
5882 //    {   
5883 //        this.fireEvent('load', this);
5884 //    }
5885 //    
5886 //   
5887 });
5888
5889  
5890
5891  /*
5892  * Based on:
5893  * Ext JS Library 1.1.1
5894  * Copyright(c) 2006-2007, Ext JS, LLC.
5895  *
5896  * Originally Released Under LGPL - original licence link has changed is not relivant.
5897  *
5898  * Fork - LGPL
5899  * <script type="text/javascript">
5900  */
5901
5902 // as we use this in bootstrap.
5903 Roo.namespace('Roo.form');
5904  /**
5905  * @class Roo.form.Action
5906  * Internal Class used to handle form actions
5907  * @constructor
5908  * @param {Roo.form.BasicForm} el The form element or its id
5909  * @param {Object} config Configuration options
5910  */
5911
5912  
5913  
5914 // define the action interface
5915 Roo.form.Action = function(form, options){
5916     this.form = form;
5917     this.options = options || {};
5918 };
5919 /**
5920  * Client Validation Failed
5921  * @const 
5922  */
5923 Roo.form.Action.CLIENT_INVALID = 'client';
5924 /**
5925  * Server Validation Failed
5926  * @const 
5927  */
5928 Roo.form.Action.SERVER_INVALID = 'server';
5929  /**
5930  * Connect to Server Failed
5931  * @const 
5932  */
5933 Roo.form.Action.CONNECT_FAILURE = 'connect';
5934 /**
5935  * Reading Data from Server Failed
5936  * @const 
5937  */
5938 Roo.form.Action.LOAD_FAILURE = 'load';
5939
5940 Roo.form.Action.prototype = {
5941     type : 'default',
5942     failureType : undefined,
5943     response : undefined,
5944     result : undefined,
5945
5946     // interface method
5947     run : function(options){
5948
5949     },
5950
5951     // interface method
5952     success : function(response){
5953
5954     },
5955
5956     // interface method
5957     handleResponse : function(response){
5958
5959     },
5960
5961     // default connection failure
5962     failure : function(response){
5963         
5964         this.response = response;
5965         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5966         this.form.afterAction(this, false);
5967     },
5968
5969     processResponse : function(response){
5970         this.response = response;
5971         if(!response.responseText){
5972             return true;
5973         }
5974         this.result = this.handleResponse(response);
5975         return this.result;
5976     },
5977
5978     // utility functions used internally
5979     getUrl : function(appendParams){
5980         var url = this.options.url || this.form.url || this.form.el.dom.action;
5981         if(appendParams){
5982             var p = this.getParams();
5983             if(p){
5984                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5985             }
5986         }
5987         return url;
5988     },
5989
5990     getMethod : function(){
5991         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5992     },
5993
5994     getParams : function(){
5995         var bp = this.form.baseParams;
5996         var p = this.options.params;
5997         if(p){
5998             if(typeof p == "object"){
5999                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6000             }else if(typeof p == 'string' && bp){
6001                 p += '&' + Roo.urlEncode(bp);
6002             }
6003         }else if(bp){
6004             p = Roo.urlEncode(bp);
6005         }
6006         return p;
6007     },
6008
6009     createCallback : function(){
6010         return {
6011             success: this.success,
6012             failure: this.failure,
6013             scope: this,
6014             timeout: (this.form.timeout*1000),
6015             upload: this.form.fileUpload ? this.success : undefined
6016         };
6017     }
6018 };
6019
6020 Roo.form.Action.Submit = function(form, options){
6021     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6022 };
6023
6024 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6025     type : 'submit',
6026
6027     haveProgress : false,
6028     uploadComplete : false,
6029     
6030     // uploadProgress indicator.
6031     uploadProgress : function()
6032     {
6033         if (!this.form.progressUrl) {
6034             return;
6035         }
6036         
6037         if (!this.haveProgress) {
6038             Roo.MessageBox.progress("Uploading", "Uploading");
6039         }
6040         if (this.uploadComplete) {
6041            Roo.MessageBox.hide();
6042            return;
6043         }
6044         
6045         this.haveProgress = true;
6046    
6047         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6048         
6049         var c = new Roo.data.Connection();
6050         c.request({
6051             url : this.form.progressUrl,
6052             params: {
6053                 id : uid
6054             },
6055             method: 'GET',
6056             success : function(req){
6057                //console.log(data);
6058                 var rdata = false;
6059                 var edata;
6060                 try  {
6061                    rdata = Roo.decode(req.responseText)
6062                 } catch (e) {
6063                     Roo.log("Invalid data from server..");
6064                     Roo.log(edata);
6065                     return;
6066                 }
6067                 if (!rdata || !rdata.success) {
6068                     Roo.log(rdata);
6069                     Roo.MessageBox.alert(Roo.encode(rdata));
6070                     return;
6071                 }
6072                 var data = rdata.data;
6073                 
6074                 if (this.uploadComplete) {
6075                    Roo.MessageBox.hide();
6076                    return;
6077                 }
6078                    
6079                 if (data){
6080                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6081                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6082                     );
6083                 }
6084                 this.uploadProgress.defer(2000,this);
6085             },
6086        
6087             failure: function(data) {
6088                 Roo.log('progress url failed ');
6089                 Roo.log(data);
6090             },
6091             scope : this
6092         });
6093            
6094     },
6095     
6096     
6097     run : function()
6098     {
6099         // run get Values on the form, so it syncs any secondary forms.
6100         this.form.getValues();
6101         
6102         var o = this.options;
6103         var method = this.getMethod();
6104         var isPost = method == 'POST';
6105         if(o.clientValidation === false || this.form.isValid()){
6106             
6107             if (this.form.progressUrl) {
6108                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6109                     (new Date() * 1) + '' + Math.random());
6110                     
6111             } 
6112             
6113             
6114             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6115                 form:this.form.el.dom,
6116                 url:this.getUrl(!isPost),
6117                 method: method,
6118                 params:isPost ? this.getParams() : null,
6119                 isUpload: this.form.fileUpload
6120             }));
6121             
6122             this.uploadProgress();
6123
6124         }else if (o.clientValidation !== false){ // client validation failed
6125             this.failureType = Roo.form.Action.CLIENT_INVALID;
6126             this.form.afterAction(this, false);
6127         }
6128     },
6129
6130     success : function(response)
6131     {
6132         this.uploadComplete= true;
6133         if (this.haveProgress) {
6134             Roo.MessageBox.hide();
6135         }
6136         
6137         
6138         var result = this.processResponse(response);
6139         if(result === true || result.success){
6140             this.form.afterAction(this, true);
6141             return;
6142         }
6143         if(result.errors){
6144             this.form.markInvalid(result.errors);
6145             this.failureType = Roo.form.Action.SERVER_INVALID;
6146         }
6147         this.form.afterAction(this, false);
6148     },
6149     failure : function(response)
6150     {
6151         this.uploadComplete= true;
6152         if (this.haveProgress) {
6153             Roo.MessageBox.hide();
6154         }
6155         
6156         this.response = response;
6157         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6158         this.form.afterAction(this, false);
6159     },
6160     
6161     handleResponse : function(response){
6162         if(this.form.errorReader){
6163             var rs = this.form.errorReader.read(response);
6164             var errors = [];
6165             if(rs.records){
6166                 for(var i = 0, len = rs.records.length; i < len; i++) {
6167                     var r = rs.records[i];
6168                     errors[i] = r.data;
6169                 }
6170             }
6171             if(errors.length < 1){
6172                 errors = null;
6173             }
6174             return {
6175                 success : rs.success,
6176                 errors : errors
6177             };
6178         }
6179         var ret = false;
6180         try {
6181             ret = Roo.decode(response.responseText);
6182         } catch (e) {
6183             ret = {
6184                 success: false,
6185                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6186                 errors : []
6187             };
6188         }
6189         return ret;
6190         
6191     }
6192 });
6193
6194
6195 Roo.form.Action.Load = function(form, options){
6196     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6197     this.reader = this.form.reader;
6198 };
6199
6200 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6201     type : 'load',
6202
6203     run : function(){
6204         
6205         Roo.Ajax.request(Roo.apply(
6206                 this.createCallback(), {
6207                     method:this.getMethod(),
6208                     url:this.getUrl(false),
6209                     params:this.getParams()
6210         }));
6211     },
6212
6213     success : function(response){
6214         
6215         var result = this.processResponse(response);
6216         if(result === true || !result.success || !result.data){
6217             this.failureType = Roo.form.Action.LOAD_FAILURE;
6218             this.form.afterAction(this, false);
6219             return;
6220         }
6221         this.form.clearInvalid();
6222         this.form.setValues(result.data);
6223         this.form.afterAction(this, true);
6224     },
6225
6226     handleResponse : function(response){
6227         if(this.form.reader){
6228             var rs = this.form.reader.read(response);
6229             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6230             return {
6231                 success : rs.success,
6232                 data : data
6233             };
6234         }
6235         return Roo.decode(response.responseText);
6236     }
6237 });
6238
6239 Roo.form.Action.ACTION_TYPES = {
6240     'load' : Roo.form.Action.Load,
6241     'submit' : Roo.form.Action.Submit
6242 };/*
6243  * - LGPL
6244  *
6245  * form
6246  * 
6247  */
6248
6249 /**
6250  * @class Roo.bootstrap.Form
6251  * @extends Roo.bootstrap.Component
6252  * Bootstrap Form class
6253  * @cfg {String} method  GET | POST (default POST)
6254  * @cfg {String} labelAlign top | left (default top)
6255  * @cfg {String} align left  | right - for navbars
6256  * @cfg {Boolean} loadMask load mask when submit (default true)
6257
6258  * 
6259  * @constructor
6260  * Create a new Form
6261  * @param {Object} config The config object
6262  */
6263
6264
6265 Roo.bootstrap.Form = function(config){
6266     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6267     this.addEvents({
6268         /**
6269          * @event clientvalidation
6270          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6271          * @param {Form} this
6272          * @param {Boolean} valid true if the form has passed client-side validation
6273          */
6274         clientvalidation: true,
6275         /**
6276          * @event beforeaction
6277          * Fires before any action is performed. Return false to cancel the action.
6278          * @param {Form} this
6279          * @param {Action} action The action to be performed
6280          */
6281         beforeaction: true,
6282         /**
6283          * @event actionfailed
6284          * Fires when an action fails.
6285          * @param {Form} this
6286          * @param {Action} action The action that failed
6287          */
6288         actionfailed : true,
6289         /**
6290          * @event actioncomplete
6291          * Fires when an action is completed.
6292          * @param {Form} this
6293          * @param {Action} action The action that completed
6294          */
6295         actioncomplete : true
6296     });
6297     
6298 };
6299
6300 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6301       
6302      /**
6303      * @cfg {String} method
6304      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6305      */
6306     method : 'POST',
6307     /**
6308      * @cfg {String} url
6309      * The URL to use for form actions if one isn't supplied in the action options.
6310      */
6311     /**
6312      * @cfg {Boolean} fileUpload
6313      * Set to true if this form is a file upload.
6314      */
6315      
6316     /**
6317      * @cfg {Object} baseParams
6318      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6319      */
6320       
6321     /**
6322      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6323      */
6324     timeout: 30,
6325     /**
6326      * @cfg {Sting} align (left|right) for navbar forms
6327      */
6328     align : 'left',
6329
6330     // private
6331     activeAction : null,
6332  
6333     /**
6334      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6335      * element by passing it or its id or mask the form itself by passing in true.
6336      * @type Mixed
6337      */
6338     waitMsgTarget : false,
6339     
6340     loadMask : true,
6341     
6342     getAutoCreate : function(){
6343         
6344         var cfg = {
6345             tag: 'form',
6346             method : this.method || 'POST',
6347             id : this.id || Roo.id(),
6348             cls : ''
6349         }
6350         if (this.parent().xtype.match(/^Nav/)) {
6351             cfg.cls = 'navbar-form navbar-' + this.align;
6352             
6353         }
6354         
6355         if (this.labelAlign == 'left' ) {
6356             cfg.cls += ' form-horizontal';
6357         }
6358         
6359         
6360         return cfg;
6361     },
6362     initEvents : function()
6363     {
6364         this.el.on('submit', this.onSubmit, this);
6365         // this was added as random key presses on the form where triggering form submit.
6366         this.el.on('keypress', function(e) {
6367             if (e.getCharCode() != 13) {
6368                 return true;
6369             }
6370             // we might need to allow it for textareas.. and some other items.
6371             // check e.getTarget().
6372             
6373             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6374                 return true;
6375             }
6376         
6377             Roo.log("keypress blocked");
6378             
6379             e.preventDefault();
6380             return false;
6381         });
6382         
6383     },
6384     // private
6385     onSubmit : function(e){
6386         e.stopEvent();
6387     },
6388     
6389      /**
6390      * Returns true if client-side validation on the form is successful.
6391      * @return Boolean
6392      */
6393     isValid : function(){
6394         var items = this.getItems();
6395         var valid = true;
6396         items.each(function(f){
6397            if(!f.validate()){
6398                valid = false;
6399                
6400            }
6401         });
6402         return valid;
6403     },
6404     /**
6405      * Returns true if any fields in this form have changed since their original load.
6406      * @return Boolean
6407      */
6408     isDirty : function(){
6409         var dirty = false;
6410         var items = this.getItems();
6411         items.each(function(f){
6412            if(f.isDirty()){
6413                dirty = true;
6414                return false;
6415            }
6416            return true;
6417         });
6418         return dirty;
6419     },
6420      /**
6421      * Performs a predefined action (submit or load) or custom actions you define on this form.
6422      * @param {String} actionName The name of the action type
6423      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6424      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6425      * accept other config options):
6426      * <pre>
6427 Property          Type             Description
6428 ----------------  ---------------  ----------------------------------------------------------------------------------
6429 url               String           The url for the action (defaults to the form's url)
6430 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6431 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6432 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6433                                    validate the form on the client (defaults to false)
6434      * </pre>
6435      * @return {BasicForm} this
6436      */
6437     doAction : function(action, options){
6438         if(typeof action == 'string'){
6439             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6440         }
6441         if(this.fireEvent('beforeaction', this, action) !== false){
6442             this.beforeAction(action);
6443             action.run.defer(100, action);
6444         }
6445         return this;
6446     },
6447     
6448     // private
6449     beforeAction : function(action){
6450         var o = action.options;
6451         
6452         if(this.loadMask){
6453             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6454         }
6455         // not really supported yet.. ??
6456         
6457         //if(this.waitMsgTarget === true){
6458         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6459         //}else if(this.waitMsgTarget){
6460         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6461         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6462         //}else {
6463         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6464        // }
6465          
6466     },
6467
6468     // private
6469     afterAction : function(action, success){
6470         this.activeAction = null;
6471         var o = action.options;
6472         
6473         //if(this.waitMsgTarget === true){
6474             this.el.unmask();
6475         //}else if(this.waitMsgTarget){
6476         //    this.waitMsgTarget.unmask();
6477         //}else{
6478         //    Roo.MessageBox.updateProgress(1);
6479         //    Roo.MessageBox.hide();
6480        // }
6481         // 
6482         if(success){
6483             if(o.reset){
6484                 this.reset();
6485             }
6486             Roo.callback(o.success, o.scope, [this, action]);
6487             this.fireEvent('actioncomplete', this, action);
6488             
6489         }else{
6490             
6491             // failure condition..
6492             // we have a scenario where updates need confirming.
6493             // eg. if a locking scenario exists..
6494             // we look for { errors : { needs_confirm : true }} in the response.
6495             if (
6496                 (typeof(action.result) != 'undefined')  &&
6497                 (typeof(action.result.errors) != 'undefined')  &&
6498                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6499            ){
6500                 var _t = this;
6501                 Roo.log("not supported yet");
6502                  /*
6503                 
6504                 Roo.MessageBox.confirm(
6505                     "Change requires confirmation",
6506                     action.result.errorMsg,
6507                     function(r) {
6508                         if (r != 'yes') {
6509                             return;
6510                         }
6511                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6512                     }
6513                     
6514                 );
6515                 */
6516                 
6517                 
6518                 return;
6519             }
6520             
6521             Roo.callback(o.failure, o.scope, [this, action]);
6522             // show an error message if no failed handler is set..
6523             if (!this.hasListener('actionfailed')) {
6524                 Roo.log("need to add dialog support");
6525                 /*
6526                 Roo.MessageBox.alert("Error",
6527                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6528                         action.result.errorMsg :
6529                         "Saving Failed, please check your entries or try again"
6530                 );
6531                 */
6532             }
6533             
6534             this.fireEvent('actionfailed', this, action);
6535         }
6536         
6537     },
6538     /**
6539      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6540      * @param {String} id The value to search for
6541      * @return Field
6542      */
6543     findField : function(id){
6544         var items = this.getItems();
6545         var field = items.get(id);
6546         if(!field){
6547              items.each(function(f){
6548                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6549                     field = f;
6550                     return false;
6551                 }
6552                 return true;
6553             });
6554         }
6555         return field || null;
6556     },
6557      /**
6558      * Mark fields in this form invalid in bulk.
6559      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6560      * @return {BasicForm} this
6561      */
6562     markInvalid : function(errors){
6563         if(errors instanceof Array){
6564             for(var i = 0, len = errors.length; i < len; i++){
6565                 var fieldError = errors[i];
6566                 var f = this.findField(fieldError.id);
6567                 if(f){
6568                     f.markInvalid(fieldError.msg);
6569                 }
6570             }
6571         }else{
6572             var field, id;
6573             for(id in errors){
6574                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6575                     field.markInvalid(errors[id]);
6576                 }
6577             }
6578         }
6579         //Roo.each(this.childForms || [], function (f) {
6580         //    f.markInvalid(errors);
6581         //});
6582         
6583         return this;
6584     },
6585
6586     /**
6587      * Set values for fields in this form in bulk.
6588      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6589      * @return {BasicForm} this
6590      */
6591     setValues : function(values){
6592         if(values instanceof Array){ // array of objects
6593             for(var i = 0, len = values.length; i < len; i++){
6594                 var v = values[i];
6595                 var f = this.findField(v.id);
6596                 if(f){
6597                     f.setValue(v.value);
6598                     if(this.trackResetOnLoad){
6599                         f.originalValue = f.getValue();
6600                     }
6601                 }
6602             }
6603         }else{ // object hash
6604             var field, id;
6605             for(id in values){
6606                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6607                     
6608                     if (field.setFromData && 
6609                         field.valueField && 
6610                         field.displayField &&
6611                         // combos' with local stores can 
6612                         // be queried via setValue()
6613                         // to set their value..
6614                         (field.store && !field.store.isLocal)
6615                         ) {
6616                         // it's a combo
6617                         var sd = { };
6618                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6619                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6620                         field.setFromData(sd);
6621                         
6622                     } else {
6623                         field.setValue(values[id]);
6624                     }
6625                     
6626                     
6627                     if(this.trackResetOnLoad){
6628                         field.originalValue = field.getValue();
6629                     }
6630                 }
6631             }
6632         }
6633          
6634         //Roo.each(this.childForms || [], function (f) {
6635         //    f.setValues(values);
6636         //});
6637                 
6638         return this;
6639     },
6640
6641     /**
6642      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6643      * they are returned as an array.
6644      * @param {Boolean} asString
6645      * @return {Object}
6646      */
6647     getValues : function(asString){
6648         //if (this.childForms) {
6649             // copy values from the child forms
6650         //    Roo.each(this.childForms, function (f) {
6651         //        this.setValues(f.getValues());
6652         //    }, this);
6653         //}
6654         
6655         
6656         
6657         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6658         if(asString === true){
6659             return fs;
6660         }
6661         return Roo.urlDecode(fs);
6662     },
6663     
6664     /**
6665      * Returns the fields in this form as an object with key/value pairs. 
6666      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6667      * @return {Object}
6668      */
6669     getFieldValues : function(with_hidden)
6670     {
6671         var items = this.getItems();
6672         var ret = {};
6673         items.each(function(f){
6674             if (!f.getName()) {
6675                 return;
6676             }
6677             var v = f.getValue();
6678             if (f.inputType =='radio') {
6679                 if (typeof(ret[f.getName()]) == 'undefined') {
6680                     ret[f.getName()] = ''; // empty..
6681                 }
6682                 
6683                 if (!f.el.dom.checked) {
6684                     return;
6685                     
6686                 }
6687                 v = f.el.dom.value;
6688                 
6689             }
6690             
6691             // not sure if this supported any more..
6692             if ((typeof(v) == 'object') && f.getRawValue) {
6693                 v = f.getRawValue() ; // dates..
6694             }
6695             // combo boxes where name != hiddenName...
6696             if (f.name != f.getName()) {
6697                 ret[f.name] = f.getRawValue();
6698             }
6699             ret[f.getName()] = v;
6700         });
6701         
6702         return ret;
6703     },
6704
6705     /**
6706      * Clears all invalid messages in this form.
6707      * @return {BasicForm} this
6708      */
6709     clearInvalid : function(){
6710         var items = this.getItems();
6711         
6712         items.each(function(f){
6713            f.clearInvalid();
6714         });
6715         
6716         
6717         
6718         return this;
6719     },
6720
6721     /**
6722      * Resets this form.
6723      * @return {BasicForm} this
6724      */
6725     reset : function(){
6726         var items = this.getItems();
6727         items.each(function(f){
6728             f.reset();
6729         });
6730         
6731         Roo.each(this.childForms || [], function (f) {
6732             f.reset();
6733         });
6734        
6735         
6736         return this;
6737     },
6738     getItems : function()
6739     {
6740         var r=new Roo.util.MixedCollection(false, function(o){
6741             return o.id || (o.id = Roo.id());
6742         });
6743         var iter = function(el) {
6744             if (el.inputEl) {
6745                 r.add(el);
6746             }
6747             if (!el.items) {
6748                 return;
6749             }
6750             Roo.each(el.items,function(e) {
6751                 iter(e);
6752             });
6753             
6754             
6755         };
6756         iter(this);
6757         return r;
6758         
6759         
6760         
6761         
6762     }
6763     
6764 });
6765
6766  
6767 /*
6768  * Based on:
6769  * Ext JS Library 1.1.1
6770  * Copyright(c) 2006-2007, Ext JS, LLC.
6771  *
6772  * Originally Released Under LGPL - original licence link has changed is not relivant.
6773  *
6774  * Fork - LGPL
6775  * <script type="text/javascript">
6776  */
6777 /**
6778  * @class Roo.form.VTypes
6779  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6780  * @singleton
6781  */
6782 Roo.form.VTypes = function(){
6783     // closure these in so they are only created once.
6784     var alpha = /^[a-zA-Z_]+$/;
6785     var alphanum = /^[a-zA-Z0-9_]+$/;
6786     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6787     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6788
6789     // All these messages and functions are configurable
6790     return {
6791         /**
6792          * The function used to validate email addresses
6793          * @param {String} value The email address
6794          */
6795         'email' : function(v){
6796             return email.test(v);
6797         },
6798         /**
6799          * The error text to display when the email validation function returns false
6800          * @type String
6801          */
6802         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6803         /**
6804          * The keystroke filter mask to be applied on email input
6805          * @type RegExp
6806          */
6807         'emailMask' : /[a-z0-9_\.\-@]/i,
6808
6809         /**
6810          * The function used to validate URLs
6811          * @param {String} value The URL
6812          */
6813         'url' : function(v){
6814             return url.test(v);
6815         },
6816         /**
6817          * The error text to display when the url validation function returns false
6818          * @type String
6819          */
6820         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6821         
6822         /**
6823          * The function used to validate alpha values
6824          * @param {String} value The value
6825          */
6826         'alpha' : function(v){
6827             return alpha.test(v);
6828         },
6829         /**
6830          * The error text to display when the alpha validation function returns false
6831          * @type String
6832          */
6833         'alphaText' : 'This field should only contain letters and _',
6834         /**
6835          * The keystroke filter mask to be applied on alpha input
6836          * @type RegExp
6837          */
6838         'alphaMask' : /[a-z_]/i,
6839
6840         /**
6841          * The function used to validate alphanumeric values
6842          * @param {String} value The value
6843          */
6844         'alphanum' : function(v){
6845             return alphanum.test(v);
6846         },
6847         /**
6848          * The error text to display when the alphanumeric validation function returns false
6849          * @type String
6850          */
6851         'alphanumText' : 'This field should only contain letters, numbers and _',
6852         /**
6853          * The keystroke filter mask to be applied on alphanumeric input
6854          * @type RegExp
6855          */
6856         'alphanumMask' : /[a-z0-9_]/i
6857     };
6858 }();/*
6859  * - LGPL
6860  *
6861  * Input
6862  * 
6863  */
6864
6865 /**
6866  * @class Roo.bootstrap.Input
6867  * @extends Roo.bootstrap.Component
6868  * Bootstrap Input class
6869  * @cfg {Boolean} disabled is it disabled
6870  * @cfg {String} fieldLabel - the label associated
6871  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6872  * @cfg {String} name name of the input
6873  * @cfg {string} fieldLabel - the label associated
6874  * @cfg {string}  inputType - input / file submit ...
6875  * @cfg {string} placeholder - placeholder to put in text.
6876  * @cfg {string}  before - input group add on before
6877  * @cfg {string} after - input group add on after
6878  * @cfg {string} size - (lg|sm) or leave empty..
6879  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6880  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6881  * @cfg {Number} md colspan out of 12 for computer-sized screens
6882  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6883  * @cfg {string} value default value of the input
6884  * @cfg {Number} labelWidth set the width of label (0-12)
6885  * @cfg {String} labelAlign (top|left)
6886  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6887  * @cfg {String} align (left|center|right) Default left
6888  * 
6889  * 
6890  * @constructor
6891  * Create a new Input
6892  * @param {Object} config The config object
6893  */
6894
6895 Roo.bootstrap.Input = function(config){
6896     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6897    
6898         this.addEvents({
6899             /**
6900              * @event focus
6901              * Fires when this field receives input focus.
6902              * @param {Roo.form.Field} this
6903              */
6904             focus : true,
6905             /**
6906              * @event blur
6907              * Fires when this field loses input focus.
6908              * @param {Roo.form.Field} this
6909              */
6910             blur : true,
6911             /**
6912              * @event specialkey
6913              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6914              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6915              * @param {Roo.form.Field} this
6916              * @param {Roo.EventObject} e The event object
6917              */
6918             specialkey : true,
6919             /**
6920              * @event change
6921              * Fires just before the field blurs if the field value has changed.
6922              * @param {Roo.form.Field} this
6923              * @param {Mixed} newValue The new value
6924              * @param {Mixed} oldValue The original value
6925              */
6926             change : true,
6927             /**
6928              * @event invalid
6929              * Fires after the field has been marked as invalid.
6930              * @param {Roo.form.Field} this
6931              * @param {String} msg The validation message
6932              */
6933             invalid : true,
6934             /**
6935              * @event valid
6936              * Fires after the field has been validated with no errors.
6937              * @param {Roo.form.Field} this
6938              */
6939             valid : true,
6940              /**
6941              * @event keyup
6942              * Fires after the key up
6943              * @param {Roo.form.Field} this
6944              * @param {Roo.EventObject}  e The event Object
6945              */
6946             keyup : true
6947         });
6948 };
6949
6950 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6951      /**
6952      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6953       automatic validation (defaults to "keyup").
6954      */
6955     validationEvent : "keyup",
6956      /**
6957      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6958      */
6959     validateOnBlur : true,
6960     /**
6961      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6962      */
6963     validationDelay : 250,
6964      /**
6965      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6966      */
6967     focusClass : "x-form-focus",  // not needed???
6968     
6969        
6970     /**
6971      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6972      */
6973     invalidClass : "has-error",
6974     
6975     /**
6976      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6977      */
6978     selectOnFocus : false,
6979     
6980      /**
6981      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6982      */
6983     maskRe : null,
6984        /**
6985      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6986      */
6987     vtype : null,
6988     
6989       /**
6990      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6991      */
6992     disableKeyFilter : false,
6993     
6994        /**
6995      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6996      */
6997     disabled : false,
6998      /**
6999      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7000      */
7001     allowBlank : true,
7002     /**
7003      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7004      */
7005     blankText : "This field is required",
7006     
7007      /**
7008      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7009      */
7010     minLength : 0,
7011     /**
7012      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7013      */
7014     maxLength : Number.MAX_VALUE,
7015     /**
7016      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7017      */
7018     minLengthText : "The minimum length for this field is {0}",
7019     /**
7020      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7021      */
7022     maxLengthText : "The maximum length for this field is {0}",
7023   
7024     
7025     /**
7026      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7027      * If available, this function will be called only after the basic validators all return true, and will be passed the
7028      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7029      */
7030     validator : null,
7031     /**
7032      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7033      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7034      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7035      */
7036     regex : null,
7037     /**
7038      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7039      */
7040     regexText : "",
7041     
7042     
7043     
7044     fieldLabel : '',
7045     inputType : 'text',
7046     
7047     name : false,
7048     placeholder: false,
7049     before : false,
7050     after : false,
7051     size : false,
7052     // private
7053     hasFocus : false,
7054     preventMark: false,
7055     isFormField : true,
7056     value : '',
7057     labelWidth : 2,
7058     labelAlign : false,
7059     readOnly : false,
7060     align : false,
7061     formatedValue : false,
7062     
7063     parentLabelAlign : function()
7064     {
7065         var parent = this;
7066         while (parent.parent()) {
7067             parent = parent.parent();
7068             if (typeof(parent.labelAlign) !='undefined') {
7069                 return parent.labelAlign;
7070             }
7071         }
7072         return 'left';
7073         
7074     },
7075     
7076     getAutoCreate : function(){
7077         
7078         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7079         
7080         var id = Roo.id();
7081         
7082         var cfg = {};
7083         
7084         if(this.inputType != 'hidden'){
7085             cfg.cls = 'form-group' //input-group
7086         }
7087         
7088         var input =  {
7089             tag: 'input',
7090             id : id,
7091             type : this.inputType,
7092             value : this.value,
7093             cls : 'form-control',
7094             placeholder : this.placeholder || ''
7095             
7096         };
7097         
7098         if(this.align){
7099             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7100         }
7101         
7102         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7103             input.maxLength = this.maxLength;
7104         }
7105         
7106         if (this.disabled) {
7107             input.disabled=true;
7108         }
7109         
7110         if (this.readOnly) {
7111             input.readonly=true;
7112         }
7113         
7114         if (this.name) {
7115             input.name = this.name;
7116         }
7117         if (this.size) {
7118             input.cls += ' input-' + this.size;
7119         }
7120         var settings=this;
7121         ['xs','sm','md','lg'].map(function(size){
7122             if (settings[size]) {
7123                 cfg.cls += ' col-' + size + '-' + settings[size];
7124             }
7125         });
7126         
7127         var inputblock = input;
7128         
7129         if (this.before || this.after) {
7130             
7131             inputblock = {
7132                 cls : 'input-group',
7133                 cn :  [] 
7134             };
7135             if (this.before && typeof(this.before) == 'string') {
7136                 
7137                 inputblock.cn.push({
7138                     tag :'span',
7139                     cls : 'roo-input-before input-group-addon',
7140                     html : this.before
7141                 });
7142             }
7143             if (this.before && typeof(this.before) == 'object') {
7144                 this.before = Roo.factory(this.before);
7145                 Roo.log(this.before);
7146                 inputblock.cn.push({
7147                     tag :'span',
7148                     cls : 'roo-input-before input-group-' +
7149                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7150                 });
7151             }
7152             
7153             inputblock.cn.push(input);
7154             
7155             if (this.after && typeof(this.after) == 'string') {
7156                 inputblock.cn.push({
7157                     tag :'span',
7158                     cls : 'roo-input-after input-group-addon',
7159                     html : this.after
7160                 });
7161             }
7162             if (this.after && typeof(this.after) == 'object') {
7163                 this.after = Roo.factory(this.after);
7164                 Roo.log(this.after);
7165                 inputblock.cn.push({
7166                     tag :'span',
7167                     cls : 'roo-input-after input-group-' +
7168                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7169                 });
7170             }
7171         };
7172         
7173         if (align ==='left' && this.fieldLabel.length) {
7174                 Roo.log("left and has label");
7175                 cfg.cn = [
7176                     
7177                     {
7178                         tag: 'label',
7179                         'for' :  id,
7180                         cls : 'control-label col-sm-' + this.labelWidth,
7181                         html : this.fieldLabel
7182                         
7183                     },
7184                     {
7185                         cls : "col-sm-" + (12 - this.labelWidth), 
7186                         cn: [
7187                             inputblock
7188                         ]
7189                     }
7190                     
7191                 ];
7192         } else if ( this.fieldLabel.length) {
7193                 Roo.log(" label");
7194                  cfg.cn = [
7195                    
7196                     {
7197                         tag: 'label',
7198                         //cls : 'input-group-addon',
7199                         html : this.fieldLabel
7200                         
7201                     },
7202                     
7203                     inputblock
7204                     
7205                 ];
7206
7207         } else {
7208             
7209                 Roo.log(" no label && no align");
7210                 cfg.cn = [
7211                     
7212                         inputblock
7213                     
7214                 ];
7215                 
7216                 
7217         };
7218         Roo.log('input-parentType: ' + this.parentType);
7219         
7220         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7221            cfg.cls += ' navbar-form';
7222            Roo.log(cfg);
7223         }
7224         
7225         return cfg;
7226         
7227     },
7228     /**
7229      * return the real input element.
7230      */
7231     inputEl: function ()
7232     {
7233         return this.el.select('input.form-control',true).first();
7234     },
7235     
7236     tooltipEl : function()
7237     {
7238         return this.inputEl();
7239     },
7240     
7241     setDisabled : function(v)
7242     {
7243         var i  = this.inputEl().dom;
7244         if (!v) {
7245             i.removeAttribute('disabled');
7246             return;
7247             
7248         }
7249         i.setAttribute('disabled','true');
7250     },
7251     initEvents : function()
7252     {
7253           
7254         this.inputEl().on("keydown" , this.fireKey,  this);
7255         this.inputEl().on("focus", this.onFocus,  this);
7256         this.inputEl().on("blur", this.onBlur,  this);
7257         
7258         this.inputEl().relayEvent('keyup', this);
7259
7260         // reference to original value for reset
7261         this.originalValue = this.getValue();
7262         //Roo.form.TextField.superclass.initEvents.call(this);
7263         if(this.validationEvent == 'keyup'){
7264             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7265             this.inputEl().on('keyup', this.filterValidation, this);
7266         }
7267         else if(this.validationEvent !== false){
7268             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7269         }
7270         
7271         if(this.selectOnFocus){
7272             this.on("focus", this.preFocus, this);
7273             
7274         }
7275         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7276             this.inputEl().on("keypress", this.filterKeys, this);
7277         }
7278        /* if(this.grow){
7279             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7280             this.el.on("click", this.autoSize,  this);
7281         }
7282         */
7283         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7284             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7285         }
7286         
7287         if (typeof(this.before) == 'object') {
7288             this.before.render(this.el.select('.roo-input-before',true).first());
7289         }
7290         if (typeof(this.after) == 'object') {
7291             this.after.render(this.el.select('.roo-input-after',true).first());
7292         }
7293         
7294         
7295     },
7296     filterValidation : function(e){
7297         if(!e.isNavKeyPress()){
7298             this.validationTask.delay(this.validationDelay);
7299         }
7300     },
7301      /**
7302      * Validates the field value
7303      * @return {Boolean} True if the value is valid, else false
7304      */
7305     validate : function(){
7306         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7307         if(this.disabled || this.validateValue(this.getRawValue())){
7308             this.clearInvalid();
7309             return true;
7310         }
7311         return false;
7312     },
7313     
7314     
7315     /**
7316      * Validates a value according to the field's validation rules and marks the field as invalid
7317      * if the validation fails
7318      * @param {Mixed} value The value to validate
7319      * @return {Boolean} True if the value is valid, else false
7320      */
7321     validateValue : function(value){
7322         if(value.length < 1)  { // if it's blank
7323              if(this.allowBlank){
7324                 this.clearInvalid();
7325                 return true;
7326              }else{
7327                 this.markInvalid(this.blankText);
7328                 return false;
7329              }
7330         }
7331         if(value.length < this.minLength){
7332             this.markInvalid(String.format(this.minLengthText, this.minLength));
7333             return false;
7334         }
7335         if(value.length > this.maxLength){
7336             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7337             return false;
7338         }
7339         if(this.vtype){
7340             var vt = Roo.form.VTypes;
7341             if(!vt[this.vtype](value, this)){
7342                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7343                 return false;
7344             }
7345         }
7346         if(typeof this.validator == "function"){
7347             var msg = this.validator(value);
7348             if(msg !== true){
7349                 this.markInvalid(msg);
7350                 return false;
7351             }
7352         }
7353         if(this.regex && !this.regex.test(value)){
7354             this.markInvalid(this.regexText);
7355             return false;
7356         }
7357         return true;
7358     },
7359
7360     
7361     
7362      // private
7363     fireKey : function(e){
7364         //Roo.log('field ' + e.getKey());
7365         if(e.isNavKeyPress()){
7366             this.fireEvent("specialkey", this, e);
7367         }
7368     },
7369     focus : function (selectText){
7370         if(this.rendered){
7371             this.inputEl().focus();
7372             if(selectText === true){
7373                 this.inputEl().dom.select();
7374             }
7375         }
7376         return this;
7377     } ,
7378     
7379     onFocus : function(){
7380         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7381            // this.el.addClass(this.focusClass);
7382         }
7383         if(!this.hasFocus){
7384             this.hasFocus = true;
7385             this.startValue = this.getValue();
7386             this.fireEvent("focus", this);
7387         }
7388     },
7389     
7390     beforeBlur : Roo.emptyFn,
7391
7392     
7393     // private
7394     onBlur : function(){
7395         this.beforeBlur();
7396         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7397             //this.el.removeClass(this.focusClass);
7398         }
7399         this.hasFocus = false;
7400         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7401             this.validate();
7402         }
7403         var v = this.getValue();
7404         if(String(v) !== String(this.startValue)){
7405             this.fireEvent('change', this, v, this.startValue);
7406         }
7407         this.fireEvent("blur", this);
7408     },
7409     
7410     /**
7411      * Resets the current field value to the originally loaded value and clears any validation messages
7412      */
7413     reset : function(){
7414         this.setValue(this.originalValue);
7415         this.clearInvalid();
7416     },
7417      /**
7418      * Returns the name of the field
7419      * @return {Mixed} name The name field
7420      */
7421     getName: function(){
7422         return this.name;
7423     },
7424      /**
7425      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7426      * @return {Mixed} value The field value
7427      */
7428     getValue : function(){
7429         
7430         var v = this.inputEl().getValue();
7431         
7432         return v;
7433     },
7434     /**
7435      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7436      * @return {Mixed} value The field value
7437      */
7438     getRawValue : function(){
7439         var v = this.inputEl().getValue();
7440         
7441         return v;
7442     },
7443     
7444     /**
7445      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7446      * @param {Mixed} value The value to set
7447      */
7448     setRawValue : function(v){
7449         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7450     },
7451     
7452     selectText : function(start, end){
7453         var v = this.getRawValue();
7454         if(v.length > 0){
7455             start = start === undefined ? 0 : start;
7456             end = end === undefined ? v.length : end;
7457             var d = this.inputEl().dom;
7458             if(d.setSelectionRange){
7459                 d.setSelectionRange(start, end);
7460             }else if(d.createTextRange){
7461                 var range = d.createTextRange();
7462                 range.moveStart("character", start);
7463                 range.moveEnd("character", v.length-end);
7464                 range.select();
7465             }
7466         }
7467     },
7468     
7469     /**
7470      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7471      * @param {Mixed} value The value to set
7472      */
7473     setValue : function(v){
7474         this.value = v;
7475         if(this.rendered){
7476             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7477             this.validate();
7478         }
7479     },
7480     
7481     /*
7482     processValue : function(value){
7483         if(this.stripCharsRe){
7484             var newValue = value.replace(this.stripCharsRe, '');
7485             if(newValue !== value){
7486                 this.setRawValue(newValue);
7487                 return newValue;
7488             }
7489         }
7490         return value;
7491     },
7492   */
7493     preFocus : function(){
7494         
7495         if(this.selectOnFocus){
7496             this.inputEl().dom.select();
7497         }
7498     },
7499     filterKeys : function(e){
7500         var k = e.getKey();
7501         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7502             return;
7503         }
7504         var c = e.getCharCode(), cc = String.fromCharCode(c);
7505         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7506             return;
7507         }
7508         if(!this.maskRe.test(cc)){
7509             e.stopEvent();
7510         }
7511     },
7512      /**
7513      * Clear any invalid styles/messages for this field
7514      */
7515     clearInvalid : function(){
7516         
7517         if(!this.el || this.preventMark){ // not rendered
7518             return;
7519         }
7520         this.el.removeClass(this.invalidClass);
7521         /*
7522         switch(this.msgTarget){
7523             case 'qtip':
7524                 this.el.dom.qtip = '';
7525                 break;
7526             case 'title':
7527                 this.el.dom.title = '';
7528                 break;
7529             case 'under':
7530                 if(this.errorEl){
7531                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7532                 }
7533                 break;
7534             case 'side':
7535                 if(this.errorIcon){
7536                     this.errorIcon.dom.qtip = '';
7537                     this.errorIcon.hide();
7538                     this.un('resize', this.alignErrorIcon, this);
7539                 }
7540                 break;
7541             default:
7542                 var t = Roo.getDom(this.msgTarget);
7543                 t.innerHTML = '';
7544                 t.style.display = 'none';
7545                 break;
7546         }
7547         */
7548         this.fireEvent('valid', this);
7549     },
7550      /**
7551      * Mark this field as invalid
7552      * @param {String} msg The validation message
7553      */
7554     markInvalid : function(msg){
7555         if(!this.el  || this.preventMark){ // not rendered
7556             return;
7557         }
7558         this.el.addClass(this.invalidClass);
7559         /*
7560         msg = msg || this.invalidText;
7561         switch(this.msgTarget){
7562             case 'qtip':
7563                 this.el.dom.qtip = msg;
7564                 this.el.dom.qclass = 'x-form-invalid-tip';
7565                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7566                     Roo.QuickTips.enable();
7567                 }
7568                 break;
7569             case 'title':
7570                 this.el.dom.title = msg;
7571                 break;
7572             case 'under':
7573                 if(!this.errorEl){
7574                     var elp = this.el.findParent('.x-form-element', 5, true);
7575                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7576                     this.errorEl.setWidth(elp.getWidth(true)-20);
7577                 }
7578                 this.errorEl.update(msg);
7579                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7580                 break;
7581             case 'side':
7582                 if(!this.errorIcon){
7583                     var elp = this.el.findParent('.x-form-element', 5, true);
7584                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7585                 }
7586                 this.alignErrorIcon();
7587                 this.errorIcon.dom.qtip = msg;
7588                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7589                 this.errorIcon.show();
7590                 this.on('resize', this.alignErrorIcon, this);
7591                 break;
7592             default:
7593                 var t = Roo.getDom(this.msgTarget);
7594                 t.innerHTML = msg;
7595                 t.style.display = this.msgDisplay;
7596                 break;
7597         }
7598         */
7599         this.fireEvent('invalid', this, msg);
7600     },
7601     // private
7602     SafariOnKeyDown : function(event)
7603     {
7604         // this is a workaround for a password hang bug on chrome/ webkit.
7605         
7606         var isSelectAll = false;
7607         
7608         if(this.inputEl().dom.selectionEnd > 0){
7609             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7610         }
7611         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7612             event.preventDefault();
7613             this.setValue('');
7614             return;
7615         }
7616         
7617         if(isSelectAll){ // backspace and delete key
7618             
7619             event.preventDefault();
7620             // this is very hacky as keydown always get's upper case.
7621             //
7622             var cc = String.fromCharCode(event.getCharCode());
7623             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7624             
7625         }
7626     },
7627     adjustWidth : function(tag, w){
7628         tag = tag.toLowerCase();
7629         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7630             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7631                 if(tag == 'input'){
7632                     return w + 2;
7633                 }
7634                 if(tag == 'textarea'){
7635                     return w-2;
7636                 }
7637             }else if(Roo.isOpera){
7638                 if(tag == 'input'){
7639                     return w + 2;
7640                 }
7641                 if(tag == 'textarea'){
7642                     return w-2;
7643                 }
7644             }
7645         }
7646         return w;
7647     }
7648     
7649 });
7650
7651  
7652 /*
7653  * - LGPL
7654  *
7655  * Input
7656  * 
7657  */
7658
7659 /**
7660  * @class Roo.bootstrap.TextArea
7661  * @extends Roo.bootstrap.Input
7662  * Bootstrap TextArea class
7663  * @cfg {Number} cols Specifies the visible width of a text area
7664  * @cfg {Number} rows Specifies the visible number of lines in a text area
7665  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7666  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7667  * @cfg {string} html text
7668  * 
7669  * @constructor
7670  * Create a new TextArea
7671  * @param {Object} config The config object
7672  */
7673
7674 Roo.bootstrap.TextArea = function(config){
7675     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7676    
7677 };
7678
7679 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7680      
7681     cols : false,
7682     rows : 5,
7683     readOnly : false,
7684     warp : 'soft',
7685     resize : false,
7686     value: false,
7687     html: false,
7688     
7689     getAutoCreate : function(){
7690         
7691         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7692         
7693         var id = Roo.id();
7694         
7695         var cfg = {};
7696         
7697         var input =  {
7698             tag: 'textarea',
7699             id : id,
7700             warp : this.warp,
7701             rows : this.rows,
7702             value : this.value || '',
7703             html: this.html || '',
7704             cls : 'form-control',
7705             placeholder : this.placeholder || '' 
7706             
7707         };
7708         
7709         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7710             input.maxLength = this.maxLength;
7711         }
7712         
7713         if(this.resize){
7714             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7715         }
7716         
7717         if(this.cols){
7718             input.cols = this.cols;
7719         }
7720         
7721         if (this.readOnly) {
7722             input.readonly = true;
7723         }
7724         
7725         if (this.name) {
7726             input.name = this.name;
7727         }
7728         
7729         if (this.size) {
7730             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7731         }
7732         
7733         var settings=this;
7734         ['xs','sm','md','lg'].map(function(size){
7735             if (settings[size]) {
7736                 cfg.cls += ' col-' + size + '-' + settings[size];
7737             }
7738         });
7739         
7740         var inputblock = input;
7741         
7742         if (this.before || this.after) {
7743             
7744             inputblock = {
7745                 cls : 'input-group',
7746                 cn :  [] 
7747             };
7748             if (this.before) {
7749                 inputblock.cn.push({
7750                     tag :'span',
7751                     cls : 'input-group-addon',
7752                     html : this.before
7753                 });
7754             }
7755             inputblock.cn.push(input);
7756             if (this.after) {
7757                 inputblock.cn.push({
7758                     tag :'span',
7759                     cls : 'input-group-addon',
7760                     html : this.after
7761                 });
7762             }
7763             
7764         }
7765         
7766         if (align ==='left' && this.fieldLabel.length) {
7767                 Roo.log("left and has label");
7768                 cfg.cn = [
7769                     
7770                     {
7771                         tag: 'label',
7772                         'for' :  id,
7773                         cls : 'control-label col-sm-' + this.labelWidth,
7774                         html : this.fieldLabel
7775                         
7776                     },
7777                     {
7778                         cls : "col-sm-" + (12 - this.labelWidth), 
7779                         cn: [
7780                             inputblock
7781                         ]
7782                     }
7783                     
7784                 ];
7785         } else if ( this.fieldLabel.length) {
7786                 Roo.log(" label");
7787                  cfg.cn = [
7788                    
7789                     {
7790                         tag: 'label',
7791                         //cls : 'input-group-addon',
7792                         html : this.fieldLabel
7793                         
7794                     },
7795                     
7796                     inputblock
7797                     
7798                 ];
7799
7800         } else {
7801             
7802                    Roo.log(" no label && no align");
7803                 cfg.cn = [
7804                     
7805                         inputblock
7806                     
7807                 ];
7808                 
7809                 
7810         }
7811         
7812         if (this.disabled) {
7813             input.disabled=true;
7814         }
7815         
7816         return cfg;
7817         
7818     },
7819     /**
7820      * return the real textarea element.
7821      */
7822     inputEl: function ()
7823     {
7824         return this.el.select('textarea.form-control',true).first();
7825     }
7826 });
7827
7828  
7829 /*
7830  * - LGPL
7831  *
7832  * trigger field - base class for combo..
7833  * 
7834  */
7835  
7836 /**
7837  * @class Roo.bootstrap.TriggerField
7838  * @extends Roo.bootstrap.Input
7839  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7840  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7841  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7842  * for which you can provide a custom implementation.  For example:
7843  * <pre><code>
7844 var trigger = new Roo.bootstrap.TriggerField();
7845 trigger.onTriggerClick = myTriggerFn;
7846 trigger.applyTo('my-field');
7847 </code></pre>
7848  *
7849  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7850  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7851  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7852  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7853  * @constructor
7854  * Create a new TriggerField.
7855  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7856  * to the base TextField)
7857  */
7858 Roo.bootstrap.TriggerField = function(config){
7859     this.mimicing = false;
7860     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7861 };
7862
7863 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7864     /**
7865      * @cfg {String} triggerClass A CSS class to apply to the trigger
7866      */
7867      /**
7868      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7869      */
7870     hideTrigger:false,
7871
7872     /** @cfg {Boolean} grow @hide */
7873     /** @cfg {Number} growMin @hide */
7874     /** @cfg {Number} growMax @hide */
7875
7876     /**
7877      * @hide 
7878      * @method
7879      */
7880     autoSize: Roo.emptyFn,
7881     // private
7882     monitorTab : true,
7883     // private
7884     deferHeight : true,
7885
7886     
7887     actionMode : 'wrap',
7888     
7889     
7890     
7891     getAutoCreate : function(){
7892        
7893         var align = this.labelAlign || this.parentLabelAlign();
7894         
7895         var id = Roo.id();
7896         
7897         var cfg = {
7898             cls: 'form-group' //input-group
7899         };
7900         
7901         
7902         var input =  {
7903             tag: 'input',
7904             id : id,
7905             type : this.inputType,
7906             cls : 'form-control',
7907             autocomplete: 'off',
7908             placeholder : this.placeholder || '' 
7909             
7910         };
7911         if (this.name) {
7912             input.name = this.name;
7913         }
7914         if (this.size) {
7915             input.cls += ' input-' + this.size;
7916         }
7917         
7918         if (this.disabled) {
7919             input.disabled=true;
7920         }
7921         
7922         var inputblock = input;
7923         
7924         if (this.before || this.after) {
7925             
7926             inputblock = {
7927                 cls : 'input-group',
7928                 cn :  [] 
7929             };
7930             if (this.before) {
7931                 inputblock.cn.push({
7932                     tag :'span',
7933                     cls : 'input-group-addon',
7934                     html : this.before
7935                 });
7936             }
7937             inputblock.cn.push(input);
7938             if (this.after) {
7939                 inputblock.cn.push({
7940                     tag :'span',
7941                     cls : 'input-group-addon',
7942                     html : this.after
7943                 });
7944             }
7945             
7946         };
7947         
7948         var box = {
7949             tag: 'div',
7950             cn: [
7951                 {
7952                     tag: 'input',
7953                     type : 'hidden',
7954                     cls: 'form-hidden-field'
7955                 },
7956                 inputblock
7957             ]
7958             
7959         };
7960         
7961         if(this.multiple){
7962             Roo.log('multiple');
7963             
7964             box = {
7965                 tag: 'div',
7966                 cn: [
7967                     {
7968                         tag: 'input',
7969                         type : 'hidden',
7970                         cls: 'form-hidden-field'
7971                     },
7972                     {
7973                         tag: 'ul',
7974                         cls: 'select2-choices',
7975                         cn:[
7976                             {
7977                                 tag: 'li',
7978                                 cls: 'select2-search-field',
7979                                 cn: [
7980
7981                                     inputblock
7982                                 ]
7983                             }
7984                         ]
7985                     }
7986                 ]
7987             }
7988         };
7989         
7990         var combobox = {
7991             cls: 'select2-container input-group',
7992             cn: [
7993                 box
7994 //                {
7995 //                    tag: 'ul',
7996 //                    cls: 'typeahead typeahead-long dropdown-menu',
7997 //                    style: 'display:none'
7998 //                }
7999             ]
8000         };
8001         
8002         if(!this.multiple && this.showToggleBtn){
8003             combobox.cn.push({
8004                 tag :'span',
8005                 cls : 'input-group-addon btn dropdown-toggle',
8006                 cn : [
8007                     {
8008                         tag: 'span',
8009                         cls: 'caret'
8010                     },
8011                     {
8012                         tag: 'span',
8013                         cls: 'combobox-clear',
8014                         cn  : [
8015                             {
8016                                 tag : 'i',
8017                                 cls: 'icon-remove'
8018                             }
8019                         ]
8020                     }
8021                 ]
8022
8023             })
8024         }
8025         
8026         if(this.multiple){
8027             combobox.cls += ' select2-container-multi';
8028         }
8029         
8030         if (align ==='left' && this.fieldLabel.length) {
8031             
8032                 Roo.log("left and has label");
8033                 cfg.cn = [
8034                     
8035                     {
8036                         tag: 'label',
8037                         'for' :  id,
8038                         cls : 'control-label col-sm-' + this.labelWidth,
8039                         html : this.fieldLabel
8040                         
8041                     },
8042                     {
8043                         cls : "col-sm-" + (12 - this.labelWidth), 
8044                         cn: [
8045                             combobox
8046                         ]
8047                     }
8048                     
8049                 ];
8050         } else if ( this.fieldLabel.length) {
8051                 Roo.log(" label");
8052                  cfg.cn = [
8053                    
8054                     {
8055                         tag: 'label',
8056                         //cls : 'input-group-addon',
8057                         html : this.fieldLabel
8058                         
8059                     },
8060                     
8061                     combobox
8062                     
8063                 ];
8064
8065         } else {
8066             
8067                 Roo.log(" no label && no align");
8068                 cfg = combobox
8069                      
8070                 
8071         }
8072          
8073         var settings=this;
8074         ['xs','sm','md','lg'].map(function(size){
8075             if (settings[size]) {
8076                 cfg.cls += ' col-' + size + '-' + settings[size];
8077             }
8078         });
8079         
8080         return cfg;
8081         
8082     },
8083     
8084     
8085     
8086     // private
8087     onResize : function(w, h){
8088 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8089 //        if(typeof w == 'number'){
8090 //            var x = w - this.trigger.getWidth();
8091 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8092 //            this.trigger.setStyle('left', x+'px');
8093 //        }
8094     },
8095
8096     // private
8097     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8098
8099     // private
8100     getResizeEl : function(){
8101         return this.inputEl();
8102     },
8103
8104     // private
8105     getPositionEl : function(){
8106         return this.inputEl();
8107     },
8108
8109     // private
8110     alignErrorIcon : function(){
8111         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8112     },
8113
8114     // private
8115     initEvents : function(){
8116         
8117         this.createList();
8118         
8119         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8120         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8121         if(!this.multiple && this.showToggleBtn){
8122             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8123             if(this.hideTrigger){
8124                 this.trigger.setDisplayed(false);
8125             }
8126             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8127         }
8128         
8129         if(this.multiple){
8130             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8131         }
8132         
8133         //this.trigger.addClassOnOver('x-form-trigger-over');
8134         //this.trigger.addClassOnClick('x-form-trigger-click');
8135         
8136         //if(!this.width){
8137         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8138         //}
8139     },
8140     
8141     createList : function()
8142     {
8143         this.list = Roo.get(document.body).createChild({
8144             tag: 'ul',
8145             cls: 'typeahead typeahead-long dropdown-menu',
8146             style: 'display:none'
8147         });
8148         
8149         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8150         
8151     },
8152
8153     // private
8154     initTrigger : function(){
8155        
8156     },
8157
8158     // private
8159     onDestroy : function(){
8160         if(this.trigger){
8161             this.trigger.removeAllListeners();
8162           //  this.trigger.remove();
8163         }
8164         //if(this.wrap){
8165         //    this.wrap.remove();
8166         //}
8167         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8168     },
8169
8170     // private
8171     onFocus : function(){
8172         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8173         /*
8174         if(!this.mimicing){
8175             this.wrap.addClass('x-trigger-wrap-focus');
8176             this.mimicing = true;
8177             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8178             if(this.monitorTab){
8179                 this.el.on("keydown", this.checkTab, this);
8180             }
8181         }
8182         */
8183     },
8184
8185     // private
8186     checkTab : function(e){
8187         if(e.getKey() == e.TAB){
8188             this.triggerBlur();
8189         }
8190     },
8191
8192     // private
8193     onBlur : function(){
8194         // do nothing
8195     },
8196
8197     // private
8198     mimicBlur : function(e, t){
8199         /*
8200         if(!this.wrap.contains(t) && this.validateBlur()){
8201             this.triggerBlur();
8202         }
8203         */
8204     },
8205
8206     // private
8207     triggerBlur : function(){
8208         this.mimicing = false;
8209         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8210         if(this.monitorTab){
8211             this.el.un("keydown", this.checkTab, this);
8212         }
8213         //this.wrap.removeClass('x-trigger-wrap-focus');
8214         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8215     },
8216
8217     // private
8218     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8219     validateBlur : function(e, t){
8220         return true;
8221     },
8222
8223     // private
8224     onDisable : function(){
8225         this.inputEl().dom.disabled = true;
8226         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8227         //if(this.wrap){
8228         //    this.wrap.addClass('x-item-disabled');
8229         //}
8230     },
8231
8232     // private
8233     onEnable : function(){
8234         this.inputEl().dom.disabled = false;
8235         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8236         //if(this.wrap){
8237         //    this.el.removeClass('x-item-disabled');
8238         //}
8239     },
8240
8241     // private
8242     onShow : function(){
8243         var ae = this.getActionEl();
8244         
8245         if(ae){
8246             ae.dom.style.display = '';
8247             ae.dom.style.visibility = 'visible';
8248         }
8249     },
8250
8251     // private
8252     
8253     onHide : function(){
8254         var ae = this.getActionEl();
8255         ae.dom.style.display = 'none';
8256     },
8257
8258     /**
8259      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8260      * by an implementing function.
8261      * @method
8262      * @param {EventObject} e
8263      */
8264     onTriggerClick : Roo.emptyFn
8265 });
8266  /*
8267  * Based on:
8268  * Ext JS Library 1.1.1
8269  * Copyright(c) 2006-2007, Ext JS, LLC.
8270  *
8271  * Originally Released Under LGPL - original licence link has changed is not relivant.
8272  *
8273  * Fork - LGPL
8274  * <script type="text/javascript">
8275  */
8276
8277
8278 /**
8279  * @class Roo.data.SortTypes
8280  * @singleton
8281  * Defines the default sorting (casting?) comparison functions used when sorting data.
8282  */
8283 Roo.data.SortTypes = {
8284     /**
8285      * Default sort that does nothing
8286      * @param {Mixed} s The value being converted
8287      * @return {Mixed} The comparison value
8288      */
8289     none : function(s){
8290         return s;
8291     },
8292     
8293     /**
8294      * The regular expression used to strip tags
8295      * @type {RegExp}
8296      * @property
8297      */
8298     stripTagsRE : /<\/?[^>]+>/gi,
8299     
8300     /**
8301      * Strips all HTML tags to sort on text only
8302      * @param {Mixed} s The value being converted
8303      * @return {String} The comparison value
8304      */
8305     asText : function(s){
8306         return String(s).replace(this.stripTagsRE, "");
8307     },
8308     
8309     /**
8310      * Strips all HTML tags to sort on text only - Case insensitive
8311      * @param {Mixed} s The value being converted
8312      * @return {String} The comparison value
8313      */
8314     asUCText : function(s){
8315         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8316     },
8317     
8318     /**
8319      * Case insensitive string
8320      * @param {Mixed} s The value being converted
8321      * @return {String} The comparison value
8322      */
8323     asUCString : function(s) {
8324         return String(s).toUpperCase();
8325     },
8326     
8327     /**
8328      * Date sorting
8329      * @param {Mixed} s The value being converted
8330      * @return {Number} The comparison value
8331      */
8332     asDate : function(s) {
8333         if(!s){
8334             return 0;
8335         }
8336         if(s instanceof Date){
8337             return s.getTime();
8338         }
8339         return Date.parse(String(s));
8340     },
8341     
8342     /**
8343      * Float sorting
8344      * @param {Mixed} s The value being converted
8345      * @return {Float} The comparison value
8346      */
8347     asFloat : function(s) {
8348         var val = parseFloat(String(s).replace(/,/g, ""));
8349         if(isNaN(val)) val = 0;
8350         return val;
8351     },
8352     
8353     /**
8354      * Integer sorting
8355      * @param {Mixed} s The value being converted
8356      * @return {Number} The comparison value
8357      */
8358     asInt : function(s) {
8359         var val = parseInt(String(s).replace(/,/g, ""));
8360         if(isNaN(val)) val = 0;
8361         return val;
8362     }
8363 };/*
8364  * Based on:
8365  * Ext JS Library 1.1.1
8366  * Copyright(c) 2006-2007, Ext JS, LLC.
8367  *
8368  * Originally Released Under LGPL - original licence link has changed is not relivant.
8369  *
8370  * Fork - LGPL
8371  * <script type="text/javascript">
8372  */
8373
8374 /**
8375 * @class Roo.data.Record
8376  * Instances of this class encapsulate both record <em>definition</em> information, and record
8377  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8378  * to access Records cached in an {@link Roo.data.Store} object.<br>
8379  * <p>
8380  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8381  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8382  * objects.<br>
8383  * <p>
8384  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8385  * @constructor
8386  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8387  * {@link #create}. The parameters are the same.
8388  * @param {Array} data An associative Array of data values keyed by the field name.
8389  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8390  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8391  * not specified an integer id is generated.
8392  */
8393 Roo.data.Record = function(data, id){
8394     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8395     this.data = data;
8396 };
8397
8398 /**
8399  * Generate a constructor for a specific record layout.
8400  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8401  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8402  * Each field definition object may contain the following properties: <ul>
8403  * <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,
8404  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8405  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8406  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8407  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8408  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8409  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8410  * this may be omitted.</p></li>
8411  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8412  * <ul><li>auto (Default, implies no conversion)</li>
8413  * <li>string</li>
8414  * <li>int</li>
8415  * <li>float</li>
8416  * <li>boolean</li>
8417  * <li>date</li></ul></p></li>
8418  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8419  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8420  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8421  * by the Reader into an object that will be stored in the Record. It is passed the
8422  * following parameters:<ul>
8423  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8424  * </ul></p></li>
8425  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8426  * </ul>
8427  * <br>usage:<br><pre><code>
8428 var TopicRecord = Roo.data.Record.create(
8429     {name: 'title', mapping: 'topic_title'},
8430     {name: 'author', mapping: 'username'},
8431     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8432     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8433     {name: 'lastPoster', mapping: 'user2'},
8434     {name: 'excerpt', mapping: 'post_text'}
8435 );
8436
8437 var myNewRecord = new TopicRecord({
8438     title: 'Do my job please',
8439     author: 'noobie',
8440     totalPosts: 1,
8441     lastPost: new Date(),
8442     lastPoster: 'Animal',
8443     excerpt: 'No way dude!'
8444 });
8445 myStore.add(myNewRecord);
8446 </code></pre>
8447  * @method create
8448  * @static
8449  */
8450 Roo.data.Record.create = function(o){
8451     var f = function(){
8452         f.superclass.constructor.apply(this, arguments);
8453     };
8454     Roo.extend(f, Roo.data.Record);
8455     var p = f.prototype;
8456     p.fields = new Roo.util.MixedCollection(false, function(field){
8457         return field.name;
8458     });
8459     for(var i = 0, len = o.length; i < len; i++){
8460         p.fields.add(new Roo.data.Field(o[i]));
8461     }
8462     f.getField = function(name){
8463         return p.fields.get(name);  
8464     };
8465     return f;
8466 };
8467
8468 Roo.data.Record.AUTO_ID = 1000;
8469 Roo.data.Record.EDIT = 'edit';
8470 Roo.data.Record.REJECT = 'reject';
8471 Roo.data.Record.COMMIT = 'commit';
8472
8473 Roo.data.Record.prototype = {
8474     /**
8475      * Readonly flag - true if this record has been modified.
8476      * @type Boolean
8477      */
8478     dirty : false,
8479     editing : false,
8480     error: null,
8481     modified: null,
8482
8483     // private
8484     join : function(store){
8485         this.store = store;
8486     },
8487
8488     /**
8489      * Set the named field to the specified value.
8490      * @param {String} name The name of the field to set.
8491      * @param {Object} value The value to set the field to.
8492      */
8493     set : function(name, value){
8494         if(this.data[name] == value){
8495             return;
8496         }
8497         this.dirty = true;
8498         if(!this.modified){
8499             this.modified = {};
8500         }
8501         if(typeof this.modified[name] == 'undefined'){
8502             this.modified[name] = this.data[name];
8503         }
8504         this.data[name] = value;
8505         if(!this.editing && this.store){
8506             this.store.afterEdit(this);
8507         }       
8508     },
8509
8510     /**
8511      * Get the value of the named field.
8512      * @param {String} name The name of the field to get the value of.
8513      * @return {Object} The value of the field.
8514      */
8515     get : function(name){
8516         return this.data[name]; 
8517     },
8518
8519     // private
8520     beginEdit : function(){
8521         this.editing = true;
8522         this.modified = {}; 
8523     },
8524
8525     // private
8526     cancelEdit : function(){
8527         this.editing = false;
8528         delete this.modified;
8529     },
8530
8531     // private
8532     endEdit : function(){
8533         this.editing = false;
8534         if(this.dirty && this.store){
8535             this.store.afterEdit(this);
8536         }
8537     },
8538
8539     /**
8540      * Usually called by the {@link Roo.data.Store} which owns the Record.
8541      * Rejects all changes made to the Record since either creation, or the last commit operation.
8542      * Modified fields are reverted to their original values.
8543      * <p>
8544      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8545      * of reject operations.
8546      */
8547     reject : function(){
8548         var m = this.modified;
8549         for(var n in m){
8550             if(typeof m[n] != "function"){
8551                 this.data[n] = m[n];
8552             }
8553         }
8554         this.dirty = false;
8555         delete this.modified;
8556         this.editing = false;
8557         if(this.store){
8558             this.store.afterReject(this);
8559         }
8560     },
8561
8562     /**
8563      * Usually called by the {@link Roo.data.Store} which owns the Record.
8564      * Commits all changes made to the Record since either creation, or the last commit operation.
8565      * <p>
8566      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8567      * of commit operations.
8568      */
8569     commit : function(){
8570         this.dirty = false;
8571         delete this.modified;
8572         this.editing = false;
8573         if(this.store){
8574             this.store.afterCommit(this);
8575         }
8576     },
8577
8578     // private
8579     hasError : function(){
8580         return this.error != null;
8581     },
8582
8583     // private
8584     clearError : function(){
8585         this.error = null;
8586     },
8587
8588     /**
8589      * Creates a copy of this record.
8590      * @param {String} id (optional) A new record id if you don't want to use this record's id
8591      * @return {Record}
8592      */
8593     copy : function(newId) {
8594         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8595     }
8596 };/*
8597  * Based on:
8598  * Ext JS Library 1.1.1
8599  * Copyright(c) 2006-2007, Ext JS, LLC.
8600  *
8601  * Originally Released Under LGPL - original licence link has changed is not relivant.
8602  *
8603  * Fork - LGPL
8604  * <script type="text/javascript">
8605  */
8606
8607
8608
8609 /**
8610  * @class Roo.data.Store
8611  * @extends Roo.util.Observable
8612  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8613  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8614  * <p>
8615  * 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
8616  * has no knowledge of the format of the data returned by the Proxy.<br>
8617  * <p>
8618  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8619  * instances from the data object. These records are cached and made available through accessor functions.
8620  * @constructor
8621  * Creates a new Store.
8622  * @param {Object} config A config object containing the objects needed for the Store to access data,
8623  * and read the data into Records.
8624  */
8625 Roo.data.Store = function(config){
8626     this.data = new Roo.util.MixedCollection(false);
8627     this.data.getKey = function(o){
8628         return o.id;
8629     };
8630     this.baseParams = {};
8631     // private
8632     this.paramNames = {
8633         "start" : "start",
8634         "limit" : "limit",
8635         "sort" : "sort",
8636         "dir" : "dir",
8637         "multisort" : "_multisort"
8638     };
8639
8640     if(config && config.data){
8641         this.inlineData = config.data;
8642         delete config.data;
8643     }
8644
8645     Roo.apply(this, config);
8646     
8647     if(this.reader){ // reader passed
8648         this.reader = Roo.factory(this.reader, Roo.data);
8649         this.reader.xmodule = this.xmodule || false;
8650         if(!this.recordType){
8651             this.recordType = this.reader.recordType;
8652         }
8653         if(this.reader.onMetaChange){
8654             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8655         }
8656     }
8657
8658     if(this.recordType){
8659         this.fields = this.recordType.prototype.fields;
8660     }
8661     this.modified = [];
8662
8663     this.addEvents({
8664         /**
8665          * @event datachanged
8666          * Fires when the data cache has changed, and a widget which is using this Store
8667          * as a Record cache should refresh its view.
8668          * @param {Store} this
8669          */
8670         datachanged : true,
8671         /**
8672          * @event metachange
8673          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8674          * @param {Store} this
8675          * @param {Object} meta The JSON metadata
8676          */
8677         metachange : true,
8678         /**
8679          * @event add
8680          * Fires when Records have been added to the Store
8681          * @param {Store} this
8682          * @param {Roo.data.Record[]} records The array of Records added
8683          * @param {Number} index The index at which the record(s) were added
8684          */
8685         add : true,
8686         /**
8687          * @event remove
8688          * Fires when a Record has been removed from the Store
8689          * @param {Store} this
8690          * @param {Roo.data.Record} record The Record that was removed
8691          * @param {Number} index The index at which the record was removed
8692          */
8693         remove : true,
8694         /**
8695          * @event update
8696          * Fires when a Record has been updated
8697          * @param {Store} this
8698          * @param {Roo.data.Record} record The Record that was updated
8699          * @param {String} operation The update operation being performed.  Value may be one of:
8700          * <pre><code>
8701  Roo.data.Record.EDIT
8702  Roo.data.Record.REJECT
8703  Roo.data.Record.COMMIT
8704          * </code></pre>
8705          */
8706         update : true,
8707         /**
8708          * @event clear
8709          * Fires when the data cache has been cleared.
8710          * @param {Store} this
8711          */
8712         clear : true,
8713         /**
8714          * @event beforeload
8715          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8716          * the load action will be canceled.
8717          * @param {Store} this
8718          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8719          */
8720         beforeload : true,
8721         /**
8722          * @event beforeloadadd
8723          * Fires after a new set of Records has been loaded.
8724          * @param {Store} this
8725          * @param {Roo.data.Record[]} records The Records that were loaded
8726          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8727          */
8728         beforeloadadd : true,
8729         /**
8730          * @event load
8731          * Fires after a new set of Records has been loaded, before they are added to the store.
8732          * @param {Store} this
8733          * @param {Roo.data.Record[]} records The Records that were loaded
8734          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8735          * @params {Object} return from reader
8736          */
8737         load : true,
8738         /**
8739          * @event loadexception
8740          * Fires if an exception occurs in the Proxy during loading.
8741          * Called with the signature of the Proxy's "loadexception" event.
8742          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8743          * 
8744          * @param {Proxy} 
8745          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8746          * @param {Object} load options 
8747          * @param {Object} jsonData from your request (normally this contains the Exception)
8748          */
8749         loadexception : true
8750     });
8751     
8752     if(this.proxy){
8753         this.proxy = Roo.factory(this.proxy, Roo.data);
8754         this.proxy.xmodule = this.xmodule || false;
8755         this.relayEvents(this.proxy,  ["loadexception"]);
8756     }
8757     this.sortToggle = {};
8758     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8759
8760     Roo.data.Store.superclass.constructor.call(this);
8761
8762     if(this.inlineData){
8763         this.loadData(this.inlineData);
8764         delete this.inlineData;
8765     }
8766 };
8767
8768 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8769      /**
8770     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8771     * without a remote query - used by combo/forms at present.
8772     */
8773     
8774     /**
8775     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8776     */
8777     /**
8778     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8779     */
8780     /**
8781     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8782     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8783     */
8784     /**
8785     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8786     * on any HTTP request
8787     */
8788     /**
8789     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8790     */
8791     /**
8792     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8793     */
8794     multiSort: false,
8795     /**
8796     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8797     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8798     */
8799     remoteSort : false,
8800
8801     /**
8802     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8803      * loaded or when a record is removed. (defaults to false).
8804     */
8805     pruneModifiedRecords : false,
8806
8807     // private
8808     lastOptions : null,
8809
8810     /**
8811      * Add Records to the Store and fires the add event.
8812      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8813      */
8814     add : function(records){
8815         records = [].concat(records);
8816         for(var i = 0, len = records.length; i < len; i++){
8817             records[i].join(this);
8818         }
8819         var index = this.data.length;
8820         this.data.addAll(records);
8821         this.fireEvent("add", this, records, index);
8822     },
8823
8824     /**
8825      * Remove a Record from the Store and fires the remove event.
8826      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8827      */
8828     remove : function(record){
8829         var index = this.data.indexOf(record);
8830         this.data.removeAt(index);
8831         if(this.pruneModifiedRecords){
8832             this.modified.remove(record);
8833         }
8834         this.fireEvent("remove", this, record, index);
8835     },
8836
8837     /**
8838      * Remove all Records from the Store and fires the clear event.
8839      */
8840     removeAll : function(){
8841         this.data.clear();
8842         if(this.pruneModifiedRecords){
8843             this.modified = [];
8844         }
8845         this.fireEvent("clear", this);
8846     },
8847
8848     /**
8849      * Inserts Records to the Store at the given index and fires the add event.
8850      * @param {Number} index The start index at which to insert the passed Records.
8851      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8852      */
8853     insert : function(index, records){
8854         records = [].concat(records);
8855         for(var i = 0, len = records.length; i < len; i++){
8856             this.data.insert(index, records[i]);
8857             records[i].join(this);
8858         }
8859         this.fireEvent("add", this, records, index);
8860     },
8861
8862     /**
8863      * Get the index within the cache of the passed Record.
8864      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8865      * @return {Number} The index of the passed Record. Returns -1 if not found.
8866      */
8867     indexOf : function(record){
8868         return this.data.indexOf(record);
8869     },
8870
8871     /**
8872      * Get the index within the cache of the Record with the passed id.
8873      * @param {String} id The id of the Record to find.
8874      * @return {Number} The index of the Record. Returns -1 if not found.
8875      */
8876     indexOfId : function(id){
8877         return this.data.indexOfKey(id);
8878     },
8879
8880     /**
8881      * Get the Record with the specified id.
8882      * @param {String} id The id of the Record to find.
8883      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8884      */
8885     getById : function(id){
8886         return this.data.key(id);
8887     },
8888
8889     /**
8890      * Get the Record at the specified index.
8891      * @param {Number} index The index of the Record to find.
8892      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8893      */
8894     getAt : function(index){
8895         return this.data.itemAt(index);
8896     },
8897
8898     /**
8899      * Returns a range of Records between specified indices.
8900      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8901      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8902      * @return {Roo.data.Record[]} An array of Records
8903      */
8904     getRange : function(start, end){
8905         return this.data.getRange(start, end);
8906     },
8907
8908     // private
8909     storeOptions : function(o){
8910         o = Roo.apply({}, o);
8911         delete o.callback;
8912         delete o.scope;
8913         this.lastOptions = o;
8914     },
8915
8916     /**
8917      * Loads the Record cache from the configured Proxy using the configured Reader.
8918      * <p>
8919      * If using remote paging, then the first load call must specify the <em>start</em>
8920      * and <em>limit</em> properties in the options.params property to establish the initial
8921      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8922      * <p>
8923      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8924      * and this call will return before the new data has been loaded. Perform any post-processing
8925      * in a callback function, or in a "load" event handler.</strong>
8926      * <p>
8927      * @param {Object} options An object containing properties which control loading options:<ul>
8928      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8929      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8930      * passed the following arguments:<ul>
8931      * <li>r : Roo.data.Record[]</li>
8932      * <li>options: Options object from the load call</li>
8933      * <li>success: Boolean success indicator</li></ul></li>
8934      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8935      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8936      * </ul>
8937      */
8938     load : function(options){
8939         options = options || {};
8940         if(this.fireEvent("beforeload", this, options) !== false){
8941             this.storeOptions(options);
8942             var p = Roo.apply(options.params || {}, this.baseParams);
8943             // if meta was not loaded from remote source.. try requesting it.
8944             if (!this.reader.metaFromRemote) {
8945                 p._requestMeta = 1;
8946             }
8947             if(this.sortInfo && this.remoteSort){
8948                 var pn = this.paramNames;
8949                 p[pn["sort"]] = this.sortInfo.field;
8950                 p[pn["dir"]] = this.sortInfo.direction;
8951             }
8952             if (this.multiSort) {
8953                 var pn = this.paramNames;
8954                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8955             }
8956             
8957             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8958         }
8959     },
8960
8961     /**
8962      * Reloads the Record cache from the configured Proxy using the configured Reader and
8963      * the options from the last load operation performed.
8964      * @param {Object} options (optional) An object containing properties which may override the options
8965      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8966      * the most recently used options are reused).
8967      */
8968     reload : function(options){
8969         this.load(Roo.applyIf(options||{}, this.lastOptions));
8970     },
8971
8972     // private
8973     // Called as a callback by the Reader during a load operation.
8974     loadRecords : function(o, options, success){
8975         if(!o || success === false){
8976             if(success !== false){
8977                 this.fireEvent("load", this, [], options, o);
8978             }
8979             if(options.callback){
8980                 options.callback.call(options.scope || this, [], options, false);
8981             }
8982             return;
8983         }
8984         // if data returned failure - throw an exception.
8985         if (o.success === false) {
8986             // show a message if no listener is registered.
8987             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8988                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8989             }
8990             // loadmask wil be hooked into this..
8991             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8992             return;
8993         }
8994         var r = o.records, t = o.totalRecords || r.length;
8995         
8996         this.fireEvent("beforeloadadd", this, r, options, o);
8997         
8998         if(!options || options.add !== true){
8999             if(this.pruneModifiedRecords){
9000                 this.modified = [];
9001             }
9002             for(var i = 0, len = r.length; i < len; i++){
9003                 r[i].join(this);
9004             }
9005             if(this.snapshot){
9006                 this.data = this.snapshot;
9007                 delete this.snapshot;
9008             }
9009             this.data.clear();
9010             this.data.addAll(r);
9011             this.totalLength = t;
9012             this.applySort();
9013             this.fireEvent("datachanged", this);
9014         }else{
9015             this.totalLength = Math.max(t, this.data.length+r.length);
9016             this.add(r);
9017         }
9018         this.fireEvent("load", this, r, options, o);
9019         if(options.callback){
9020             options.callback.call(options.scope || this, r, options, true);
9021         }
9022     },
9023
9024
9025     /**
9026      * Loads data from a passed data block. A Reader which understands the format of the data
9027      * must have been configured in the constructor.
9028      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9029      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9030      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9031      */
9032     loadData : function(o, append){
9033         var r = this.reader.readRecords(o);
9034         this.loadRecords(r, {add: append}, true);
9035     },
9036
9037     /**
9038      * Gets the number of cached records.
9039      * <p>
9040      * <em>If using paging, this may not be the total size of the dataset. If the data object
9041      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9042      * the data set size</em>
9043      */
9044     getCount : function(){
9045         return this.data.length || 0;
9046     },
9047
9048     /**
9049      * Gets the total number of records in the dataset as returned by the server.
9050      * <p>
9051      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9052      * the dataset size</em>
9053      */
9054     getTotalCount : function(){
9055         return this.totalLength || 0;
9056     },
9057
9058     /**
9059      * Returns the sort state of the Store as an object with two properties:
9060      * <pre><code>
9061  field {String} The name of the field by which the Records are sorted
9062  direction {String} The sort order, "ASC" or "DESC"
9063      * </code></pre>
9064      */
9065     getSortState : function(){
9066         return this.sortInfo;
9067     },
9068
9069     // private
9070     applySort : function(){
9071         if(this.sortInfo && !this.remoteSort){
9072             var s = this.sortInfo, f = s.field;
9073             var st = this.fields.get(f).sortType;
9074             var fn = function(r1, r2){
9075                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9076                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9077             };
9078             this.data.sort(s.direction, fn);
9079             if(this.snapshot && this.snapshot != this.data){
9080                 this.snapshot.sort(s.direction, fn);
9081             }
9082         }
9083     },
9084
9085     /**
9086      * Sets the default sort column and order to be used by the next load operation.
9087      * @param {String} fieldName The name of the field to sort by.
9088      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9089      */
9090     setDefaultSort : function(field, dir){
9091         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9092     },
9093
9094     /**
9095      * Sort the Records.
9096      * If remote sorting is used, the sort is performed on the server, and the cache is
9097      * reloaded. If local sorting is used, the cache is sorted internally.
9098      * @param {String} fieldName The name of the field to sort by.
9099      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9100      */
9101     sort : function(fieldName, dir){
9102         var f = this.fields.get(fieldName);
9103         if(!dir){
9104             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9105             
9106             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9107                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9108             }else{
9109                 dir = f.sortDir;
9110             }
9111         }
9112         this.sortToggle[f.name] = dir;
9113         this.sortInfo = {field: f.name, direction: dir};
9114         if(!this.remoteSort){
9115             this.applySort();
9116             this.fireEvent("datachanged", this);
9117         }else{
9118             this.load(this.lastOptions);
9119         }
9120     },
9121
9122     /**
9123      * Calls the specified function for each of the Records in the cache.
9124      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9125      * Returning <em>false</em> aborts and exits the iteration.
9126      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9127      */
9128     each : function(fn, scope){
9129         this.data.each(fn, scope);
9130     },
9131
9132     /**
9133      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9134      * (e.g., during paging).
9135      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9136      */
9137     getModifiedRecords : function(){
9138         return this.modified;
9139     },
9140
9141     // private
9142     createFilterFn : function(property, value, anyMatch){
9143         if(!value.exec){ // not a regex
9144             value = String(value);
9145             if(value.length == 0){
9146                 return false;
9147             }
9148             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9149         }
9150         return function(r){
9151             return value.test(r.data[property]);
9152         };
9153     },
9154
9155     /**
9156      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9157      * @param {String} property A field on your records
9158      * @param {Number} start The record index to start at (defaults to 0)
9159      * @param {Number} end The last record index to include (defaults to length - 1)
9160      * @return {Number} The sum
9161      */
9162     sum : function(property, start, end){
9163         var rs = this.data.items, v = 0;
9164         start = start || 0;
9165         end = (end || end === 0) ? end : rs.length-1;
9166
9167         for(var i = start; i <= end; i++){
9168             v += (rs[i].data[property] || 0);
9169         }
9170         return v;
9171     },
9172
9173     /**
9174      * Filter the records by a specified property.
9175      * @param {String} field A field on your records
9176      * @param {String/RegExp} value Either a string that the field
9177      * should start with or a RegExp to test against the field
9178      * @param {Boolean} anyMatch True to match any part not just the beginning
9179      */
9180     filter : function(property, value, anyMatch){
9181         var fn = this.createFilterFn(property, value, anyMatch);
9182         return fn ? this.filterBy(fn) : this.clearFilter();
9183     },
9184
9185     /**
9186      * Filter by a function. The specified function will be called with each
9187      * record in this data source. If the function returns true the record is included,
9188      * otherwise it is filtered.
9189      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9190      * @param {Object} scope (optional) The scope of the function (defaults to this)
9191      */
9192     filterBy : function(fn, scope){
9193         this.snapshot = this.snapshot || this.data;
9194         this.data = this.queryBy(fn, scope||this);
9195         this.fireEvent("datachanged", this);
9196     },
9197
9198     /**
9199      * Query the records by a specified property.
9200      * @param {String} field A field on your records
9201      * @param {String/RegExp} value Either a string that the field
9202      * should start with or a RegExp to test against the field
9203      * @param {Boolean} anyMatch True to match any part not just the beginning
9204      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9205      */
9206     query : function(property, value, anyMatch){
9207         var fn = this.createFilterFn(property, value, anyMatch);
9208         return fn ? this.queryBy(fn) : this.data.clone();
9209     },
9210
9211     /**
9212      * Query by a function. The specified function will be called with each
9213      * record in this data source. If the function returns true the record is included
9214      * in the results.
9215      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9216      * @param {Object} scope (optional) The scope of the function (defaults to this)
9217       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9218      **/
9219     queryBy : function(fn, scope){
9220         var data = this.snapshot || this.data;
9221         return data.filterBy(fn, scope||this);
9222     },
9223
9224     /**
9225      * Collects unique values for a particular dataIndex from this store.
9226      * @param {String} dataIndex The property to collect
9227      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9228      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9229      * @return {Array} An array of the unique values
9230      **/
9231     collect : function(dataIndex, allowNull, bypassFilter){
9232         var d = (bypassFilter === true && this.snapshot) ?
9233                 this.snapshot.items : this.data.items;
9234         var v, sv, r = [], l = {};
9235         for(var i = 0, len = d.length; i < len; i++){
9236             v = d[i].data[dataIndex];
9237             sv = String(v);
9238             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9239                 l[sv] = true;
9240                 r[r.length] = v;
9241             }
9242         }
9243         return r;
9244     },
9245
9246     /**
9247      * Revert to a view of the Record cache with no filtering applied.
9248      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9249      */
9250     clearFilter : function(suppressEvent){
9251         if(this.snapshot && this.snapshot != this.data){
9252             this.data = this.snapshot;
9253             delete this.snapshot;
9254             if(suppressEvent !== true){
9255                 this.fireEvent("datachanged", this);
9256             }
9257         }
9258     },
9259
9260     // private
9261     afterEdit : function(record){
9262         if(this.modified.indexOf(record) == -1){
9263             this.modified.push(record);
9264         }
9265         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9266     },
9267     
9268     // private
9269     afterReject : function(record){
9270         this.modified.remove(record);
9271         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9272     },
9273
9274     // private
9275     afterCommit : function(record){
9276         this.modified.remove(record);
9277         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9278     },
9279
9280     /**
9281      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9282      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9283      */
9284     commitChanges : function(){
9285         var m = this.modified.slice(0);
9286         this.modified = [];
9287         for(var i = 0, len = m.length; i < len; i++){
9288             m[i].commit();
9289         }
9290     },
9291
9292     /**
9293      * Cancel outstanding changes on all changed records.
9294      */
9295     rejectChanges : function(){
9296         var m = this.modified.slice(0);
9297         this.modified = [];
9298         for(var i = 0, len = m.length; i < len; i++){
9299             m[i].reject();
9300         }
9301     },
9302
9303     onMetaChange : function(meta, rtype, o){
9304         this.recordType = rtype;
9305         this.fields = rtype.prototype.fields;
9306         delete this.snapshot;
9307         this.sortInfo = meta.sortInfo || this.sortInfo;
9308         this.modified = [];
9309         this.fireEvent('metachange', this, this.reader.meta);
9310     },
9311     
9312     moveIndex : function(data, type)
9313     {
9314         var index = this.indexOf(data);
9315         
9316         var newIndex = index + type;
9317         
9318         this.remove(data);
9319         
9320         this.insert(newIndex, data);
9321         
9322     }
9323 });/*
9324  * Based on:
9325  * Ext JS Library 1.1.1
9326  * Copyright(c) 2006-2007, Ext JS, LLC.
9327  *
9328  * Originally Released Under LGPL - original licence link has changed is not relivant.
9329  *
9330  * Fork - LGPL
9331  * <script type="text/javascript">
9332  */
9333
9334 /**
9335  * @class Roo.data.SimpleStore
9336  * @extends Roo.data.Store
9337  * Small helper class to make creating Stores from Array data easier.
9338  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9339  * @cfg {Array} fields An array of field definition objects, or field name strings.
9340  * @cfg {Array} data The multi-dimensional array of data
9341  * @constructor
9342  * @param {Object} config
9343  */
9344 Roo.data.SimpleStore = function(config){
9345     Roo.data.SimpleStore.superclass.constructor.call(this, {
9346         isLocal : true,
9347         reader: new Roo.data.ArrayReader({
9348                 id: config.id
9349             },
9350             Roo.data.Record.create(config.fields)
9351         ),
9352         proxy : new Roo.data.MemoryProxy(config.data)
9353     });
9354     this.load();
9355 };
9356 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9357  * Based on:
9358  * Ext JS Library 1.1.1
9359  * Copyright(c) 2006-2007, Ext JS, LLC.
9360  *
9361  * Originally Released Under LGPL - original licence link has changed is not relivant.
9362  *
9363  * Fork - LGPL
9364  * <script type="text/javascript">
9365  */
9366
9367 /**
9368 /**
9369  * @extends Roo.data.Store
9370  * @class Roo.data.JsonStore
9371  * Small helper class to make creating Stores for JSON data easier. <br/>
9372 <pre><code>
9373 var store = new Roo.data.JsonStore({
9374     url: 'get-images.php',
9375     root: 'images',
9376     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9377 });
9378 </code></pre>
9379  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9380  * JsonReader and HttpProxy (unless inline data is provided).</b>
9381  * @cfg {Array} fields An array of field definition objects, or field name strings.
9382  * @constructor
9383  * @param {Object} config
9384  */
9385 Roo.data.JsonStore = function(c){
9386     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9387         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9388         reader: new Roo.data.JsonReader(c, c.fields)
9389     }));
9390 };
9391 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9392  * Based on:
9393  * Ext JS Library 1.1.1
9394  * Copyright(c) 2006-2007, Ext JS, LLC.
9395  *
9396  * Originally Released Under LGPL - original licence link has changed is not relivant.
9397  *
9398  * Fork - LGPL
9399  * <script type="text/javascript">
9400  */
9401
9402  
9403 Roo.data.Field = function(config){
9404     if(typeof config == "string"){
9405         config = {name: config};
9406     }
9407     Roo.apply(this, config);
9408     
9409     if(!this.type){
9410         this.type = "auto";
9411     }
9412     
9413     var st = Roo.data.SortTypes;
9414     // named sortTypes are supported, here we look them up
9415     if(typeof this.sortType == "string"){
9416         this.sortType = st[this.sortType];
9417     }
9418     
9419     // set default sortType for strings and dates
9420     if(!this.sortType){
9421         switch(this.type){
9422             case "string":
9423                 this.sortType = st.asUCString;
9424                 break;
9425             case "date":
9426                 this.sortType = st.asDate;
9427                 break;
9428             default:
9429                 this.sortType = st.none;
9430         }
9431     }
9432
9433     // define once
9434     var stripRe = /[\$,%]/g;
9435
9436     // prebuilt conversion function for this field, instead of
9437     // switching every time we're reading a value
9438     if(!this.convert){
9439         var cv, dateFormat = this.dateFormat;
9440         switch(this.type){
9441             case "":
9442             case "auto":
9443             case undefined:
9444                 cv = function(v){ return v; };
9445                 break;
9446             case "string":
9447                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9448                 break;
9449             case "int":
9450                 cv = function(v){
9451                     return v !== undefined && v !== null && v !== '' ?
9452                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9453                     };
9454                 break;
9455             case "float":
9456                 cv = function(v){
9457                     return v !== undefined && v !== null && v !== '' ?
9458                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9459                     };
9460                 break;
9461             case "bool":
9462             case "boolean":
9463                 cv = function(v){ return v === true || v === "true" || v == 1; };
9464                 break;
9465             case "date":
9466                 cv = function(v){
9467                     if(!v){
9468                         return '';
9469                     }
9470                     if(v instanceof Date){
9471                         return v;
9472                     }
9473                     if(dateFormat){
9474                         if(dateFormat == "timestamp"){
9475                             return new Date(v*1000);
9476                         }
9477                         return Date.parseDate(v, dateFormat);
9478                     }
9479                     var parsed = Date.parse(v);
9480                     return parsed ? new Date(parsed) : null;
9481                 };
9482              break;
9483             
9484         }
9485         this.convert = cv;
9486     }
9487 };
9488
9489 Roo.data.Field.prototype = {
9490     dateFormat: null,
9491     defaultValue: "",
9492     mapping: null,
9493     sortType : null,
9494     sortDir : "ASC"
9495 };/*
9496  * Based on:
9497  * Ext JS Library 1.1.1
9498  * Copyright(c) 2006-2007, Ext JS, LLC.
9499  *
9500  * Originally Released Under LGPL - original licence link has changed is not relivant.
9501  *
9502  * Fork - LGPL
9503  * <script type="text/javascript">
9504  */
9505  
9506 // Base class for reading structured data from a data source.  This class is intended to be
9507 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9508
9509 /**
9510  * @class Roo.data.DataReader
9511  * Base class for reading structured data from a data source.  This class is intended to be
9512  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9513  */
9514
9515 Roo.data.DataReader = function(meta, recordType){
9516     
9517     this.meta = meta;
9518     
9519     this.recordType = recordType instanceof Array ? 
9520         Roo.data.Record.create(recordType) : recordType;
9521 };
9522
9523 Roo.data.DataReader.prototype = {
9524      /**
9525      * Create an empty record
9526      * @param {Object} data (optional) - overlay some values
9527      * @return {Roo.data.Record} record created.
9528      */
9529     newRow :  function(d) {
9530         var da =  {};
9531         this.recordType.prototype.fields.each(function(c) {
9532             switch( c.type) {
9533                 case 'int' : da[c.name] = 0; break;
9534                 case 'date' : da[c.name] = new Date(); break;
9535                 case 'float' : da[c.name] = 0.0; break;
9536                 case 'boolean' : da[c.name] = false; break;
9537                 default : da[c.name] = ""; break;
9538             }
9539             
9540         });
9541         return new this.recordType(Roo.apply(da, d));
9542     }
9543     
9544 };/*
9545  * Based on:
9546  * Ext JS Library 1.1.1
9547  * Copyright(c) 2006-2007, Ext JS, LLC.
9548  *
9549  * Originally Released Under LGPL - original licence link has changed is not relivant.
9550  *
9551  * Fork - LGPL
9552  * <script type="text/javascript">
9553  */
9554
9555 /**
9556  * @class Roo.data.DataProxy
9557  * @extends Roo.data.Observable
9558  * This class is an abstract base class for implementations which provide retrieval of
9559  * unformatted data objects.<br>
9560  * <p>
9561  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9562  * (of the appropriate type which knows how to parse the data object) to provide a block of
9563  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9564  * <p>
9565  * Custom implementations must implement the load method as described in
9566  * {@link Roo.data.HttpProxy#load}.
9567  */
9568 Roo.data.DataProxy = function(){
9569     this.addEvents({
9570         /**
9571          * @event beforeload
9572          * Fires before a network request is made to retrieve a data object.
9573          * @param {Object} This DataProxy object.
9574          * @param {Object} params The params parameter to the load function.
9575          */
9576         beforeload : true,
9577         /**
9578          * @event load
9579          * Fires before the load method's callback is called.
9580          * @param {Object} This DataProxy object.
9581          * @param {Object} o The data object.
9582          * @param {Object} arg The callback argument object passed to the load function.
9583          */
9584         load : true,
9585         /**
9586          * @event loadexception
9587          * Fires if an Exception occurs during data retrieval.
9588          * @param {Object} This DataProxy object.
9589          * @param {Object} o The data object.
9590          * @param {Object} arg The callback argument object passed to the load function.
9591          * @param {Object} e The Exception.
9592          */
9593         loadexception : true
9594     });
9595     Roo.data.DataProxy.superclass.constructor.call(this);
9596 };
9597
9598 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9599
9600     /**
9601      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9602      */
9603 /*
9604  * Based on:
9605  * Ext JS Library 1.1.1
9606  * Copyright(c) 2006-2007, Ext JS, LLC.
9607  *
9608  * Originally Released Under LGPL - original licence link has changed is not relivant.
9609  *
9610  * Fork - LGPL
9611  * <script type="text/javascript">
9612  */
9613 /**
9614  * @class Roo.data.MemoryProxy
9615  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9616  * to the Reader when its load method is called.
9617  * @constructor
9618  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9619  */
9620 Roo.data.MemoryProxy = function(data){
9621     if (data.data) {
9622         data = data.data;
9623     }
9624     Roo.data.MemoryProxy.superclass.constructor.call(this);
9625     this.data = data;
9626 };
9627
9628 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9629     /**
9630      * Load data from the requested source (in this case an in-memory
9631      * data object passed to the constructor), read the data object into
9632      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9633      * process that block using the passed callback.
9634      * @param {Object} params This parameter is not used by the MemoryProxy class.
9635      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9636      * object into a block of Roo.data.Records.
9637      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9638      * The function must be passed <ul>
9639      * <li>The Record block object</li>
9640      * <li>The "arg" argument from the load function</li>
9641      * <li>A boolean success indicator</li>
9642      * </ul>
9643      * @param {Object} scope The scope in which to call the callback
9644      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9645      */
9646     load : function(params, reader, callback, scope, arg){
9647         params = params || {};
9648         var result;
9649         try {
9650             result = reader.readRecords(this.data);
9651         }catch(e){
9652             this.fireEvent("loadexception", this, arg, null, e);
9653             callback.call(scope, null, arg, false);
9654             return;
9655         }
9656         callback.call(scope, result, arg, true);
9657     },
9658     
9659     // private
9660     update : function(params, records){
9661         
9662     }
9663 });/*
9664  * Based on:
9665  * Ext JS Library 1.1.1
9666  * Copyright(c) 2006-2007, Ext JS, LLC.
9667  *
9668  * Originally Released Under LGPL - original licence link has changed is not relivant.
9669  *
9670  * Fork - LGPL
9671  * <script type="text/javascript">
9672  */
9673 /**
9674  * @class Roo.data.HttpProxy
9675  * @extends Roo.data.DataProxy
9676  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9677  * configured to reference a certain URL.<br><br>
9678  * <p>
9679  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9680  * from which the running page was served.<br><br>
9681  * <p>
9682  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9683  * <p>
9684  * Be aware that to enable the browser to parse an XML document, the server must set
9685  * the Content-Type header in the HTTP response to "text/xml".
9686  * @constructor
9687  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9688  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9689  * will be used to make the request.
9690  */
9691 Roo.data.HttpProxy = function(conn){
9692     Roo.data.HttpProxy.superclass.constructor.call(this);
9693     // is conn a conn config or a real conn?
9694     this.conn = conn;
9695     this.useAjax = !conn || !conn.events;
9696   
9697 };
9698
9699 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9700     // thse are take from connection...
9701     
9702     /**
9703      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9704      */
9705     /**
9706      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9707      * extra parameters to each request made by this object. (defaults to undefined)
9708      */
9709     /**
9710      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9711      *  to each request made by this object. (defaults to undefined)
9712      */
9713     /**
9714      * @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)
9715      */
9716     /**
9717      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9718      */
9719      /**
9720      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9721      * @type Boolean
9722      */
9723   
9724
9725     /**
9726      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9727      * @type Boolean
9728      */
9729     /**
9730      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9731      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9732      * a finer-grained basis than the DataProxy events.
9733      */
9734     getConnection : function(){
9735         return this.useAjax ? Roo.Ajax : this.conn;
9736     },
9737
9738     /**
9739      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9740      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9741      * process that block using the passed callback.
9742      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9743      * for the request to the remote server.
9744      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9745      * object into a block of Roo.data.Records.
9746      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9747      * The function must be passed <ul>
9748      * <li>The Record block object</li>
9749      * <li>The "arg" argument from the load function</li>
9750      * <li>A boolean success indicator</li>
9751      * </ul>
9752      * @param {Object} scope The scope in which to call the callback
9753      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9754      */
9755     load : function(params, reader, callback, scope, arg){
9756         if(this.fireEvent("beforeload", this, params) !== false){
9757             var  o = {
9758                 params : params || {},
9759                 request: {
9760                     callback : callback,
9761                     scope : scope,
9762                     arg : arg
9763                 },
9764                 reader: reader,
9765                 callback : this.loadResponse,
9766                 scope: this
9767             };
9768             if(this.useAjax){
9769                 Roo.applyIf(o, this.conn);
9770                 if(this.activeRequest){
9771                     Roo.Ajax.abort(this.activeRequest);
9772                 }
9773                 this.activeRequest = Roo.Ajax.request(o);
9774             }else{
9775                 this.conn.request(o);
9776             }
9777         }else{
9778             callback.call(scope||this, null, arg, false);
9779         }
9780     },
9781
9782     // private
9783     loadResponse : function(o, success, response){
9784         delete this.activeRequest;
9785         if(!success){
9786             this.fireEvent("loadexception", this, o, response);
9787             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9788             return;
9789         }
9790         var result;
9791         try {
9792             result = o.reader.read(response);
9793         }catch(e){
9794             this.fireEvent("loadexception", this, o, response, e);
9795             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9796             return;
9797         }
9798         
9799         this.fireEvent("load", this, o, o.request.arg);
9800         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9801     },
9802
9803     // private
9804     update : function(dataSet){
9805
9806     },
9807
9808     // private
9809     updateResponse : function(dataSet){
9810
9811     }
9812 });/*
9813  * Based on:
9814  * Ext JS Library 1.1.1
9815  * Copyright(c) 2006-2007, Ext JS, LLC.
9816  *
9817  * Originally Released Under LGPL - original licence link has changed is not relivant.
9818  *
9819  * Fork - LGPL
9820  * <script type="text/javascript">
9821  */
9822
9823 /**
9824  * @class Roo.data.ScriptTagProxy
9825  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9826  * other than the originating domain of the running page.<br><br>
9827  * <p>
9828  * <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
9829  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9830  * <p>
9831  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9832  * source code that is used as the source inside a &lt;script> tag.<br><br>
9833  * <p>
9834  * In order for the browser to process the returned data, the server must wrap the data object
9835  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9836  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9837  * depending on whether the callback name was passed:
9838  * <p>
9839  * <pre><code>
9840 boolean scriptTag = false;
9841 String cb = request.getParameter("callback");
9842 if (cb != null) {
9843     scriptTag = true;
9844     response.setContentType("text/javascript");
9845 } else {
9846     response.setContentType("application/x-json");
9847 }
9848 Writer out = response.getWriter();
9849 if (scriptTag) {
9850     out.write(cb + "(");
9851 }
9852 out.print(dataBlock.toJsonString());
9853 if (scriptTag) {
9854     out.write(");");
9855 }
9856 </pre></code>
9857  *
9858  * @constructor
9859  * @param {Object} config A configuration object.
9860  */
9861 Roo.data.ScriptTagProxy = function(config){
9862     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9863     Roo.apply(this, config);
9864     this.head = document.getElementsByTagName("head")[0];
9865 };
9866
9867 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9868
9869 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9870     /**
9871      * @cfg {String} url The URL from which to request the data object.
9872      */
9873     /**
9874      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9875      */
9876     timeout : 30000,
9877     /**
9878      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9879      * the server the name of the callback function set up by the load call to process the returned data object.
9880      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9881      * javascript output which calls this named function passing the data object as its only parameter.
9882      */
9883     callbackParam : "callback",
9884     /**
9885      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9886      * name to the request.
9887      */
9888     nocache : true,
9889
9890     /**
9891      * Load data from the configured URL, read the data object into
9892      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9893      * process that block using the passed callback.
9894      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9895      * for the request to the remote server.
9896      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9897      * object into a block of Roo.data.Records.
9898      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9899      * The function must be passed <ul>
9900      * <li>The Record block object</li>
9901      * <li>The "arg" argument from the load function</li>
9902      * <li>A boolean success indicator</li>
9903      * </ul>
9904      * @param {Object} scope The scope in which to call the callback
9905      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9906      */
9907     load : function(params, reader, callback, scope, arg){
9908         if(this.fireEvent("beforeload", this, params) !== false){
9909
9910             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9911
9912             var url = this.url;
9913             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9914             if(this.nocache){
9915                 url += "&_dc=" + (new Date().getTime());
9916             }
9917             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9918             var trans = {
9919                 id : transId,
9920                 cb : "stcCallback"+transId,
9921                 scriptId : "stcScript"+transId,
9922                 params : params,
9923                 arg : arg,
9924                 url : url,
9925                 callback : callback,
9926                 scope : scope,
9927                 reader : reader
9928             };
9929             var conn = this;
9930
9931             window[trans.cb] = function(o){
9932                 conn.handleResponse(o, trans);
9933             };
9934
9935             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9936
9937             if(this.autoAbort !== false){
9938                 this.abort();
9939             }
9940
9941             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9942
9943             var script = document.createElement("script");
9944             script.setAttribute("src", url);
9945             script.setAttribute("type", "text/javascript");
9946             script.setAttribute("id", trans.scriptId);
9947             this.head.appendChild(script);
9948
9949             this.trans = trans;
9950         }else{
9951             callback.call(scope||this, null, arg, false);
9952         }
9953     },
9954
9955     // private
9956     isLoading : function(){
9957         return this.trans ? true : false;
9958     },
9959
9960     /**
9961      * Abort the current server request.
9962      */
9963     abort : function(){
9964         if(this.isLoading()){
9965             this.destroyTrans(this.trans);
9966         }
9967     },
9968
9969     // private
9970     destroyTrans : function(trans, isLoaded){
9971         this.head.removeChild(document.getElementById(trans.scriptId));
9972         clearTimeout(trans.timeoutId);
9973         if(isLoaded){
9974             window[trans.cb] = undefined;
9975             try{
9976                 delete window[trans.cb];
9977             }catch(e){}
9978         }else{
9979             // if hasn't been loaded, wait for load to remove it to prevent script error
9980             window[trans.cb] = function(){
9981                 window[trans.cb] = undefined;
9982                 try{
9983                     delete window[trans.cb];
9984                 }catch(e){}
9985             };
9986         }
9987     },
9988
9989     // private
9990     handleResponse : function(o, trans){
9991         this.trans = false;
9992         this.destroyTrans(trans, true);
9993         var result;
9994         try {
9995             result = trans.reader.readRecords(o);
9996         }catch(e){
9997             this.fireEvent("loadexception", this, o, trans.arg, e);
9998             trans.callback.call(trans.scope||window, null, trans.arg, false);
9999             return;
10000         }
10001         this.fireEvent("load", this, o, trans.arg);
10002         trans.callback.call(trans.scope||window, result, trans.arg, true);
10003     },
10004
10005     // private
10006     handleFailure : function(trans){
10007         this.trans = false;
10008         this.destroyTrans(trans, false);
10009         this.fireEvent("loadexception", this, null, trans.arg);
10010         trans.callback.call(trans.scope||window, null, trans.arg, false);
10011     }
10012 });/*
10013  * Based on:
10014  * Ext JS Library 1.1.1
10015  * Copyright(c) 2006-2007, Ext JS, LLC.
10016  *
10017  * Originally Released Under LGPL - original licence link has changed is not relivant.
10018  *
10019  * Fork - LGPL
10020  * <script type="text/javascript">
10021  */
10022
10023 /**
10024  * @class Roo.data.JsonReader
10025  * @extends Roo.data.DataReader
10026  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10027  * based on mappings in a provided Roo.data.Record constructor.
10028  * 
10029  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10030  * in the reply previously. 
10031  * 
10032  * <p>
10033  * Example code:
10034  * <pre><code>
10035 var RecordDef = Roo.data.Record.create([
10036     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10037     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10038 ]);
10039 var myReader = new Roo.data.JsonReader({
10040     totalProperty: "results",    // The property which contains the total dataset size (optional)
10041     root: "rows",                // The property which contains an Array of row objects
10042     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10043 }, RecordDef);
10044 </code></pre>
10045  * <p>
10046  * This would consume a JSON file like this:
10047  * <pre><code>
10048 { 'results': 2, 'rows': [
10049     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10050     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10051 }
10052 </code></pre>
10053  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10054  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10055  * paged from the remote server.
10056  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10057  * @cfg {String} root name of the property which contains the Array of row objects.
10058  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10059  * @constructor
10060  * Create a new JsonReader
10061  * @param {Object} meta Metadata configuration options
10062  * @param {Object} recordType Either an Array of field definition objects,
10063  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10064  */
10065 Roo.data.JsonReader = function(meta, recordType){
10066     
10067     meta = meta || {};
10068     // set some defaults:
10069     Roo.applyIf(meta, {
10070         totalProperty: 'total',
10071         successProperty : 'success',
10072         root : 'data',
10073         id : 'id'
10074     });
10075     
10076     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10077 };
10078 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10079     
10080     /**
10081      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10082      * Used by Store query builder to append _requestMeta to params.
10083      * 
10084      */
10085     metaFromRemote : false,
10086     /**
10087      * This method is only used by a DataProxy which has retrieved data from a remote server.
10088      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10089      * @return {Object} data A data block which is used by an Roo.data.Store object as
10090      * a cache of Roo.data.Records.
10091      */
10092     read : function(response){
10093         var json = response.responseText;
10094        
10095         var o = /* eval:var:o */ eval("("+json+")");
10096         if(!o) {
10097             throw {message: "JsonReader.read: Json object not found"};
10098         }
10099         
10100         if(o.metaData){
10101             
10102             delete this.ef;
10103             this.metaFromRemote = true;
10104             this.meta = o.metaData;
10105             this.recordType = Roo.data.Record.create(o.metaData.fields);
10106             this.onMetaChange(this.meta, this.recordType, o);
10107         }
10108         return this.readRecords(o);
10109     },
10110
10111     // private function a store will implement
10112     onMetaChange : function(meta, recordType, o){
10113
10114     },
10115
10116     /**
10117          * @ignore
10118          */
10119     simpleAccess: function(obj, subsc) {
10120         return obj[subsc];
10121     },
10122
10123         /**
10124          * @ignore
10125          */
10126     getJsonAccessor: function(){
10127         var re = /[\[\.]/;
10128         return function(expr) {
10129             try {
10130                 return(re.test(expr))
10131                     ? new Function("obj", "return obj." + expr)
10132                     : function(obj){
10133                         return obj[expr];
10134                     };
10135             } catch(e){}
10136             return Roo.emptyFn;
10137         };
10138     }(),
10139
10140     /**
10141      * Create a data block containing Roo.data.Records from an XML document.
10142      * @param {Object} o An object which contains an Array of row objects in the property specified
10143      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10144      * which contains the total size of the dataset.
10145      * @return {Object} data A data block which is used by an Roo.data.Store object as
10146      * a cache of Roo.data.Records.
10147      */
10148     readRecords : function(o){
10149         /**
10150          * After any data loads, the raw JSON data is available for further custom processing.
10151          * @type Object
10152          */
10153         this.o = o;
10154         var s = this.meta, Record = this.recordType,
10155             f = Record.prototype.fields, fi = f.items, fl = f.length;
10156
10157 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10158         if (!this.ef) {
10159             if(s.totalProperty) {
10160                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10161                 }
10162                 if(s.successProperty) {
10163                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10164                 }
10165                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10166                 if (s.id) {
10167                         var g = this.getJsonAccessor(s.id);
10168                         this.getId = function(rec) {
10169                                 var r = g(rec);  
10170                                 return (r === undefined || r === "") ? null : r;
10171                         };
10172                 } else {
10173                         this.getId = function(){return null;};
10174                 }
10175             this.ef = [];
10176             for(var jj = 0; jj < fl; jj++){
10177                 f = fi[jj];
10178                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10179                 this.ef[jj] = this.getJsonAccessor(map);
10180             }
10181         }
10182
10183         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10184         if(s.totalProperty){
10185             var vt = parseInt(this.getTotal(o), 10);
10186             if(!isNaN(vt)){
10187                 totalRecords = vt;
10188             }
10189         }
10190         if(s.successProperty){
10191             var vs = this.getSuccess(o);
10192             if(vs === false || vs === 'false'){
10193                 success = false;
10194             }
10195         }
10196         var records = [];
10197             for(var i = 0; i < c; i++){
10198                     var n = root[i];
10199                 var values = {};
10200                 var id = this.getId(n);
10201                 for(var j = 0; j < fl; j++){
10202                     f = fi[j];
10203                 var v = this.ef[j](n);
10204                 if (!f.convert) {
10205                     Roo.log('missing convert for ' + f.name);
10206                     Roo.log(f);
10207                     continue;
10208                 }
10209                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10210                 }
10211                 var record = new Record(values, id);
10212                 record.json = n;
10213                 records[i] = record;
10214             }
10215             return {
10216             raw : o,
10217                 success : success,
10218                 records : records,
10219                 totalRecords : totalRecords
10220             };
10221     }
10222 });/*
10223  * Based on:
10224  * Ext JS Library 1.1.1
10225  * Copyright(c) 2006-2007, Ext JS, LLC.
10226  *
10227  * Originally Released Under LGPL - original licence link has changed is not relivant.
10228  *
10229  * Fork - LGPL
10230  * <script type="text/javascript">
10231  */
10232
10233 /**
10234  * @class Roo.data.ArrayReader
10235  * @extends Roo.data.DataReader
10236  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10237  * Each element of that Array represents a row of data fields. The
10238  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10239  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10240  * <p>
10241  * Example code:.
10242  * <pre><code>
10243 var RecordDef = Roo.data.Record.create([
10244     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10245     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10246 ]);
10247 var myReader = new Roo.data.ArrayReader({
10248     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10249 }, RecordDef);
10250 </code></pre>
10251  * <p>
10252  * This would consume an Array like this:
10253  * <pre><code>
10254 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10255   </code></pre>
10256  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10257  * @constructor
10258  * Create a new JsonReader
10259  * @param {Object} meta Metadata configuration options.
10260  * @param {Object} recordType Either an Array of field definition objects
10261  * as specified to {@link Roo.data.Record#create},
10262  * or an {@link Roo.data.Record} object
10263  * created using {@link Roo.data.Record#create}.
10264  */
10265 Roo.data.ArrayReader = function(meta, recordType){
10266     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10267 };
10268
10269 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10270     /**
10271      * Create a data block containing Roo.data.Records from an XML document.
10272      * @param {Object} o An Array of row objects which represents the dataset.
10273      * @return {Object} data A data block which is used by an Roo.data.Store object as
10274      * a cache of Roo.data.Records.
10275      */
10276     readRecords : function(o){
10277         var sid = this.meta ? this.meta.id : null;
10278         var recordType = this.recordType, fields = recordType.prototype.fields;
10279         var records = [];
10280         var root = o;
10281             for(var i = 0; i < root.length; i++){
10282                     var n = root[i];
10283                 var values = {};
10284                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10285                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10286                 var f = fields.items[j];
10287                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10288                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10289                 v = f.convert(v);
10290                 values[f.name] = v;
10291             }
10292                 var record = new recordType(values, id);
10293                 record.json = n;
10294                 records[records.length] = record;
10295             }
10296             return {
10297                 records : records,
10298                 totalRecords : records.length
10299             };
10300     }
10301 });/*
10302  * - LGPL
10303  * * 
10304  */
10305
10306 /**
10307  * @class Roo.bootstrap.ComboBox
10308  * @extends Roo.bootstrap.TriggerField
10309  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10310  * @cfg {Boolean} append (true|false) default false
10311  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10312  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10313  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10314  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10315  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10316  * @constructor
10317  * Create a new ComboBox.
10318  * @param {Object} config Configuration options
10319  */
10320 Roo.bootstrap.ComboBox = function(config){
10321     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10322     this.addEvents({
10323         /**
10324          * @event expand
10325          * Fires when the dropdown list is expanded
10326              * @param {Roo.bootstrap.ComboBox} combo This combo box
10327              */
10328         'expand' : true,
10329         /**
10330          * @event collapse
10331          * Fires when the dropdown list is collapsed
10332              * @param {Roo.bootstrap.ComboBox} combo This combo box
10333              */
10334         'collapse' : true,
10335         /**
10336          * @event beforeselect
10337          * Fires before a list item is selected. Return false to cancel the selection.
10338              * @param {Roo.bootstrap.ComboBox} combo This combo box
10339              * @param {Roo.data.Record} record The data record returned from the underlying store
10340              * @param {Number} index The index of the selected item in the dropdown list
10341              */
10342         'beforeselect' : true,
10343         /**
10344          * @event select
10345          * Fires when a list item is selected
10346              * @param {Roo.bootstrap.ComboBox} combo This combo box
10347              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10348              * @param {Number} index The index of the selected item in the dropdown list
10349              */
10350         'select' : true,
10351         /**
10352          * @event beforequery
10353          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10354          * The event object passed has these properties:
10355              * @param {Roo.bootstrap.ComboBox} combo This combo box
10356              * @param {String} query The query
10357              * @param {Boolean} forceAll true to force "all" query
10358              * @param {Boolean} cancel true to cancel the query
10359              * @param {Object} e The query event object
10360              */
10361         'beforequery': true,
10362          /**
10363          * @event add
10364          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10365              * @param {Roo.bootstrap.ComboBox} combo This combo box
10366              */
10367         'add' : true,
10368         /**
10369          * @event edit
10370          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10371              * @param {Roo.bootstrap.ComboBox} combo This combo box
10372              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10373              */
10374         'edit' : true,
10375         /**
10376          * @event remove
10377          * Fires when the remove value from the combobox array
10378              * @param {Roo.bootstrap.ComboBox} combo This combo box
10379              */
10380         'remove' : true
10381         
10382     });
10383     
10384     this.item = [];
10385     this.tickItems = [];
10386     
10387     this.selectedIndex = -1;
10388     if(this.mode == 'local'){
10389         if(config.queryDelay === undefined){
10390             this.queryDelay = 10;
10391         }
10392         if(config.minChars === undefined){
10393             this.minChars = 0;
10394         }
10395     }
10396 };
10397
10398 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10399      
10400     /**
10401      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10402      * rendering into an Roo.Editor, defaults to false)
10403      */
10404     /**
10405      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10406      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10407      */
10408     /**
10409      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10410      */
10411     /**
10412      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10413      * the dropdown list (defaults to undefined, with no header element)
10414      */
10415
10416      /**
10417      * @cfg {String/Roo.Template} tpl The template to use to render the output
10418      */
10419      
10420      /**
10421      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10422      */
10423     listWidth: undefined,
10424     /**
10425      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10426      * mode = 'remote' or 'text' if mode = 'local')
10427      */
10428     displayField: undefined,
10429     /**
10430      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10431      * mode = 'remote' or 'value' if mode = 'local'). 
10432      * Note: use of a valueField requires the user make a selection
10433      * in order for a value to be mapped.
10434      */
10435     valueField: undefined,
10436     
10437     
10438     /**
10439      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10440      * field's data value (defaults to the underlying DOM element's name)
10441      */
10442     hiddenName: undefined,
10443     /**
10444      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10445      */
10446     listClass: '',
10447     /**
10448      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10449      */
10450     selectedClass: 'active',
10451     
10452     /**
10453      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10454      */
10455     shadow:'sides',
10456     /**
10457      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10458      * anchor positions (defaults to 'tl-bl')
10459      */
10460     listAlign: 'tl-bl?',
10461     /**
10462      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10463      */
10464     maxHeight: 300,
10465     /**
10466      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10467      * query specified by the allQuery config option (defaults to 'query')
10468      */
10469     triggerAction: 'query',
10470     /**
10471      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10472      * (defaults to 4, does not apply if editable = false)
10473      */
10474     minChars : 4,
10475     /**
10476      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10477      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10478      */
10479     typeAhead: false,
10480     /**
10481      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10482      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10483      */
10484     queryDelay: 500,
10485     /**
10486      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10487      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10488      */
10489     pageSize: 0,
10490     /**
10491      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10492      * when editable = true (defaults to false)
10493      */
10494     selectOnFocus:false,
10495     /**
10496      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10497      */
10498     queryParam: 'query',
10499     /**
10500      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10501      * when mode = 'remote' (defaults to 'Loading...')
10502      */
10503     loadingText: 'Loading...',
10504     /**
10505      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10506      */
10507     resizable: false,
10508     /**
10509      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10510      */
10511     handleHeight : 8,
10512     /**
10513      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10514      * traditional select (defaults to true)
10515      */
10516     editable: true,
10517     /**
10518      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10519      */
10520     allQuery: '',
10521     /**
10522      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10523      */
10524     mode: 'remote',
10525     /**
10526      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10527      * listWidth has a higher value)
10528      */
10529     minListWidth : 70,
10530     /**
10531      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10532      * allow the user to set arbitrary text into the field (defaults to false)
10533      */
10534     forceSelection:false,
10535     /**
10536      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10537      * if typeAhead = true (defaults to 250)
10538      */
10539     typeAheadDelay : 250,
10540     /**
10541      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10542      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10543      */
10544     valueNotFoundText : undefined,
10545     /**
10546      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10547      */
10548     blockFocus : false,
10549     
10550     /**
10551      * @cfg {Boolean} disableClear Disable showing of clear button.
10552      */
10553     disableClear : false,
10554     /**
10555      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10556      */
10557     alwaysQuery : false,
10558     
10559     /**
10560      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10561      */
10562     multiple : false,
10563     
10564     //private
10565     addicon : false,
10566     editicon: false,
10567     
10568     page: 0,
10569     hasQuery: false,
10570     append: false,
10571     loadNext: false,
10572     autoFocus : true,
10573     tickable : false,
10574     btnPosition : 'right',
10575     triggerList : true,
10576     showToggleBtn : true,
10577     // element that contains real text value.. (when hidden is used..)
10578     
10579     getAutoCreate : function()
10580     {
10581         var cfg = false;
10582         
10583         /*
10584          *  Normal ComboBox
10585          */
10586         if(!this.tickable){
10587             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10588             return cfg;
10589         }
10590         
10591         /*
10592          *  ComboBox with tickable selections
10593          */
10594              
10595         var align = this.labelAlign || this.parentLabelAlign();
10596         
10597         cfg = {
10598             cls : 'form-group roo-combobox-tickable' //input-group
10599         };
10600         
10601         
10602         var buttons = {
10603             tag : 'div',
10604             cls : 'tickable-buttons',
10605             cn : [
10606                 {
10607                     tag : 'button',
10608                     type : 'button',
10609                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10610                     html : 'Edit'
10611                 },
10612                 {
10613                     tag : 'button',
10614                     type : 'button',
10615                     name : 'ok',
10616                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10617                     html : 'Done'
10618                 },
10619                 {
10620                     tag : 'button',
10621                     type : 'button',
10622                     name : 'cancel',
10623                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10624                     html : 'Cancel'
10625                 }
10626             ]
10627         };
10628         
10629         var _this = this;
10630         Roo.each(buttons.cn, function(c){
10631             if (_this.size) {
10632                 c.cls += ' btn-' + _this.size;
10633             }
10634
10635             if (_this.disabled) {
10636                 c.disabled = true;
10637             }
10638         });
10639         
10640         var box = {
10641             tag: 'div',
10642             cn: [
10643                 {
10644                     tag: 'input',
10645                     type : 'hidden',
10646                     cls: 'form-hidden-field'
10647                 },
10648                 {
10649                     tag: 'ul',
10650                     cls: 'select2-choices',
10651                     cn:[
10652                         {
10653                             tag: 'li',
10654                             cls: 'select2-search-field',
10655                             cn: [
10656
10657                                 buttons
10658                             ]
10659                         }
10660                     ]
10661                 }
10662             ]
10663         }
10664         
10665         var combobox = {
10666             cls: 'select2-container input-group select2-container-multi',
10667             cn: [
10668                 box
10669 //                {
10670 //                    tag: 'ul',
10671 //                    cls: 'typeahead typeahead-long dropdown-menu',
10672 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10673 //                }
10674             ]
10675         };
10676         
10677         if (align ==='left' && this.fieldLabel.length) {
10678             
10679                 Roo.log("left and has label");
10680                 cfg.cn = [
10681                     
10682                     {
10683                         tag: 'label',
10684                         'for' :  id,
10685                         cls : 'control-label col-sm-' + this.labelWidth,
10686                         html : this.fieldLabel
10687                         
10688                     },
10689                     {
10690                         cls : "col-sm-" + (12 - this.labelWidth), 
10691                         cn: [
10692                             combobox
10693                         ]
10694                     }
10695                     
10696                 ];
10697         } else if ( this.fieldLabel.length) {
10698                 Roo.log(" label");
10699                  cfg.cn = [
10700                    
10701                     {
10702                         tag: 'label',
10703                         //cls : 'input-group-addon',
10704                         html : this.fieldLabel
10705                         
10706                     },
10707                     
10708                     combobox
10709                     
10710                 ];
10711
10712         } else {
10713             
10714                 Roo.log(" no label && no align");
10715                 cfg = combobox
10716                      
10717                 
10718         }
10719          
10720         var settings=this;
10721         ['xs','sm','md','lg'].map(function(size){
10722             if (settings[size]) {
10723                 cfg.cls += ' col-' + size + '-' + settings[size];
10724             }
10725         });
10726         
10727         return cfg;
10728         
10729     },
10730     
10731     // private
10732     initEvents: function()
10733     {
10734         
10735         if (!this.store) {
10736             throw "can not find store for combo";
10737         }
10738         this.store = Roo.factory(this.store, Roo.data);
10739         
10740         if(this.tickable){
10741             this.initTickableEvents();
10742             return;
10743         }
10744         
10745         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10746         
10747         if(this.hiddenName){
10748             
10749             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10750             
10751             this.hiddenField.dom.value =
10752                 this.hiddenValue !== undefined ? this.hiddenValue :
10753                 this.value !== undefined ? this.value : '';
10754
10755             // prevent input submission
10756             this.el.dom.removeAttribute('name');
10757             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10758              
10759              
10760         }
10761         //if(Roo.isGecko){
10762         //    this.el.dom.setAttribute('autocomplete', 'off');
10763         //}
10764         
10765         var cls = 'x-combo-list';
10766         
10767         //this.list = new Roo.Layer({
10768         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10769         //});
10770         
10771         var _this = this;
10772         
10773         (function(){
10774             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10775             _this.list.setWidth(lw);
10776         }).defer(100);
10777         
10778         this.list.on('mouseover', this.onViewOver, this);
10779         this.list.on('mousemove', this.onViewMove, this);
10780         
10781         this.list.on('scroll', this.onViewScroll, this);
10782         
10783         /*
10784         this.list.swallowEvent('mousewheel');
10785         this.assetHeight = 0;
10786
10787         if(this.title){
10788             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10789             this.assetHeight += this.header.getHeight();
10790         }
10791
10792         this.innerList = this.list.createChild({cls:cls+'-inner'});
10793         this.innerList.on('mouseover', this.onViewOver, this);
10794         this.innerList.on('mousemove', this.onViewMove, this);
10795         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10796         
10797         if(this.allowBlank && !this.pageSize && !this.disableClear){
10798             this.footer = this.list.createChild({cls:cls+'-ft'});
10799             this.pageTb = new Roo.Toolbar(this.footer);
10800            
10801         }
10802         if(this.pageSize){
10803             this.footer = this.list.createChild({cls:cls+'-ft'});
10804             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10805                     {pageSize: this.pageSize});
10806             
10807         }
10808         
10809         if (this.pageTb && this.allowBlank && !this.disableClear) {
10810             var _this = this;
10811             this.pageTb.add(new Roo.Toolbar.Fill(), {
10812                 cls: 'x-btn-icon x-btn-clear',
10813                 text: '&#160;',
10814                 handler: function()
10815                 {
10816                     _this.collapse();
10817                     _this.clearValue();
10818                     _this.onSelect(false, -1);
10819                 }
10820             });
10821         }
10822         if (this.footer) {
10823             this.assetHeight += this.footer.getHeight();
10824         }
10825         */
10826             
10827         if(!this.tpl){
10828             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10829         }
10830
10831         this.view = new Roo.View(this.list, this.tpl, {
10832             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10833         });
10834         //this.view.wrapEl.setDisplayed(false);
10835         this.view.on('click', this.onViewClick, this);
10836         
10837         
10838         
10839         this.store.on('beforeload', this.onBeforeLoad, this);
10840         this.store.on('load', this.onLoad, this);
10841         this.store.on('loadexception', this.onLoadException, this);
10842         /*
10843         if(this.resizable){
10844             this.resizer = new Roo.Resizable(this.list,  {
10845                pinned:true, handles:'se'
10846             });
10847             this.resizer.on('resize', function(r, w, h){
10848                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10849                 this.listWidth = w;
10850                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10851                 this.restrictHeight();
10852             }, this);
10853             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10854         }
10855         */
10856         if(!this.editable){
10857             this.editable = true;
10858             this.setEditable(false);
10859         }
10860         
10861         /*
10862         
10863         if (typeof(this.events.add.listeners) != 'undefined') {
10864             
10865             this.addicon = this.wrap.createChild(
10866                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10867        
10868             this.addicon.on('click', function(e) {
10869                 this.fireEvent('add', this);
10870             }, this);
10871         }
10872         if (typeof(this.events.edit.listeners) != 'undefined') {
10873             
10874             this.editicon = this.wrap.createChild(
10875                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10876             if (this.addicon) {
10877                 this.editicon.setStyle('margin-left', '40px');
10878             }
10879             this.editicon.on('click', function(e) {
10880                 
10881                 // we fire even  if inothing is selected..
10882                 this.fireEvent('edit', this, this.lastData );
10883                 
10884             }, this);
10885         }
10886         */
10887         
10888         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10889             "up" : function(e){
10890                 this.inKeyMode = true;
10891                 this.selectPrev();
10892             },
10893
10894             "down" : function(e){
10895                 if(!this.isExpanded()){
10896                     this.onTriggerClick();
10897                 }else{
10898                     this.inKeyMode = true;
10899                     this.selectNext();
10900                 }
10901             },
10902
10903             "enter" : function(e){
10904 //                this.onViewClick();
10905                 //return true;
10906                 this.collapse();
10907                 
10908                 if(this.fireEvent("specialkey", this, e)){
10909                     this.onViewClick(false);
10910                 }
10911                 
10912                 return true;
10913             },
10914
10915             "esc" : function(e){
10916                 this.collapse();
10917             },
10918
10919             "tab" : function(e){
10920                 this.collapse();
10921                 
10922                 if(this.fireEvent("specialkey", this, e)){
10923                     this.onViewClick(false);
10924                 }
10925                 
10926                 return true;
10927             },
10928
10929             scope : this,
10930
10931             doRelay : function(foo, bar, hname){
10932                 if(hname == 'down' || this.scope.isExpanded()){
10933                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10934                 }
10935                 return true;
10936             },
10937
10938             forceKeyDown: true
10939         });
10940         
10941         
10942         this.queryDelay = Math.max(this.queryDelay || 10,
10943                 this.mode == 'local' ? 10 : 250);
10944         
10945         
10946         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10947         
10948         if(this.typeAhead){
10949             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10950         }
10951         if(this.editable !== false){
10952             this.inputEl().on("keyup", this.onKeyUp, this);
10953         }
10954         if(this.forceSelection){
10955             this.inputEl().on('blur', this.doForce, this);
10956         }
10957         
10958         if(this.multiple){
10959             this.choices = this.el.select('ul.select2-choices', true).first();
10960             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10961         }
10962     },
10963     
10964     initTickableEvents: function()
10965     {   
10966         this.createList();
10967         
10968         if(this.hiddenName){
10969             
10970             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10971             
10972             this.hiddenField.dom.value =
10973                 this.hiddenValue !== undefined ? this.hiddenValue :
10974                 this.value !== undefined ? this.value : '';
10975
10976             // prevent input submission
10977             this.el.dom.removeAttribute('name');
10978             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10979              
10980              
10981         }
10982         
10983 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10984         
10985         this.choices = this.el.select('ul.select2-choices', true).first();
10986         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10987         if(this.triggerList){
10988             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10989         }
10990          
10991         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10992         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10993         
10994         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10995         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10996         
10997         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10998         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10999         
11000         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11001         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11002         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11003         
11004         this.okBtn.hide();
11005         this.cancelBtn.hide();
11006         
11007         var _this = this;
11008         
11009         (function(){
11010             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11011             _this.list.setWidth(lw);
11012         }).defer(100);
11013         
11014         this.list.on('mouseover', this.onViewOver, this);
11015         this.list.on('mousemove', this.onViewMove, this);
11016         
11017         this.list.on('scroll', this.onViewScroll, this);
11018         
11019         if(!this.tpl){
11020             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>';
11021         }
11022
11023         this.view = new Roo.View(this.list, this.tpl, {
11024             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11025         });
11026         
11027         //this.view.wrapEl.setDisplayed(false);
11028         this.view.on('click', this.onViewClick, this);
11029         
11030         
11031         
11032         this.store.on('beforeload', this.onBeforeLoad, this);
11033         this.store.on('load', this.onLoad, this);
11034         this.store.on('loadexception', this.onLoadException, this);
11035         
11036 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11037 //            "up" : function(e){
11038 //                this.inKeyMode = true;
11039 //                this.selectPrev();
11040 //            },
11041 //
11042 //            "down" : function(e){
11043 //                if(!this.isExpanded()){
11044 //                    this.onTriggerClick();
11045 //                }else{
11046 //                    this.inKeyMode = true;
11047 //                    this.selectNext();
11048 //                }
11049 //            },
11050 //
11051 //            "enter" : function(e){
11052 ////                this.onViewClick();
11053 //                //return true;
11054 //                this.collapse();
11055 //                
11056 //                if(this.fireEvent("specialkey", this, e)){
11057 //                    this.onViewClick(false);
11058 //                }
11059 //                
11060 //                return true;
11061 //            },
11062 //
11063 //            "esc" : function(e){
11064 //                this.collapse();
11065 //            },
11066 //
11067 //            "tab" : function(e){
11068 //                this.collapse();
11069 //                
11070 //                if(this.fireEvent("specialkey", this, e)){
11071 //                    this.onViewClick(false);
11072 //                }
11073 //                
11074 //                return true;
11075 //            },
11076 //
11077 //            scope : this,
11078 //
11079 //            doRelay : function(foo, bar, hname){
11080 //                if(hname == 'down' || this.scope.isExpanded()){
11081 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11082 //                }
11083 //                return true;
11084 //            },
11085 //
11086 //            forceKeyDown: true
11087 //        });
11088         
11089         
11090         this.queryDelay = Math.max(this.queryDelay || 10,
11091                 this.mode == 'local' ? 10 : 250);
11092         
11093         
11094         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11095         
11096         if(this.typeAhead){
11097             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11098         }
11099     },
11100
11101     onDestroy : function(){
11102         if(this.view){
11103             this.view.setStore(null);
11104             this.view.el.removeAllListeners();
11105             this.view.el.remove();
11106             this.view.purgeListeners();
11107         }
11108         if(this.list){
11109             this.list.dom.innerHTML  = '';
11110         }
11111         
11112         if(this.store){
11113             this.store.un('beforeload', this.onBeforeLoad, this);
11114             this.store.un('load', this.onLoad, this);
11115             this.store.un('loadexception', this.onLoadException, this);
11116         }
11117         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11118     },
11119
11120     // private
11121     fireKey : function(e){
11122         if(e.isNavKeyPress() && !this.list.isVisible()){
11123             this.fireEvent("specialkey", this, e);
11124         }
11125     },
11126
11127     // private
11128     onResize: function(w, h){
11129 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11130 //        
11131 //        if(typeof w != 'number'){
11132 //            // we do not handle it!?!?
11133 //            return;
11134 //        }
11135 //        var tw = this.trigger.getWidth();
11136 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11137 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11138 //        var x = w - tw;
11139 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11140 //            
11141 //        //this.trigger.setStyle('left', x+'px');
11142 //        
11143 //        if(this.list && this.listWidth === undefined){
11144 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11145 //            this.list.setWidth(lw);
11146 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11147 //        }
11148         
11149     
11150         
11151     },
11152
11153     /**
11154      * Allow or prevent the user from directly editing the field text.  If false is passed,
11155      * the user will only be able to select from the items defined in the dropdown list.  This method
11156      * is the runtime equivalent of setting the 'editable' config option at config time.
11157      * @param {Boolean} value True to allow the user to directly edit the field text
11158      */
11159     setEditable : function(value){
11160         if(value == this.editable){
11161             return;
11162         }
11163         this.editable = value;
11164         if(!value){
11165             this.inputEl().dom.setAttribute('readOnly', true);
11166             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11167             this.inputEl().addClass('x-combo-noedit');
11168         }else{
11169             this.inputEl().dom.setAttribute('readOnly', false);
11170             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11171             this.inputEl().removeClass('x-combo-noedit');
11172         }
11173     },
11174
11175     // private
11176     
11177     onBeforeLoad : function(combo,opts){
11178         if(!this.hasFocus){
11179             return;
11180         }
11181          if (!opts.add) {
11182             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11183          }
11184         this.restrictHeight();
11185         this.selectedIndex = -1;
11186     },
11187
11188     // private
11189     onLoad : function(){
11190         
11191         this.hasQuery = false;
11192         
11193         if(!this.hasFocus){
11194             return;
11195         }
11196         
11197         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11198             this.loading.hide();
11199         }
11200         
11201         if(this.store.getCount() > 0){
11202             this.expand();
11203 //            this.restrictHeight();
11204             if(this.lastQuery == this.allQuery){
11205                 if(this.editable && !this.tickable){
11206                     this.inputEl().dom.select();
11207                 }
11208                 
11209                 if(
11210                     !this.selectByValue(this.value, true) &&
11211                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11212                     this.store.lastOptions.add != true)
11213                 ){
11214                     this.select(0, true);
11215                 }
11216             }else{
11217                 if(this.autoFocus){
11218                     this.selectNext();
11219                 }
11220                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11221                     this.taTask.delay(this.typeAheadDelay);
11222                 }
11223             }
11224         }else{
11225             this.onEmptyResults();
11226         }
11227         
11228         //this.el.focus();
11229     },
11230     // private
11231     onLoadException : function()
11232     {
11233         this.hasQuery = false;
11234         
11235         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11236             this.loading.hide();
11237         }
11238         
11239         this.collapse();
11240         Roo.log(this.store.reader.jsonData);
11241         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11242             // fixme
11243             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11244         }
11245         
11246         
11247     },
11248     // private
11249     onTypeAhead : function(){
11250         if(this.store.getCount() > 0){
11251             var r = this.store.getAt(0);
11252             var newValue = r.data[this.displayField];
11253             var len = newValue.length;
11254             var selStart = this.getRawValue().length;
11255             
11256             if(selStart != len){
11257                 this.setRawValue(newValue);
11258                 this.selectText(selStart, newValue.length);
11259             }
11260         }
11261     },
11262
11263     // private
11264     onSelect : function(record, index){
11265         
11266         if(this.fireEvent('beforeselect', this, record, index) !== false){
11267         
11268             this.setFromData(index > -1 ? record.data : false);
11269             
11270             this.collapse();
11271             this.fireEvent('select', this, record, index);
11272         }
11273     },
11274
11275     /**
11276      * Returns the currently selected field value or empty string if no value is set.
11277      * @return {String} value The selected value
11278      */
11279     getValue : function(){
11280         
11281         if(this.multiple){
11282             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11283         }
11284         
11285         if(this.valueField){
11286             return typeof this.value != 'undefined' ? this.value : '';
11287         }else{
11288             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11289         }
11290     },
11291
11292     /**
11293      * Clears any text/value currently set in the field
11294      */
11295     clearValue : function(){
11296         if(this.hiddenField){
11297             this.hiddenField.dom.value = '';
11298         }
11299         this.value = '';
11300         this.setRawValue('');
11301         this.lastSelectionText = '';
11302         
11303     },
11304
11305     /**
11306      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11307      * will be displayed in the field.  If the value does not match the data value of an existing item,
11308      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11309      * Otherwise the field will be blank (although the value will still be set).
11310      * @param {String} value The value to match
11311      */
11312     setValue : function(v){
11313         if(this.multiple){
11314             this.syncValue();
11315             return;
11316         }
11317         
11318         var text = v;
11319         if(this.valueField){
11320             var r = this.findRecord(this.valueField, v);
11321             if(r){
11322                 text = r.data[this.displayField];
11323             }else if(this.valueNotFoundText !== undefined){
11324                 text = this.valueNotFoundText;
11325             }
11326         }
11327         this.lastSelectionText = text;
11328         if(this.hiddenField){
11329             this.hiddenField.dom.value = v;
11330         }
11331         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11332         this.value = v;
11333     },
11334     /**
11335      * @property {Object} the last set data for the element
11336      */
11337     
11338     lastData : false,
11339     /**
11340      * Sets the value of the field based on a object which is related to the record format for the store.
11341      * @param {Object} value the value to set as. or false on reset?
11342      */
11343     setFromData : function(o){
11344         
11345         if(this.multiple){
11346             this.addItem(o);
11347             return;
11348         }
11349             
11350         var dv = ''; // display value
11351         var vv = ''; // value value..
11352         this.lastData = o;
11353         if (this.displayField) {
11354             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11355         } else {
11356             // this is an error condition!!!
11357             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11358         }
11359         
11360         if(this.valueField){
11361             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11362         }
11363         
11364         if(this.hiddenField){
11365             this.hiddenField.dom.value = vv;
11366             
11367             this.lastSelectionText = dv;
11368             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11369             this.value = vv;
11370             return;
11371         }
11372         // no hidden field.. - we store the value in 'value', but still display
11373         // display field!!!!
11374         this.lastSelectionText = dv;
11375         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11376         this.value = vv;
11377         
11378         
11379     },
11380     // private
11381     reset : function(){
11382         // overridden so that last data is reset..
11383         this.setValue(this.originalValue);
11384         this.clearInvalid();
11385         this.lastData = false;
11386         if (this.view) {
11387             this.view.clearSelections();
11388         }
11389     },
11390     // private
11391     findRecord : function(prop, value){
11392         var record;
11393         if(this.store.getCount() > 0){
11394             this.store.each(function(r){
11395                 if(r.data[prop] == value){
11396                     record = r;
11397                     return false;
11398                 }
11399                 return true;
11400             });
11401         }
11402         return record;
11403     },
11404     
11405     getName: function()
11406     {
11407         // returns hidden if it's set..
11408         if (!this.rendered) {return ''};
11409         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11410         
11411     },
11412     // private
11413     onViewMove : function(e, t){
11414         this.inKeyMode = false;
11415     },
11416
11417     // private
11418     onViewOver : function(e, t){
11419         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11420             return;
11421         }
11422         var item = this.view.findItemFromChild(t);
11423         
11424         if(item){
11425             var index = this.view.indexOf(item);
11426             this.select(index, false);
11427         }
11428     },
11429
11430     // private
11431     onViewClick : function(view, doFocus, el, e)
11432     {
11433         var index = this.view.getSelectedIndexes()[0];
11434         
11435         var r = this.store.getAt(index);
11436         
11437         if(this.tickable){
11438             
11439             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11440                 return;
11441             }
11442             
11443             var rm = false;
11444             var _this = this;
11445             
11446             Roo.each(this.tickItems, function(v,k){
11447                 
11448                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11449                     _this.tickItems.splice(k, 1);
11450                     rm = true;
11451                     return;
11452                 }
11453             })
11454             
11455             if(rm){
11456                 return;
11457             }
11458             
11459             this.tickItems.push(r.data);
11460             return;
11461         }
11462         
11463         if(r){
11464             this.onSelect(r, index);
11465         }
11466         if(doFocus !== false && !this.blockFocus){
11467             this.inputEl().focus();
11468         }
11469     },
11470
11471     // private
11472     restrictHeight : function(){
11473         //this.innerList.dom.style.height = '';
11474         //var inner = this.innerList.dom;
11475         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11476         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11477         //this.list.beginUpdate();
11478         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11479         this.list.alignTo(this.inputEl(), this.listAlign);
11480         this.list.alignTo(this.inputEl(), this.listAlign);
11481         //this.list.endUpdate();
11482     },
11483
11484     // private
11485     onEmptyResults : function(){
11486         this.collapse();
11487     },
11488
11489     /**
11490      * Returns true if the dropdown list is expanded, else false.
11491      */
11492     isExpanded : function(){
11493         return this.list.isVisible();
11494     },
11495
11496     /**
11497      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11498      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11499      * @param {String} value The data value of the item to select
11500      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11501      * selected item if it is not currently in view (defaults to true)
11502      * @return {Boolean} True if the value matched an item in the list, else false
11503      */
11504     selectByValue : function(v, scrollIntoView){
11505         if(v !== undefined && v !== null){
11506             var r = this.findRecord(this.valueField || this.displayField, v);
11507             if(r){
11508                 this.select(this.store.indexOf(r), scrollIntoView);
11509                 return true;
11510             }
11511         }
11512         return false;
11513     },
11514
11515     /**
11516      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11517      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11518      * @param {Number} index The zero-based index of the list item to select
11519      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11520      * selected item if it is not currently in view (defaults to true)
11521      */
11522     select : function(index, scrollIntoView){
11523         this.selectedIndex = index;
11524         this.view.select(index);
11525         if(scrollIntoView !== false){
11526             var el = this.view.getNode(index);
11527             if(el && !this.multiple && !this.tickable){
11528                 this.list.scrollChildIntoView(el, false);
11529             }
11530         }
11531     },
11532
11533     // private
11534     selectNext : function(){
11535         var ct = this.store.getCount();
11536         if(ct > 0){
11537             if(this.selectedIndex == -1){
11538                 this.select(0);
11539             }else if(this.selectedIndex < ct-1){
11540                 this.select(this.selectedIndex+1);
11541             }
11542         }
11543     },
11544
11545     // private
11546     selectPrev : function(){
11547         var ct = this.store.getCount();
11548         if(ct > 0){
11549             if(this.selectedIndex == -1){
11550                 this.select(0);
11551             }else if(this.selectedIndex != 0){
11552                 this.select(this.selectedIndex-1);
11553             }
11554         }
11555     },
11556
11557     // private
11558     onKeyUp : function(e){
11559         if(this.editable !== false && !e.isSpecialKey()){
11560             this.lastKey = e.getKey();
11561             this.dqTask.delay(this.queryDelay);
11562         }
11563     },
11564
11565     // private
11566     validateBlur : function(){
11567         return !this.list || !this.list.isVisible();   
11568     },
11569
11570     // private
11571     initQuery : function(){
11572         this.doQuery(this.getRawValue());
11573     },
11574
11575     // private
11576     doForce : function(){
11577         if(this.inputEl().dom.value.length > 0){
11578             this.inputEl().dom.value =
11579                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11580              
11581         }
11582     },
11583
11584     /**
11585      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11586      * query allowing the query action to be canceled if needed.
11587      * @param {String} query The SQL query to execute
11588      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11589      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11590      * saved in the current store (defaults to false)
11591      */
11592     doQuery : function(q, forceAll){
11593         
11594         if(q === undefined || q === null){
11595             q = '';
11596         }
11597         var qe = {
11598             query: q,
11599             forceAll: forceAll,
11600             combo: this,
11601             cancel:false
11602         };
11603         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11604             return false;
11605         }
11606         q = qe.query;
11607         
11608         forceAll = qe.forceAll;
11609         if(forceAll === true || (q.length >= this.minChars)){
11610             
11611             this.hasQuery = true;
11612             
11613             if(this.lastQuery != q || this.alwaysQuery){
11614                 this.lastQuery = q;
11615                 if(this.mode == 'local'){
11616                     this.selectedIndex = -1;
11617                     if(forceAll){
11618                         this.store.clearFilter();
11619                     }else{
11620                         this.store.filter(this.displayField, q);
11621                     }
11622                     this.onLoad();
11623                 }else{
11624                     this.store.baseParams[this.queryParam] = q;
11625                     
11626                     var options = {params : this.getParams(q)};
11627                     
11628                     if(this.loadNext){
11629                         options.add = true;
11630                         options.params.start = this.page * this.pageSize;
11631                     }
11632                     
11633                     this.store.load(options);
11634                     /*
11635                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11636                      *  we should expand the list on onLoad
11637                      *  so command out it
11638                      */
11639 //                    this.expand();
11640                 }
11641             }else{
11642                 this.selectedIndex = -1;
11643                 this.onLoad();   
11644             }
11645         }
11646         
11647         this.loadNext = false;
11648     },
11649
11650     // private
11651     getParams : function(q){
11652         var p = {};
11653         //p[this.queryParam] = q;
11654         
11655         if(this.pageSize){
11656             p.start = 0;
11657             p.limit = this.pageSize;
11658         }
11659         return p;
11660     },
11661
11662     /**
11663      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11664      */
11665     collapse : function(){
11666         if(!this.isExpanded()){
11667             return;
11668         }
11669         
11670         this.list.hide();
11671         
11672         if(this.tickable){
11673             this.okBtn.hide();
11674             this.cancelBtn.hide();
11675             this.trigger.show();
11676         }
11677         
11678         Roo.get(document).un('mousedown', this.collapseIf, this);
11679         Roo.get(document).un('mousewheel', this.collapseIf, this);
11680         if (!this.editable) {
11681             Roo.get(document).un('keydown', this.listKeyPress, this);
11682         }
11683         this.fireEvent('collapse', this);
11684     },
11685
11686     // private
11687     collapseIf : function(e){
11688         var in_combo  = e.within(this.el);
11689         var in_list =  e.within(this.list);
11690         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11691         
11692         if (in_combo || in_list || is_list) {
11693             //e.stopPropagation();
11694             return;
11695         }
11696         
11697         if(this.tickable){
11698             this.onTickableFooterButtonClick(e, false, false);
11699         }
11700
11701         this.collapse();
11702         
11703     },
11704
11705     /**
11706      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11707      */
11708     expand : function(){
11709        
11710         if(this.isExpanded() || !this.hasFocus){
11711             return;
11712         }
11713         
11714         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11715         this.list.setWidth(lw);
11716         
11717         
11718          Roo.log('expand');
11719         
11720         this.list.show();
11721         
11722         this.restrictHeight();
11723         
11724         if(this.tickable){
11725             
11726             this.tickItems = Roo.apply([], this.item);
11727             
11728             this.okBtn.show();
11729             this.cancelBtn.show();
11730             this.trigger.hide();
11731             
11732         }
11733         
11734         Roo.get(document).on('mousedown', this.collapseIf, this);
11735         Roo.get(document).on('mousewheel', this.collapseIf, this);
11736         if (!this.editable) {
11737             Roo.get(document).on('keydown', this.listKeyPress, this);
11738         }
11739         
11740         this.fireEvent('expand', this);
11741     },
11742
11743     // private
11744     // Implements the default empty TriggerField.onTriggerClick function
11745     onTriggerClick : function(e)
11746     {
11747         Roo.log('trigger click');
11748         
11749         if(this.disabled || !this.triggerList){
11750             return;
11751         }
11752         
11753         this.page = 0;
11754         this.loadNext = false;
11755         
11756         if(this.isExpanded()){
11757             this.collapse();
11758             if (!this.blockFocus) {
11759                 this.inputEl().focus();
11760             }
11761             
11762         }else {
11763             this.hasFocus = true;
11764             if(this.triggerAction == 'all') {
11765                 this.doQuery(this.allQuery, true);
11766             } else {
11767                 this.doQuery(this.getRawValue());
11768             }
11769             if (!this.blockFocus) {
11770                 this.inputEl().focus();
11771             }
11772         }
11773     },
11774     
11775     onTickableTriggerClick : function(e)
11776     {
11777         if(this.disabled){
11778             return;
11779         }
11780         
11781         this.page = 0;
11782         this.loadNext = false;
11783         this.hasFocus = true;
11784         
11785         if(this.triggerAction == 'all') {
11786             this.doQuery(this.allQuery, true);
11787         } else {
11788             this.doQuery(this.getRawValue());
11789         }
11790     },
11791     
11792     onSearchFieldClick : function(e)
11793     {
11794         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11795             return;
11796         }
11797         
11798         this.page = 0;
11799         this.loadNext = false;
11800         this.hasFocus = true;
11801         
11802         if(this.triggerAction == 'all') {
11803             this.doQuery(this.allQuery, true);
11804         } else {
11805             this.doQuery(this.getRawValue());
11806         }
11807     },
11808     
11809     listKeyPress : function(e)
11810     {
11811         //Roo.log('listkeypress');
11812         // scroll to first matching element based on key pres..
11813         if (e.isSpecialKey()) {
11814             return false;
11815         }
11816         var k = String.fromCharCode(e.getKey()).toUpperCase();
11817         //Roo.log(k);
11818         var match  = false;
11819         var csel = this.view.getSelectedNodes();
11820         var cselitem = false;
11821         if (csel.length) {
11822             var ix = this.view.indexOf(csel[0]);
11823             cselitem  = this.store.getAt(ix);
11824             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11825                 cselitem = false;
11826             }
11827             
11828         }
11829         
11830         this.store.each(function(v) { 
11831             if (cselitem) {
11832                 // start at existing selection.
11833                 if (cselitem.id == v.id) {
11834                     cselitem = false;
11835                 }
11836                 return true;
11837             }
11838                 
11839             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11840                 match = this.store.indexOf(v);
11841                 return false;
11842             }
11843             return true;
11844         }, this);
11845         
11846         if (match === false) {
11847             return true; // no more action?
11848         }
11849         // scroll to?
11850         this.view.select(match);
11851         var sn = Roo.get(this.view.getSelectedNodes()[0])
11852         sn.scrollIntoView(sn.dom.parentNode, false);
11853     },
11854     
11855     onViewScroll : function(e, t){
11856         
11857         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){
11858             return;
11859         }
11860         
11861         this.hasQuery = true;
11862         
11863         this.loading = this.list.select('.loading', true).first();
11864         
11865         if(this.loading === null){
11866             this.list.createChild({
11867                 tag: 'div',
11868                 cls: 'loading select2-more-results select2-active',
11869                 html: 'Loading more results...'
11870             })
11871             
11872             this.loading = this.list.select('.loading', true).first();
11873             
11874             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11875             
11876             this.loading.hide();
11877         }
11878         
11879         this.loading.show();
11880         
11881         var _combo = this;
11882         
11883         this.page++;
11884         this.loadNext = true;
11885         
11886         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11887         
11888         return;
11889     },
11890     
11891     addItem : function(o)
11892     {   
11893         var dv = ''; // display value
11894         
11895         if (this.displayField) {
11896             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11897         } else {
11898             // this is an error condition!!!
11899             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11900         }
11901         
11902         if(!dv.length){
11903             return;
11904         }
11905         
11906         var choice = this.choices.createChild({
11907             tag: 'li',
11908             cls: 'select2-search-choice',
11909             cn: [
11910                 {
11911                     tag: 'div',
11912                     html: dv
11913                 },
11914                 {
11915                     tag: 'a',
11916                     href: '#',
11917                     cls: 'select2-search-choice-close',
11918                     tabindex: '-1'
11919                 }
11920             ]
11921             
11922         }, this.searchField);
11923         
11924         var close = choice.select('a.select2-search-choice-close', true).first()
11925         
11926         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11927         
11928         this.item.push(o);
11929         
11930         this.lastData = o;
11931         
11932         this.syncValue();
11933         
11934         this.inputEl().dom.value = '';
11935         
11936     },
11937     
11938     onRemoveItem : function(e, _self, o)
11939     {
11940         e.preventDefault();
11941         var index = this.item.indexOf(o.data) * 1;
11942         
11943         if( index < 0){
11944             Roo.log('not this item?!');
11945             return;
11946         }
11947         
11948         this.item.splice(index, 1);
11949         o.item.remove();
11950         
11951         this.syncValue();
11952         
11953         this.fireEvent('remove', this, e);
11954         
11955     },
11956     
11957     syncValue : function()
11958     {
11959         if(!this.item.length){
11960             this.clearValue();
11961             return;
11962         }
11963             
11964         var value = [];
11965         var _this = this;
11966         Roo.each(this.item, function(i){
11967             if(_this.valueField){
11968                 value.push(i[_this.valueField]);
11969                 return;
11970             }
11971
11972             value.push(i);
11973         });
11974
11975         this.value = value.join(',');
11976
11977         if(this.hiddenField){
11978             this.hiddenField.dom.value = this.value;
11979         }
11980     },
11981     
11982     clearItem : function()
11983     {
11984         if(!this.multiple){
11985             return;
11986         }
11987         
11988         this.item = [];
11989         
11990         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11991            c.remove();
11992         });
11993         
11994         this.syncValue();
11995     },
11996     
11997     inputEl: function ()
11998     {
11999         if(this.tickable){
12000             return this.searchField;
12001         }
12002         return this.el.select('input.form-control',true).first();
12003     },
12004     
12005     
12006     onTickableFooterButtonClick : function(e, btn, el)
12007     {
12008         e.preventDefault();
12009         
12010         if(btn && btn.name == 'cancel'){
12011             this.tickItems = Roo.apply([], this.item);
12012             this.collapse();
12013             return;
12014         }
12015         
12016         this.clearItem();
12017         
12018         var _this = this;
12019         
12020         Roo.each(this.tickItems, function(o){
12021             _this.addItem(o);
12022         });
12023         
12024         this.collapse();
12025         
12026     },
12027     
12028     validate : function()
12029     {
12030         var v = this.getRawValue();
12031         
12032         if(this.multiple){
12033             v = this.getValue();
12034         }
12035         
12036         if(this.disabled || this.validateValue(v)){
12037             this.clearInvalid();
12038             return true;
12039         }
12040         return false;
12041     }
12042     
12043     
12044
12045     /** 
12046     * @cfg {Boolean} grow 
12047     * @hide 
12048     */
12049     /** 
12050     * @cfg {Number} growMin 
12051     * @hide 
12052     */
12053     /** 
12054     * @cfg {Number} growMax 
12055     * @hide 
12056     */
12057     /**
12058      * @hide
12059      * @method autoSize
12060      */
12061 });
12062 /*
12063  * Based on:
12064  * Ext JS Library 1.1.1
12065  * Copyright(c) 2006-2007, Ext JS, LLC.
12066  *
12067  * Originally Released Under LGPL - original licence link has changed is not relivant.
12068  *
12069  * Fork - LGPL
12070  * <script type="text/javascript">
12071  */
12072
12073 /**
12074  * @class Roo.View
12075  * @extends Roo.util.Observable
12076  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12077  * This class also supports single and multi selection modes. <br>
12078  * Create a data model bound view:
12079  <pre><code>
12080  var store = new Roo.data.Store(...);
12081
12082  var view = new Roo.View({
12083     el : "my-element",
12084     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12085  
12086     singleSelect: true,
12087     selectedClass: "ydataview-selected",
12088     store: store
12089  });
12090
12091  // listen for node click?
12092  view.on("click", function(vw, index, node, e){
12093  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12094  });
12095
12096  // load XML data
12097  dataModel.load("foobar.xml");
12098  </code></pre>
12099  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12100  * <br><br>
12101  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12102  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12103  * 
12104  * Note: old style constructor is still suported (container, template, config)
12105  * 
12106  * @constructor
12107  * Create a new View
12108  * @param {Object} config The config object
12109  * 
12110  */
12111 Roo.View = function(config, depreciated_tpl, depreciated_config){
12112     
12113     this.parent = false;
12114     
12115     if (typeof(depreciated_tpl) == 'undefined') {
12116         // new way.. - universal constructor.
12117         Roo.apply(this, config);
12118         this.el  = Roo.get(this.el);
12119     } else {
12120         // old format..
12121         this.el  = Roo.get(config);
12122         this.tpl = depreciated_tpl;
12123         Roo.apply(this, depreciated_config);
12124     }
12125     this.wrapEl  = this.el.wrap().wrap();
12126     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12127     
12128     
12129     if(typeof(this.tpl) == "string"){
12130         this.tpl = new Roo.Template(this.tpl);
12131     } else {
12132         // support xtype ctors..
12133         this.tpl = new Roo.factory(this.tpl, Roo);
12134     }
12135     
12136     
12137     this.tpl.compile();
12138     
12139     /** @private */
12140     this.addEvents({
12141         /**
12142          * @event beforeclick
12143          * Fires before a click is processed. Returns false to cancel the default action.
12144          * @param {Roo.View} this
12145          * @param {Number} index The index of the target node
12146          * @param {HTMLElement} node The target node
12147          * @param {Roo.EventObject} e The raw event object
12148          */
12149             "beforeclick" : true,
12150         /**
12151          * @event click
12152          * Fires when a template node is clicked.
12153          * @param {Roo.View} this
12154          * @param {Number} index The index of the target node
12155          * @param {HTMLElement} node The target node
12156          * @param {Roo.EventObject} e The raw event object
12157          */
12158             "click" : true,
12159         /**
12160          * @event dblclick
12161          * Fires when a template node is double clicked.
12162          * @param {Roo.View} this
12163          * @param {Number} index The index of the target node
12164          * @param {HTMLElement} node The target node
12165          * @param {Roo.EventObject} e The raw event object
12166          */
12167             "dblclick" : true,
12168         /**
12169          * @event contextmenu
12170          * Fires when a template node is right clicked.
12171          * @param {Roo.View} this
12172          * @param {Number} index The index of the target node
12173          * @param {HTMLElement} node The target node
12174          * @param {Roo.EventObject} e The raw event object
12175          */
12176             "contextmenu" : true,
12177         /**
12178          * @event selectionchange
12179          * Fires when the selected nodes change.
12180          * @param {Roo.View} this
12181          * @param {Array} selections Array of the selected nodes
12182          */
12183             "selectionchange" : true,
12184     
12185         /**
12186          * @event beforeselect
12187          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12188          * @param {Roo.View} this
12189          * @param {HTMLElement} node The node to be selected
12190          * @param {Array} selections Array of currently selected nodes
12191          */
12192             "beforeselect" : true,
12193         /**
12194          * @event preparedata
12195          * Fires on every row to render, to allow you to change the data.
12196          * @param {Roo.View} this
12197          * @param {Object} data to be rendered (change this)
12198          */
12199           "preparedata" : true
12200           
12201           
12202         });
12203
12204
12205
12206     this.el.on({
12207         "click": this.onClick,
12208         "dblclick": this.onDblClick,
12209         "contextmenu": this.onContextMenu,
12210         scope:this
12211     });
12212
12213     this.selections = [];
12214     this.nodes = [];
12215     this.cmp = new Roo.CompositeElementLite([]);
12216     if(this.store){
12217         this.store = Roo.factory(this.store, Roo.data);
12218         this.setStore(this.store, true);
12219     }
12220     
12221     if ( this.footer && this.footer.xtype) {
12222            
12223          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12224         
12225         this.footer.dataSource = this.store
12226         this.footer.container = fctr;
12227         this.footer = Roo.factory(this.footer, Roo);
12228         fctr.insertFirst(this.el);
12229         
12230         // this is a bit insane - as the paging toolbar seems to detach the el..
12231 //        dom.parentNode.parentNode.parentNode
12232          // they get detached?
12233     }
12234     
12235     
12236     Roo.View.superclass.constructor.call(this);
12237     
12238     
12239 };
12240
12241 Roo.extend(Roo.View, Roo.util.Observable, {
12242     
12243      /**
12244      * @cfg {Roo.data.Store} store Data store to load data from.
12245      */
12246     store : false,
12247     
12248     /**
12249      * @cfg {String|Roo.Element} el The container element.
12250      */
12251     el : '',
12252     
12253     /**
12254      * @cfg {String|Roo.Template} tpl The template used by this View 
12255      */
12256     tpl : false,
12257     /**
12258      * @cfg {String} dataName the named area of the template to use as the data area
12259      *                          Works with domtemplates roo-name="name"
12260      */
12261     dataName: false,
12262     /**
12263      * @cfg {String} selectedClass The css class to add to selected nodes
12264      */
12265     selectedClass : "x-view-selected",
12266      /**
12267      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12268      */
12269     emptyText : "",
12270     
12271     /**
12272      * @cfg {String} text to display on mask (default Loading)
12273      */
12274     mask : false,
12275     /**
12276      * @cfg {Boolean} multiSelect Allow multiple selection
12277      */
12278     multiSelect : false,
12279     /**
12280      * @cfg {Boolean} singleSelect Allow single selection
12281      */
12282     singleSelect:  false,
12283     
12284     /**
12285      * @cfg {Boolean} toggleSelect - selecting 
12286      */
12287     toggleSelect : false,
12288     
12289     /**
12290      * @cfg {Boolean} tickable - selecting 
12291      */
12292     tickable : false,
12293     
12294     /**
12295      * Returns the element this view is bound to.
12296      * @return {Roo.Element}
12297      */
12298     getEl : function(){
12299         return this.wrapEl;
12300     },
12301     
12302     
12303
12304     /**
12305      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12306      */
12307     refresh : function(){
12308         Roo.log('refresh');
12309         var t = this.tpl;
12310         
12311         // if we are using something like 'domtemplate', then
12312         // the what gets used is:
12313         // t.applySubtemplate(NAME, data, wrapping data..)
12314         // the outer template then get' applied with
12315         //     the store 'extra data'
12316         // and the body get's added to the
12317         //      roo-name="data" node?
12318         //      <span class='roo-tpl-{name}'></span> ?????
12319         
12320         
12321         
12322         this.clearSelections();
12323         this.el.update("");
12324         var html = [];
12325         var records = this.store.getRange();
12326         if(records.length < 1) {
12327             
12328             // is this valid??  = should it render a template??
12329             
12330             this.el.update(this.emptyText);
12331             return;
12332         }
12333         var el = this.el;
12334         if (this.dataName) {
12335             this.el.update(t.apply(this.store.meta)); //????
12336             el = this.el.child('.roo-tpl-' + this.dataName);
12337         }
12338         
12339         for(var i = 0, len = records.length; i < len; i++){
12340             var data = this.prepareData(records[i].data, i, records[i]);
12341             this.fireEvent("preparedata", this, data, i, records[i]);
12342             
12343             var d = Roo.apply({}, data);
12344             
12345             if(this.tickable){
12346                 Roo.apply(d, {'roo-id' : Roo.id()});
12347                 
12348                 var _this = this;
12349             
12350                 Roo.each(this.parent.item, function(item){
12351                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12352                         return;
12353                     }
12354                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12355                 });
12356             }
12357             
12358             html[html.length] = Roo.util.Format.trim(
12359                 this.dataName ?
12360                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12361                     t.apply(d)
12362             );
12363         }
12364         
12365         
12366         
12367         el.update(html.join(""));
12368         this.nodes = el.dom.childNodes;
12369         this.updateIndexes(0);
12370     },
12371     
12372
12373     /**
12374      * Function to override to reformat the data that is sent to
12375      * the template for each node.
12376      * DEPRICATED - use the preparedata event handler.
12377      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12378      * a JSON object for an UpdateManager bound view).
12379      */
12380     prepareData : function(data, index, record)
12381     {
12382         this.fireEvent("preparedata", this, data, index, record);
12383         return data;
12384     },
12385
12386     onUpdate : function(ds, record){
12387          Roo.log('on update');   
12388         this.clearSelections();
12389         var index = this.store.indexOf(record);
12390         var n = this.nodes[index];
12391         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12392         n.parentNode.removeChild(n);
12393         this.updateIndexes(index, index);
12394     },
12395
12396     
12397     
12398 // --------- FIXME     
12399     onAdd : function(ds, records, index)
12400     {
12401         Roo.log(['on Add', ds, records, index] );        
12402         this.clearSelections();
12403         if(this.nodes.length == 0){
12404             this.refresh();
12405             return;
12406         }
12407         var n = this.nodes[index];
12408         for(var i = 0, len = records.length; i < len; i++){
12409             var d = this.prepareData(records[i].data, i, records[i]);
12410             if(n){
12411                 this.tpl.insertBefore(n, d);
12412             }else{
12413                 
12414                 this.tpl.append(this.el, d);
12415             }
12416         }
12417         this.updateIndexes(index);
12418     },
12419
12420     onRemove : function(ds, record, index){
12421         Roo.log('onRemove');
12422         this.clearSelections();
12423         var el = this.dataName  ?
12424             this.el.child('.roo-tpl-' + this.dataName) :
12425             this.el; 
12426         
12427         el.dom.removeChild(this.nodes[index]);
12428         this.updateIndexes(index);
12429     },
12430
12431     /**
12432      * Refresh an individual node.
12433      * @param {Number} index
12434      */
12435     refreshNode : function(index){
12436         this.onUpdate(this.store, this.store.getAt(index));
12437     },
12438
12439     updateIndexes : function(startIndex, endIndex){
12440         var ns = this.nodes;
12441         startIndex = startIndex || 0;
12442         endIndex = endIndex || ns.length - 1;
12443         for(var i = startIndex; i <= endIndex; i++){
12444             ns[i].nodeIndex = i;
12445         }
12446     },
12447
12448     /**
12449      * Changes the data store this view uses and refresh the view.
12450      * @param {Store} store
12451      */
12452     setStore : function(store, initial){
12453         if(!initial && this.store){
12454             this.store.un("datachanged", this.refresh);
12455             this.store.un("add", this.onAdd);
12456             this.store.un("remove", this.onRemove);
12457             this.store.un("update", this.onUpdate);
12458             this.store.un("clear", this.refresh);
12459             this.store.un("beforeload", this.onBeforeLoad);
12460             this.store.un("load", this.onLoad);
12461             this.store.un("loadexception", this.onLoad);
12462         }
12463         if(store){
12464           
12465             store.on("datachanged", this.refresh, this);
12466             store.on("add", this.onAdd, this);
12467             store.on("remove", this.onRemove, this);
12468             store.on("update", this.onUpdate, this);
12469             store.on("clear", this.refresh, this);
12470             store.on("beforeload", this.onBeforeLoad, this);
12471             store.on("load", this.onLoad, this);
12472             store.on("loadexception", this.onLoad, this);
12473         }
12474         
12475         if(store){
12476             this.refresh();
12477         }
12478     },
12479     /**
12480      * onbeforeLoad - masks the loading area.
12481      *
12482      */
12483     onBeforeLoad : function(store,opts)
12484     {
12485          Roo.log('onBeforeLoad');   
12486         if (!opts.add) {
12487             this.el.update("");
12488         }
12489         this.el.mask(this.mask ? this.mask : "Loading" ); 
12490     },
12491     onLoad : function ()
12492     {
12493         this.el.unmask();
12494     },
12495     
12496
12497     /**
12498      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12499      * @param {HTMLElement} node
12500      * @return {HTMLElement} The template node
12501      */
12502     findItemFromChild : function(node){
12503         var el = this.dataName  ?
12504             this.el.child('.roo-tpl-' + this.dataName,true) :
12505             this.el.dom; 
12506         
12507         if(!node || node.parentNode == el){
12508                     return node;
12509             }
12510             var p = node.parentNode;
12511             while(p && p != el){
12512             if(p.parentNode == el){
12513                 return p;
12514             }
12515             p = p.parentNode;
12516         }
12517             return null;
12518     },
12519
12520     /** @ignore */
12521     onClick : function(e){
12522         var item = this.findItemFromChild(e.getTarget());
12523         if(item){
12524             var index = this.indexOf(item);
12525             if(this.onItemClick(item, index, e) !== false){
12526                 this.fireEvent("click", this, index, item, e);
12527             }
12528         }else{
12529             this.clearSelections();
12530         }
12531     },
12532
12533     /** @ignore */
12534     onContextMenu : function(e){
12535         var item = this.findItemFromChild(e.getTarget());
12536         if(item){
12537             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12538         }
12539     },
12540
12541     /** @ignore */
12542     onDblClick : function(e){
12543         var item = this.findItemFromChild(e.getTarget());
12544         if(item){
12545             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12546         }
12547     },
12548
12549     onItemClick : function(item, index, e)
12550     {
12551         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12552             return false;
12553         }
12554         if (this.toggleSelect) {
12555             var m = this.isSelected(item) ? 'unselect' : 'select';
12556             Roo.log(m);
12557             var _t = this;
12558             _t[m](item, true, false);
12559             return true;
12560         }
12561         if(this.multiSelect || this.singleSelect){
12562             if(this.multiSelect && e.shiftKey && this.lastSelection){
12563                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12564             }else{
12565                 this.select(item, this.multiSelect && e.ctrlKey);
12566                 this.lastSelection = item;
12567             }
12568             
12569             if(!this.tickable){
12570                 e.preventDefault();
12571             }
12572             
12573         }
12574         return true;
12575     },
12576
12577     /**
12578      * Get the number of selected nodes.
12579      * @return {Number}
12580      */
12581     getSelectionCount : function(){
12582         return this.selections.length;
12583     },
12584
12585     /**
12586      * Get the currently selected nodes.
12587      * @return {Array} An array of HTMLElements
12588      */
12589     getSelectedNodes : function(){
12590         return this.selections;
12591     },
12592
12593     /**
12594      * Get the indexes of the selected nodes.
12595      * @return {Array}
12596      */
12597     getSelectedIndexes : function(){
12598         var indexes = [], s = this.selections;
12599         for(var i = 0, len = s.length; i < len; i++){
12600             indexes.push(s[i].nodeIndex);
12601         }
12602         return indexes;
12603     },
12604
12605     /**
12606      * Clear all selections
12607      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12608      */
12609     clearSelections : function(suppressEvent){
12610         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12611             this.cmp.elements = this.selections;
12612             this.cmp.removeClass(this.selectedClass);
12613             this.selections = [];
12614             if(!suppressEvent){
12615                 this.fireEvent("selectionchange", this, this.selections);
12616             }
12617         }
12618     },
12619
12620     /**
12621      * Returns true if the passed node is selected
12622      * @param {HTMLElement/Number} node The node or node index
12623      * @return {Boolean}
12624      */
12625     isSelected : function(node){
12626         var s = this.selections;
12627         if(s.length < 1){
12628             return false;
12629         }
12630         node = this.getNode(node);
12631         return s.indexOf(node) !== -1;
12632     },
12633
12634     /**
12635      * Selects nodes.
12636      * @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
12637      * @param {Boolean} keepExisting (optional) true to keep existing selections
12638      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12639      */
12640     select : function(nodeInfo, keepExisting, suppressEvent){
12641         if(nodeInfo instanceof Array){
12642             if(!keepExisting){
12643                 this.clearSelections(true);
12644             }
12645             for(var i = 0, len = nodeInfo.length; i < len; i++){
12646                 this.select(nodeInfo[i], true, true);
12647             }
12648             return;
12649         } 
12650         var node = this.getNode(nodeInfo);
12651         if(!node || this.isSelected(node)){
12652             return; // already selected.
12653         }
12654         if(!keepExisting){
12655             this.clearSelections(true);
12656         }
12657         
12658         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12659             Roo.fly(node).addClass(this.selectedClass);
12660             this.selections.push(node);
12661             if(!suppressEvent){
12662                 this.fireEvent("selectionchange", this, this.selections);
12663             }
12664         }
12665         
12666         
12667     },
12668       /**
12669      * Unselects nodes.
12670      * @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
12671      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12672      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12673      */
12674     unselect : function(nodeInfo, keepExisting, suppressEvent)
12675     {
12676         if(nodeInfo instanceof Array){
12677             Roo.each(this.selections, function(s) {
12678                 this.unselect(s, nodeInfo);
12679             }, this);
12680             return;
12681         }
12682         var node = this.getNode(nodeInfo);
12683         if(!node || !this.isSelected(node)){
12684             Roo.log("not selected");
12685             return; // not selected.
12686         }
12687         // fireevent???
12688         var ns = [];
12689         Roo.each(this.selections, function(s) {
12690             if (s == node ) {
12691                 Roo.fly(node).removeClass(this.selectedClass);
12692
12693                 return;
12694             }
12695             ns.push(s);
12696         },this);
12697         
12698         this.selections= ns;
12699         this.fireEvent("selectionchange", this, this.selections);
12700     },
12701
12702     /**
12703      * Gets a template node.
12704      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12705      * @return {HTMLElement} The node or null if it wasn't found
12706      */
12707     getNode : function(nodeInfo){
12708         if(typeof nodeInfo == "string"){
12709             return document.getElementById(nodeInfo);
12710         }else if(typeof nodeInfo == "number"){
12711             return this.nodes[nodeInfo];
12712         }
12713         return nodeInfo;
12714     },
12715
12716     /**
12717      * Gets a range template nodes.
12718      * @param {Number} startIndex
12719      * @param {Number} endIndex
12720      * @return {Array} An array of nodes
12721      */
12722     getNodes : function(start, end){
12723         var ns = this.nodes;
12724         start = start || 0;
12725         end = typeof end == "undefined" ? ns.length - 1 : end;
12726         var nodes = [];
12727         if(start <= end){
12728             for(var i = start; i <= end; i++){
12729                 nodes.push(ns[i]);
12730             }
12731         } else{
12732             for(var i = start; i >= end; i--){
12733                 nodes.push(ns[i]);
12734             }
12735         }
12736         return nodes;
12737     },
12738
12739     /**
12740      * Finds the index of the passed node
12741      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12742      * @return {Number} The index of the node or -1
12743      */
12744     indexOf : function(node){
12745         node = this.getNode(node);
12746         if(typeof node.nodeIndex == "number"){
12747             return node.nodeIndex;
12748         }
12749         var ns = this.nodes;
12750         for(var i = 0, len = ns.length; i < len; i++){
12751             if(ns[i] == node){
12752                 return i;
12753             }
12754         }
12755         return -1;
12756     }
12757 });
12758 /*
12759  * - LGPL
12760  *
12761  * based on jquery fullcalendar
12762  * 
12763  */
12764
12765 Roo.bootstrap = Roo.bootstrap || {};
12766 /**
12767  * @class Roo.bootstrap.Calendar
12768  * @extends Roo.bootstrap.Component
12769  * Bootstrap Calendar class
12770  * @cfg {Boolean} loadMask (true|false) default false
12771  * @cfg {Object} header generate the user specific header of the calendar, default false
12772
12773  * @constructor
12774  * Create a new Container
12775  * @param {Object} config The config object
12776  */
12777
12778
12779
12780 Roo.bootstrap.Calendar = function(config){
12781     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12782      this.addEvents({
12783         /**
12784              * @event select
12785              * Fires when a date is selected
12786              * @param {DatePicker} this
12787              * @param {Date} date The selected date
12788              */
12789         'select': true,
12790         /**
12791              * @event monthchange
12792              * Fires when the displayed month changes 
12793              * @param {DatePicker} this
12794              * @param {Date} date The selected month
12795              */
12796         'monthchange': true,
12797         /**
12798              * @event evententer
12799              * Fires when mouse over an event
12800              * @param {Calendar} this
12801              * @param {event} Event
12802              */
12803         'evententer': true,
12804         /**
12805              * @event eventleave
12806              * Fires when the mouse leaves an
12807              * @param {Calendar} this
12808              * @param {event}
12809              */
12810         'eventleave': true,
12811         /**
12812              * @event eventclick
12813              * Fires when the mouse click an
12814              * @param {Calendar} this
12815              * @param {event}
12816              */
12817         'eventclick': true
12818         
12819     });
12820
12821 };
12822
12823 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12824     
12825      /**
12826      * @cfg {Number} startDay
12827      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12828      */
12829     startDay : 0,
12830     
12831     loadMask : false,
12832     
12833     header : false,
12834       
12835     getAutoCreate : function(){
12836         
12837         
12838         var fc_button = function(name, corner, style, content ) {
12839             return Roo.apply({},{
12840                 tag : 'span',
12841                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12842                          (corner.length ?
12843                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12844                             ''
12845                         ),
12846                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12847                 unselectable: 'on'
12848             });
12849         };
12850         
12851         var header = {};
12852         
12853         if(!this.header){
12854             header = {
12855                 tag : 'table',
12856                 cls : 'fc-header',
12857                 style : 'width:100%',
12858                 cn : [
12859                     {
12860                         tag: 'tr',
12861                         cn : [
12862                             {
12863                                 tag : 'td',
12864                                 cls : 'fc-header-left',
12865                                 cn : [
12866                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12867                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12868                                     { tag: 'span', cls: 'fc-header-space' },
12869                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12870
12871
12872                                 ]
12873                             },
12874
12875                             {
12876                                 tag : 'td',
12877                                 cls : 'fc-header-center',
12878                                 cn : [
12879                                     {
12880                                         tag: 'span',
12881                                         cls: 'fc-header-title',
12882                                         cn : {
12883                                             tag: 'H2',
12884                                             html : 'month / year'
12885                                         }
12886                                     }
12887
12888                                 ]
12889                             },
12890                             {
12891                                 tag : 'td',
12892                                 cls : 'fc-header-right',
12893                                 cn : [
12894                               /*      fc_button('month', 'left', '', 'month' ),
12895                                     fc_button('week', '', '', 'week' ),
12896                                     fc_button('day', 'right', '', 'day' )
12897                                 */    
12898
12899                                 ]
12900                             }
12901
12902                         ]
12903                     }
12904                 ]
12905             };
12906         }
12907         
12908         header = this.header;
12909         
12910        
12911         var cal_heads = function() {
12912             var ret = [];
12913             // fixme - handle this.
12914             
12915             for (var i =0; i < Date.dayNames.length; i++) {
12916                 var d = Date.dayNames[i];
12917                 ret.push({
12918                     tag: 'th',
12919                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12920                     html : d.substring(0,3)
12921                 });
12922                 
12923             }
12924             ret[0].cls += ' fc-first';
12925             ret[6].cls += ' fc-last';
12926             return ret;
12927         };
12928         var cal_cell = function(n) {
12929             return  {
12930                 tag: 'td',
12931                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12932                 cn : [
12933                     {
12934                         cn : [
12935                             {
12936                                 cls: 'fc-day-number',
12937                                 html: 'D'
12938                             },
12939                             {
12940                                 cls: 'fc-day-content',
12941                              
12942                                 cn : [
12943                                      {
12944                                         style: 'position: relative;' // height: 17px;
12945                                     }
12946                                 ]
12947                             }
12948                             
12949                             
12950                         ]
12951                     }
12952                 ]
12953                 
12954             }
12955         };
12956         var cal_rows = function() {
12957             
12958             var ret = []
12959             for (var r = 0; r < 6; r++) {
12960                 var row= {
12961                     tag : 'tr',
12962                     cls : 'fc-week',
12963                     cn : []
12964                 };
12965                 
12966                 for (var i =0; i < Date.dayNames.length; i++) {
12967                     var d = Date.dayNames[i];
12968                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12969
12970                 }
12971                 row.cn[0].cls+=' fc-first';
12972                 row.cn[0].cn[0].style = 'min-height:90px';
12973                 row.cn[6].cls+=' fc-last';
12974                 ret.push(row);
12975                 
12976             }
12977             ret[0].cls += ' fc-first';
12978             ret[4].cls += ' fc-prev-last';
12979             ret[5].cls += ' fc-last';
12980             return ret;
12981             
12982         };
12983         
12984         var cal_table = {
12985             tag: 'table',
12986             cls: 'fc-border-separate',
12987             style : 'width:100%',
12988             cellspacing  : 0,
12989             cn : [
12990                 { 
12991                     tag: 'thead',
12992                     cn : [
12993                         { 
12994                             tag: 'tr',
12995                             cls : 'fc-first fc-last',
12996                             cn : cal_heads()
12997                         }
12998                     ]
12999                 },
13000                 { 
13001                     tag: 'tbody',
13002                     cn : cal_rows()
13003                 }
13004                   
13005             ]
13006         };
13007          
13008          var cfg = {
13009             cls : 'fc fc-ltr',
13010             cn : [
13011                 header,
13012                 {
13013                     cls : 'fc-content',
13014                     style : "position: relative;",
13015                     cn : [
13016                         {
13017                             cls : 'fc-view fc-view-month fc-grid',
13018                             style : 'position: relative',
13019                             unselectable : 'on',
13020                             cn : [
13021                                 {
13022                                     cls : 'fc-event-container',
13023                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13024                                 },
13025                                 cal_table
13026                             ]
13027                         }
13028                     ]
13029     
13030                 }
13031            ] 
13032             
13033         };
13034         
13035          
13036         
13037         return cfg;
13038     },
13039     
13040     
13041     initEvents : function()
13042     {
13043         if(!this.store){
13044             throw "can not find store for calendar";
13045         }
13046         
13047         var mark = {
13048             tag: "div",
13049             cls:"x-dlg-mask",
13050             style: "text-align:center",
13051             cn: [
13052                 {
13053                     tag: "div",
13054                     style: "background-color:white;width:50%;margin:250 auto",
13055                     cn: [
13056                         {
13057                             tag: "img",
13058                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13059                         },
13060                         {
13061                             tag: "span",
13062                             html: "Loading"
13063                         }
13064                         
13065                     ]
13066                 }
13067             ]
13068         }
13069         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13070         
13071         var size = this.el.select('.fc-content', true).first().getSize();
13072         this.maskEl.setSize(size.width, size.height);
13073         this.maskEl.enableDisplayMode("block");
13074         if(!this.loadMask){
13075             this.maskEl.hide();
13076         }
13077         
13078         this.store = Roo.factory(this.store, Roo.data);
13079         this.store.on('load', this.onLoad, this);
13080         this.store.on('beforeload', this.onBeforeLoad, this);
13081         
13082         this.resize();
13083         
13084         this.cells = this.el.select('.fc-day',true);
13085         //Roo.log(this.cells);
13086         this.textNodes = this.el.query('.fc-day-number');
13087         this.cells.addClassOnOver('fc-state-hover');
13088         
13089         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13090         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13091         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13092         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13093         
13094         this.on('monthchange', this.onMonthChange, this);
13095         
13096         this.update(new Date().clearTime());
13097     },
13098     
13099     resize : function() {
13100         var sz  = this.el.getSize();
13101         
13102         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13103         this.el.select('.fc-day-content div',true).setHeight(34);
13104     },
13105     
13106     
13107     // private
13108     showPrevMonth : function(e){
13109         this.update(this.activeDate.add("mo", -1));
13110     },
13111     showToday : function(e){
13112         this.update(new Date().clearTime());
13113     },
13114     // private
13115     showNextMonth : function(e){
13116         this.update(this.activeDate.add("mo", 1));
13117     },
13118
13119     // private
13120     showPrevYear : function(){
13121         this.update(this.activeDate.add("y", -1));
13122     },
13123
13124     // private
13125     showNextYear : function(){
13126         this.update(this.activeDate.add("y", 1));
13127     },
13128
13129     
13130    // private
13131     update : function(date)
13132     {
13133         var vd = this.activeDate;
13134         this.activeDate = date;
13135 //        if(vd && this.el){
13136 //            var t = date.getTime();
13137 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13138 //                Roo.log('using add remove');
13139 //                
13140 //                this.fireEvent('monthchange', this, date);
13141 //                
13142 //                this.cells.removeClass("fc-state-highlight");
13143 //                this.cells.each(function(c){
13144 //                   if(c.dateValue == t){
13145 //                       c.addClass("fc-state-highlight");
13146 //                       setTimeout(function(){
13147 //                            try{c.dom.firstChild.focus();}catch(e){}
13148 //                       }, 50);
13149 //                       return false;
13150 //                   }
13151 //                   return true;
13152 //                });
13153 //                return;
13154 //            }
13155 //        }
13156         
13157         var days = date.getDaysInMonth();
13158         
13159         var firstOfMonth = date.getFirstDateOfMonth();
13160         var startingPos = firstOfMonth.getDay()-this.startDay;
13161         
13162         if(startingPos < this.startDay){
13163             startingPos += 7;
13164         }
13165         
13166         var pm = date.add(Date.MONTH, -1);
13167         var prevStart = pm.getDaysInMonth()-startingPos;
13168 //        
13169         this.cells = this.el.select('.fc-day',true);
13170         this.textNodes = this.el.query('.fc-day-number');
13171         this.cells.addClassOnOver('fc-state-hover');
13172         
13173         var cells = this.cells.elements;
13174         var textEls = this.textNodes;
13175         
13176         Roo.each(cells, function(cell){
13177             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13178         });
13179         
13180         days += startingPos;
13181
13182         // convert everything to numbers so it's fast
13183         var day = 86400000;
13184         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13185         //Roo.log(d);
13186         //Roo.log(pm);
13187         //Roo.log(prevStart);
13188         
13189         var today = new Date().clearTime().getTime();
13190         var sel = date.clearTime().getTime();
13191         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13192         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13193         var ddMatch = this.disabledDatesRE;
13194         var ddText = this.disabledDatesText;
13195         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13196         var ddaysText = this.disabledDaysText;
13197         var format = this.format;
13198         
13199         var setCellClass = function(cal, cell){
13200             cell.row = 0;
13201             cell.events = [];
13202             cell.more = [];
13203             //Roo.log('set Cell Class');
13204             cell.title = "";
13205             var t = d.getTime();
13206             
13207             //Roo.log(d);
13208             
13209             cell.dateValue = t;
13210             if(t == today){
13211                 cell.className += " fc-today";
13212                 cell.className += " fc-state-highlight";
13213                 cell.title = cal.todayText;
13214             }
13215             if(t == sel){
13216                 // disable highlight in other month..
13217                 //cell.className += " fc-state-highlight";
13218                 
13219             }
13220             // disabling
13221             if(t < min) {
13222                 cell.className = " fc-state-disabled";
13223                 cell.title = cal.minText;
13224                 return;
13225             }
13226             if(t > max) {
13227                 cell.className = " fc-state-disabled";
13228                 cell.title = cal.maxText;
13229                 return;
13230             }
13231             if(ddays){
13232                 if(ddays.indexOf(d.getDay()) != -1){
13233                     cell.title = ddaysText;
13234                     cell.className = " fc-state-disabled";
13235                 }
13236             }
13237             if(ddMatch && format){
13238                 var fvalue = d.dateFormat(format);
13239                 if(ddMatch.test(fvalue)){
13240                     cell.title = ddText.replace("%0", fvalue);
13241                     cell.className = " fc-state-disabled";
13242                 }
13243             }
13244             
13245             if (!cell.initialClassName) {
13246                 cell.initialClassName = cell.dom.className;
13247             }
13248             
13249             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13250         };
13251
13252         var i = 0;
13253         
13254         for(; i < startingPos; i++) {
13255             textEls[i].innerHTML = (++prevStart);
13256             d.setDate(d.getDate()+1);
13257             
13258             cells[i].className = "fc-past fc-other-month";
13259             setCellClass(this, cells[i]);
13260         }
13261         
13262         var intDay = 0;
13263         
13264         for(; i < days; i++){
13265             intDay = i - startingPos + 1;
13266             textEls[i].innerHTML = (intDay);
13267             d.setDate(d.getDate()+1);
13268             
13269             cells[i].className = ''; // "x-date-active";
13270             setCellClass(this, cells[i]);
13271         }
13272         var extraDays = 0;
13273         
13274         for(; i < 42; i++) {
13275             textEls[i].innerHTML = (++extraDays);
13276             d.setDate(d.getDate()+1);
13277             
13278             cells[i].className = "fc-future fc-other-month";
13279             setCellClass(this, cells[i]);
13280         }
13281         
13282         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13283         
13284         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13285         
13286         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13287         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13288         
13289         if(totalRows != 6){
13290             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13291             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13292         }
13293         
13294         this.fireEvent('monthchange', this, date);
13295         
13296         
13297         /*
13298         if(!this.internalRender){
13299             var main = this.el.dom.firstChild;
13300             var w = main.offsetWidth;
13301             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13302             Roo.fly(main).setWidth(w);
13303             this.internalRender = true;
13304             // opera does not respect the auto grow header center column
13305             // then, after it gets a width opera refuses to recalculate
13306             // without a second pass
13307             if(Roo.isOpera && !this.secondPass){
13308                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13309                 this.secondPass = true;
13310                 this.update.defer(10, this, [date]);
13311             }
13312         }
13313         */
13314         
13315     },
13316     
13317     findCell : function(dt) {
13318         dt = dt.clearTime().getTime();
13319         var ret = false;
13320         this.cells.each(function(c){
13321             //Roo.log("check " +c.dateValue + '?=' + dt);
13322             if(c.dateValue == dt){
13323                 ret = c;
13324                 return false;
13325             }
13326             return true;
13327         });
13328         
13329         return ret;
13330     },
13331     
13332     findCells : function(ev) {
13333         var s = ev.start.clone().clearTime().getTime();
13334        // Roo.log(s);
13335         var e= ev.end.clone().clearTime().getTime();
13336        // Roo.log(e);
13337         var ret = [];
13338         this.cells.each(function(c){
13339              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13340             
13341             if(c.dateValue > e){
13342                 return ;
13343             }
13344             if(c.dateValue < s){
13345                 return ;
13346             }
13347             ret.push(c);
13348         });
13349         
13350         return ret;    
13351     },
13352     
13353 //    findBestRow: function(cells)
13354 //    {
13355 //        var ret = 0;
13356 //        
13357 //        for (var i =0 ; i < cells.length;i++) {
13358 //            ret  = Math.max(cells[i].rows || 0,ret);
13359 //        }
13360 //        return ret;
13361 //        
13362 //    },
13363     
13364     
13365     addItem : function(ev)
13366     {
13367         // look for vertical location slot in
13368         var cells = this.findCells(ev);
13369         
13370 //        ev.row = this.findBestRow(cells);
13371         
13372         // work out the location.
13373         
13374         var crow = false;
13375         var rows = [];
13376         for(var i =0; i < cells.length; i++) {
13377             
13378             cells[i].row = cells[0].row;
13379             
13380             if(i == 0){
13381                 cells[i].row = cells[i].row + 1;
13382             }
13383             
13384             if (!crow) {
13385                 crow = {
13386                     start : cells[i],
13387                     end :  cells[i]
13388                 };
13389                 continue;
13390             }
13391             if (crow.start.getY() == cells[i].getY()) {
13392                 // on same row.
13393                 crow.end = cells[i];
13394                 continue;
13395             }
13396             // different row.
13397             rows.push(crow);
13398             crow = {
13399                 start: cells[i],
13400                 end : cells[i]
13401             };
13402             
13403         }
13404         
13405         rows.push(crow);
13406         ev.els = [];
13407         ev.rows = rows;
13408         ev.cells = cells;
13409         
13410         cells[0].events.push(ev);
13411         
13412         this.calevents.push(ev);
13413     },
13414     
13415     clearEvents: function() {
13416         
13417         if(!this.calevents){
13418             return;
13419         }
13420         
13421         Roo.each(this.cells.elements, function(c){
13422             c.row = 0;
13423             c.events = [];
13424             c.more = [];
13425         });
13426         
13427         Roo.each(this.calevents, function(e) {
13428             Roo.each(e.els, function(el) {
13429                 el.un('mouseenter' ,this.onEventEnter, this);
13430                 el.un('mouseleave' ,this.onEventLeave, this);
13431                 el.remove();
13432             },this);
13433         },this);
13434         
13435         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13436             e.remove();
13437         });
13438         
13439     },
13440     
13441     renderEvents: function()
13442     {   
13443         var _this = this;
13444         
13445         this.cells.each(function(c) {
13446             
13447             if(c.row < 5){
13448                 return;
13449             }
13450             
13451             var ev = c.events;
13452             
13453             var r = 4;
13454             if(c.row != c.events.length){
13455                 r = 4 - (4 - (c.row - c.events.length));
13456             }
13457             
13458             c.events = ev.slice(0, r);
13459             c.more = ev.slice(r);
13460             
13461             if(c.more.length && c.more.length == 1){
13462                 c.events.push(c.more.pop());
13463             }
13464             
13465             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13466             
13467         });
13468             
13469         this.cells.each(function(c) {
13470             
13471             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13472             
13473             
13474             for (var e = 0; e < c.events.length; e++){
13475                 var ev = c.events[e];
13476                 var rows = ev.rows;
13477                 
13478                 for(var i = 0; i < rows.length; i++) {
13479                 
13480                     // how many rows should it span..
13481
13482                     var  cfg = {
13483                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13484                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13485
13486                         unselectable : "on",
13487                         cn : [
13488                             {
13489                                 cls: 'fc-event-inner',
13490                                 cn : [
13491     //                                {
13492     //                                  tag:'span',
13493     //                                  cls: 'fc-event-time',
13494     //                                  html : cells.length > 1 ? '' : ev.time
13495     //                                },
13496                                     {
13497                                       tag:'span',
13498                                       cls: 'fc-event-title',
13499                                       html : String.format('{0}', ev.title)
13500                                     }
13501
13502
13503                                 ]
13504                             },
13505                             {
13506                                 cls: 'ui-resizable-handle ui-resizable-e',
13507                                 html : '&nbsp;&nbsp;&nbsp'
13508                             }
13509
13510                         ]
13511                     };
13512
13513                     if (i == 0) {
13514                         cfg.cls += ' fc-event-start';
13515                     }
13516                     if ((i+1) == rows.length) {
13517                         cfg.cls += ' fc-event-end';
13518                     }
13519
13520                     var ctr = _this.el.select('.fc-event-container',true).first();
13521                     var cg = ctr.createChild(cfg);
13522
13523                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13524                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13525
13526                     var r = (c.more.length) ? 1 : 0;
13527                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13528                     cg.setWidth(ebox.right - sbox.x -2);
13529
13530                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13531                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13532                     cg.on('click', _this.onEventClick, _this, ev);
13533
13534                     ev.els.push(cg);
13535                     
13536                 }
13537                 
13538             }
13539             
13540             
13541             if(c.more.length){
13542                 var  cfg = {
13543                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13544                     style : 'position: absolute',
13545                     unselectable : "on",
13546                     cn : [
13547                         {
13548                             cls: 'fc-event-inner',
13549                             cn : [
13550                                 {
13551                                   tag:'span',
13552                                   cls: 'fc-event-title',
13553                                   html : 'More'
13554                                 }
13555
13556
13557                             ]
13558                         },
13559                         {
13560                             cls: 'ui-resizable-handle ui-resizable-e',
13561                             html : '&nbsp;&nbsp;&nbsp'
13562                         }
13563
13564                     ]
13565                 };
13566
13567                 var ctr = _this.el.select('.fc-event-container',true).first();
13568                 var cg = ctr.createChild(cfg);
13569
13570                 var sbox = c.select('.fc-day-content',true).first().getBox();
13571                 var ebox = c.select('.fc-day-content',true).first().getBox();
13572                 //Roo.log(cg);
13573                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13574                 cg.setWidth(ebox.right - sbox.x -2);
13575
13576                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13577                 
13578             }
13579             
13580         });
13581         
13582         
13583         
13584     },
13585     
13586     onEventEnter: function (e, el,event,d) {
13587         this.fireEvent('evententer', this, el, event);
13588     },
13589     
13590     onEventLeave: function (e, el,event,d) {
13591         this.fireEvent('eventleave', this, el, event);
13592     },
13593     
13594     onEventClick: function (e, el,event,d) {
13595         this.fireEvent('eventclick', this, el, event);
13596     },
13597     
13598     onMonthChange: function () {
13599         this.store.load();
13600     },
13601     
13602     onMoreEventClick: function(e, el, more)
13603     {
13604         var _this = this;
13605         
13606         this.calpopover.placement = 'right';
13607         this.calpopover.setTitle('More');
13608         
13609         this.calpopover.setContent('');
13610         
13611         var ctr = this.calpopover.el.select('.popover-content', true).first();
13612         
13613         Roo.each(more, function(m){
13614             var cfg = {
13615                 cls : 'fc-event-hori fc-event-draggable',
13616                 html : m.title
13617             }
13618             var cg = ctr.createChild(cfg);
13619             
13620             cg.on('click', _this.onEventClick, _this, m);
13621         });
13622         
13623         this.calpopover.show(el);
13624         
13625         
13626     },
13627     
13628     onLoad: function () 
13629     {   
13630         this.calevents = [];
13631         var cal = this;
13632         
13633         if(this.store.getCount() > 0){
13634             this.store.data.each(function(d){
13635                cal.addItem({
13636                     id : d.data.id,
13637                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13638                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13639                     time : d.data.start_time,
13640                     title : d.data.title,
13641                     description : d.data.description,
13642                     venue : d.data.venue
13643                 });
13644             });
13645         }
13646         
13647         this.renderEvents();
13648         
13649         if(this.calevents.length && this.loadMask){
13650             this.maskEl.hide();
13651         }
13652     },
13653     
13654     onBeforeLoad: function()
13655     {
13656         this.clearEvents();
13657         if(this.loadMask){
13658             this.maskEl.show();
13659         }
13660     }
13661 });
13662
13663  
13664  /*
13665  * - LGPL
13666  *
13667  * element
13668  * 
13669  */
13670
13671 /**
13672  * @class Roo.bootstrap.Popover
13673  * @extends Roo.bootstrap.Component
13674  * Bootstrap Popover class
13675  * @cfg {String} html contents of the popover   (or false to use children..)
13676  * @cfg {String} title of popover (or false to hide)
13677  * @cfg {String} placement how it is placed
13678  * @cfg {String} trigger click || hover (or false to trigger manually)
13679  * @cfg {String} over what (parent or false to trigger manually.)
13680  * @cfg {Number} delay - delay before showing
13681  
13682  * @constructor
13683  * Create a new Popover
13684  * @param {Object} config The config object
13685  */
13686
13687 Roo.bootstrap.Popover = function(config){
13688     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13689 };
13690
13691 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13692     
13693     title: 'Fill in a title',
13694     html: false,
13695     
13696     placement : 'right',
13697     trigger : 'hover', // hover
13698     
13699     delay : 0,
13700     
13701     over: 'parent',
13702     
13703     can_build_overlaid : false,
13704     
13705     getChildContainer : function()
13706     {
13707         return this.el.select('.popover-content',true).first();
13708     },
13709     
13710     getAutoCreate : function(){
13711          Roo.log('make popover?');
13712         var cfg = {
13713            cls : 'popover roo-dynamic',
13714            style: 'display:block',
13715            cn : [
13716                 {
13717                     cls : 'arrow'
13718                 },
13719                 {
13720                     cls : 'popover-inner',
13721                     cn : [
13722                         {
13723                             tag: 'h3',
13724                             cls: 'popover-title',
13725                             html : this.title
13726                         },
13727                         {
13728                             cls : 'popover-content',
13729                             html : this.html
13730                         }
13731                     ]
13732                     
13733                 }
13734            ]
13735         };
13736         
13737         return cfg;
13738     },
13739     setTitle: function(str)
13740     {
13741         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13742     },
13743     setContent: function(str)
13744     {
13745         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13746     },
13747     // as it get's added to the bottom of the page.
13748     onRender : function(ct, position)
13749     {
13750         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13751         if(!this.el){
13752             var cfg = Roo.apply({},  this.getAutoCreate());
13753             cfg.id = Roo.id();
13754             
13755             if (this.cls) {
13756                 cfg.cls += ' ' + this.cls;
13757             }
13758             if (this.style) {
13759                 cfg.style = this.style;
13760             }
13761             Roo.log("adding to ")
13762             this.el = Roo.get(document.body).createChild(cfg, position);
13763             Roo.log(this.el);
13764         }
13765         this.initEvents();
13766     },
13767     
13768     initEvents : function()
13769     {
13770         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13771         this.el.enableDisplayMode('block');
13772         this.el.hide();
13773         if (this.over === false) {
13774             return; 
13775         }
13776         if (this.triggers === false) {
13777             return;
13778         }
13779         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13780         var triggers = this.trigger ? this.trigger.split(' ') : [];
13781         Roo.each(triggers, function(trigger) {
13782         
13783             if (trigger == 'click') {
13784                 on_el.on('click', this.toggle, this);
13785             } else if (trigger != 'manual') {
13786                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13787                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13788       
13789                 on_el.on(eventIn  ,this.enter, this);
13790                 on_el.on(eventOut, this.leave, this);
13791             }
13792         }, this);
13793         
13794     },
13795     
13796     
13797     // private
13798     timeout : null,
13799     hoverState : null,
13800     
13801     toggle : function () {
13802         this.hoverState == 'in' ? this.leave() : this.enter();
13803     },
13804     
13805     enter : function () {
13806        
13807     
13808         clearTimeout(this.timeout);
13809     
13810         this.hoverState = 'in'
13811     
13812         if (!this.delay || !this.delay.show) {
13813             this.show();
13814             return 
13815         }
13816         var _t = this;
13817         this.timeout = setTimeout(function () {
13818             if (_t.hoverState == 'in') {
13819                 _t.show();
13820             }
13821         }, this.delay.show)
13822     },
13823     leave : function() {
13824         clearTimeout(this.timeout);
13825     
13826         this.hoverState = 'out'
13827     
13828         if (!this.delay || !this.delay.hide) {
13829             this.hide();
13830             return 
13831         }
13832         var _t = this;
13833         this.timeout = setTimeout(function () {
13834             if (_t.hoverState == 'out') {
13835                 _t.hide();
13836             }
13837         }, this.delay.hide)
13838     },
13839     
13840     show : function (on_el)
13841     {
13842         if (!on_el) {
13843             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13844         }
13845         // set content.
13846         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13847         if (this.html !== false) {
13848             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13849         }
13850         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13851         if (!this.title.length) {
13852             this.el.select('.popover-title',true).hide();
13853         }
13854         
13855         var placement = typeof this.placement == 'function' ?
13856             this.placement.call(this, this.el, on_el) :
13857             this.placement;
13858             
13859         var autoToken = /\s?auto?\s?/i;
13860         var autoPlace = autoToken.test(placement);
13861         if (autoPlace) {
13862             placement = placement.replace(autoToken, '') || 'top';
13863         }
13864         
13865         //this.el.detach()
13866         //this.el.setXY([0,0]);
13867         this.el.show();
13868         this.el.dom.style.display='block';
13869         this.el.addClass(placement);
13870         
13871         //this.el.appendTo(on_el);
13872         
13873         var p = this.getPosition();
13874         var box = this.el.getBox();
13875         
13876         if (autoPlace) {
13877             // fixme..
13878         }
13879         var align = Roo.bootstrap.Popover.alignment[placement]
13880         this.el.alignTo(on_el, align[0],align[1]);
13881         //var arrow = this.el.select('.arrow',true).first();
13882         //arrow.set(align[2], 
13883         
13884         this.el.addClass('in');
13885         this.hoverState = null;
13886         
13887         if (this.el.hasClass('fade')) {
13888             // fade it?
13889         }
13890         
13891     },
13892     hide : function()
13893     {
13894         this.el.setXY([0,0]);
13895         this.el.removeClass('in');
13896         this.el.hide();
13897         
13898     }
13899     
13900 });
13901
13902 Roo.bootstrap.Popover.alignment = {
13903     'left' : ['r-l', [-10,0], 'right'],
13904     'right' : ['l-r', [10,0], 'left'],
13905     'bottom' : ['t-b', [0,10], 'top'],
13906     'top' : [ 'b-t', [0,-10], 'bottom']
13907 };
13908
13909  /*
13910  * - LGPL
13911  *
13912  * Progress
13913  * 
13914  */
13915
13916 /**
13917  * @class Roo.bootstrap.Progress
13918  * @extends Roo.bootstrap.Component
13919  * Bootstrap Progress class
13920  * @cfg {Boolean} striped striped of the progress bar
13921  * @cfg {Boolean} active animated of the progress bar
13922  * 
13923  * 
13924  * @constructor
13925  * Create a new Progress
13926  * @param {Object} config The config object
13927  */
13928
13929 Roo.bootstrap.Progress = function(config){
13930     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13931 };
13932
13933 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13934     
13935     striped : false,
13936     active: false,
13937     
13938     getAutoCreate : function(){
13939         var cfg = {
13940             tag: 'div',
13941             cls: 'progress'
13942         };
13943         
13944         
13945         if(this.striped){
13946             cfg.cls += ' progress-striped';
13947         }
13948       
13949         if(this.active){
13950             cfg.cls += ' active';
13951         }
13952         
13953         
13954         return cfg;
13955     }
13956    
13957 });
13958
13959  
13960
13961  /*
13962  * - LGPL
13963  *
13964  * ProgressBar
13965  * 
13966  */
13967
13968 /**
13969  * @class Roo.bootstrap.ProgressBar
13970  * @extends Roo.bootstrap.Component
13971  * Bootstrap ProgressBar class
13972  * @cfg {Number} aria_valuenow aria-value now
13973  * @cfg {Number} aria_valuemin aria-value min
13974  * @cfg {Number} aria_valuemax aria-value max
13975  * @cfg {String} label label for the progress bar
13976  * @cfg {String} panel (success | info | warning | danger )
13977  * @cfg {String} role role of the progress bar
13978  * @cfg {String} sr_only text
13979  * 
13980  * 
13981  * @constructor
13982  * Create a new ProgressBar
13983  * @param {Object} config The config object
13984  */
13985
13986 Roo.bootstrap.ProgressBar = function(config){
13987     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13988 };
13989
13990 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13991     
13992     aria_valuenow : 0,
13993     aria_valuemin : 0,
13994     aria_valuemax : 100,
13995     label : false,
13996     panel : false,
13997     role : false,
13998     sr_only: false,
13999     
14000     getAutoCreate : function()
14001     {
14002         
14003         var cfg = {
14004             tag: 'div',
14005             cls: 'progress-bar',
14006             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14007         };
14008         
14009         if(this.sr_only){
14010             cfg.cn = {
14011                 tag: 'span',
14012                 cls: 'sr-only',
14013                 html: this.sr_only
14014             }
14015         }
14016         
14017         if(this.role){
14018             cfg.role = this.role;
14019         }
14020         
14021         if(this.aria_valuenow){
14022             cfg['aria-valuenow'] = this.aria_valuenow;
14023         }
14024         
14025         if(this.aria_valuemin){
14026             cfg['aria-valuemin'] = this.aria_valuemin;
14027         }
14028         
14029         if(this.aria_valuemax){
14030             cfg['aria-valuemax'] = this.aria_valuemax;
14031         }
14032         
14033         if(this.label && !this.sr_only){
14034             cfg.html = this.label;
14035         }
14036         
14037         if(this.panel){
14038             cfg.cls += ' progress-bar-' + this.panel;
14039         }
14040         
14041         return cfg;
14042     },
14043     
14044     update : function(aria_valuenow)
14045     {
14046         this.aria_valuenow = aria_valuenow;
14047         
14048         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14049     }
14050    
14051 });
14052
14053  
14054
14055  /*
14056  * - LGPL
14057  *
14058  * column
14059  * 
14060  */
14061
14062 /**
14063  * @class Roo.bootstrap.TabGroup
14064  * @extends Roo.bootstrap.Column
14065  * Bootstrap Column class
14066  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14067  * @cfg {Boolean} carousel true to make the group behave like a carousel
14068  * 
14069  * @constructor
14070  * Create a new TabGroup
14071  * @param {Object} config The config object
14072  */
14073
14074 Roo.bootstrap.TabGroup = function(config){
14075     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14076     if (!this.navId) {
14077         this.navId = Roo.id();
14078     }
14079     this.tabs = [];
14080     Roo.bootstrap.TabGroup.register(this);
14081     
14082 };
14083
14084 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14085     
14086     carousel : false,
14087     transition : false,
14088      
14089     getAutoCreate : function()
14090     {
14091         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14092         
14093         cfg.cls += ' tab-content';
14094         
14095         if (this.carousel) {
14096             cfg.cls += ' carousel slide';
14097             cfg.cn = [{
14098                cls : 'carousel-inner'
14099             }]
14100         }
14101         
14102         
14103         return cfg;
14104     },
14105     getChildContainer : function()
14106     {
14107         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14108     },
14109     
14110     /**
14111     * register a Navigation item
14112     * @param {Roo.bootstrap.NavItem} the navitem to add
14113     */
14114     register : function(item)
14115     {
14116         this.tabs.push( item);
14117         item.navId = this.navId; // not really needed..
14118     
14119     },
14120     
14121     getActivePanel : function()
14122     {
14123         var r = false;
14124         Roo.each(this.tabs, function(t) {
14125             if (t.active) {
14126                 r = t;
14127                 return false;
14128             }
14129             return null;
14130         });
14131         return r;
14132         
14133     },
14134     getPanelByName : function(n)
14135     {
14136         var r = false;
14137         Roo.each(this.tabs, function(t) {
14138             if (t.tabId == n) {
14139                 r = t;
14140                 return false;
14141             }
14142             return null;
14143         });
14144         return r;
14145     },
14146     indexOfPanel : function(p)
14147     {
14148         var r = false;
14149         Roo.each(this.tabs, function(t,i) {
14150             if (t.tabId == p.tabId) {
14151                 r = i;
14152                 return false;
14153             }
14154             return null;
14155         });
14156         return r;
14157     },
14158     /**
14159      * show a specific panel
14160      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14161      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14162      */
14163     showPanel : function (pan)
14164     {
14165         
14166         if (typeof(pan) == 'number') {
14167             pan = this.tabs[pan];
14168         }
14169         if (typeof(pan) == 'string') {
14170             pan = this.getPanelByName(pan);
14171         }
14172         if (pan.tabId == this.getActivePanel().tabId) {
14173             return true;
14174         }
14175         var cur = this.getActivePanel();
14176         
14177         if (false === cur.fireEvent('beforedeactivate')) {
14178             return false;
14179         }
14180         
14181         if (this.carousel) {
14182             this.transition = true;
14183             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14184             var lr = dir == 'next' ? 'left' : 'right';
14185             pan.el.addClass(dir); // or prev
14186             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14187             cur.el.addClass(lr); // or right
14188             pan.el.addClass(lr);
14189             
14190             var _this = this;
14191             cur.el.on('transitionend', function() {
14192                 Roo.log("trans end?");
14193                 
14194                 pan.el.removeClass([lr,dir]);
14195                 pan.setActive(true);
14196                 
14197                 cur.el.removeClass([lr]);
14198                 cur.setActive(false);
14199                 
14200                 _this.transition = false;
14201                 
14202             }, this, { single:  true } );
14203             return true;
14204         }
14205         
14206         cur.setActive(false);
14207         pan.setActive(true);
14208         return true;
14209         
14210     },
14211     showPanelNext : function()
14212     {
14213         var i = this.indexOfPanel(this.getActivePanel());
14214         if (i > this.tabs.length) {
14215             return;
14216         }
14217         this.showPanel(this.tabs[i+1]);
14218     },
14219     showPanelPrev : function()
14220     {
14221         var i = this.indexOfPanel(this.getActivePanel());
14222         if (i  < 1) {
14223             return;
14224         }
14225         this.showPanel(this.tabs[i-1]);
14226     }
14227     
14228     
14229   
14230 });
14231
14232  
14233
14234  
14235  
14236 Roo.apply(Roo.bootstrap.TabGroup, {
14237     
14238     groups: {},
14239      /**
14240     * register a Navigation Group
14241     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14242     */
14243     register : function(navgrp)
14244     {
14245         this.groups[navgrp.navId] = navgrp;
14246         
14247     },
14248     /**
14249     * fetch a Navigation Group based on the navigation ID
14250     * if one does not exist , it will get created.
14251     * @param {string} the navgroup to add
14252     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14253     */
14254     get: function(navId) {
14255         if (typeof(this.groups[navId]) == 'undefined') {
14256             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14257         }
14258         return this.groups[navId] ;
14259     }
14260     
14261     
14262     
14263 });
14264
14265  /*
14266  * - LGPL
14267  *
14268  * TabPanel
14269  * 
14270  */
14271
14272 /**
14273  * @class Roo.bootstrap.TabPanel
14274  * @extends Roo.bootstrap.Component
14275  * Bootstrap TabPanel class
14276  * @cfg {Boolean} active panel active
14277  * @cfg {String} html panel content
14278  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14279  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14280  * 
14281  * 
14282  * @constructor
14283  * Create a new TabPanel
14284  * @param {Object} config The config object
14285  */
14286
14287 Roo.bootstrap.TabPanel = function(config){
14288     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14289     this.addEvents({
14290         /**
14291              * @event changed
14292              * Fires when the active status changes
14293              * @param {Roo.bootstrap.TabPanel} this
14294              * @param {Boolean} state the new state
14295             
14296          */
14297         'changed': true,
14298         /**
14299              * @event beforedeactivate
14300              * Fires before a tab is de-activated - can be used to do validation on a form.
14301              * @param {Roo.bootstrap.TabPanel} this
14302              * @return {Boolean} false if there is an error
14303             
14304          */
14305         'beforedeactivate': true
14306      });
14307     
14308     this.tabId = this.tabId || Roo.id();
14309   
14310 };
14311
14312 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14313     
14314     active: false,
14315     html: false,
14316     tabId: false,
14317     navId : false,
14318     
14319     getAutoCreate : function(){
14320         var cfg = {
14321             tag: 'div',
14322             // item is needed for carousel - not sure if it has any effect otherwise
14323             cls: 'tab-pane item',
14324             html: this.html || ''
14325         };
14326         
14327         if(this.active){
14328             cfg.cls += ' active';
14329         }
14330         
14331         if(this.tabId){
14332             cfg.tabId = this.tabId;
14333         }
14334         
14335         
14336         return cfg;
14337     },
14338     
14339     initEvents:  function()
14340     {
14341         Roo.log('-------- init events on tab panel ---------');
14342         
14343         var p = this.parent();
14344         this.navId = this.navId || p.navId;
14345         
14346         if (typeof(this.navId) != 'undefined') {
14347             // not really needed.. but just in case.. parent should be a NavGroup.
14348             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14349             Roo.log(['register', tg, this]);
14350             tg.register(this);
14351         }
14352     },
14353     
14354     
14355     onRender : function(ct, position)
14356     {
14357        // Roo.log("Call onRender: " + this.xtype);
14358         
14359         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14360         
14361         
14362         
14363         
14364         
14365     },
14366     
14367     setActive: function(state)
14368     {
14369         Roo.log("panel - set active " + this.tabId + "=" + state);
14370         
14371         this.active = state;
14372         if (!state) {
14373             this.el.removeClass('active');
14374             
14375         } else  if (!this.el.hasClass('active')) {
14376             this.el.addClass('active');
14377         }
14378         this.fireEvent('changed', this, state);
14379     }
14380     
14381     
14382 });
14383  
14384
14385  
14386
14387  /*
14388  * - LGPL
14389  *
14390  * DateField
14391  * 
14392  */
14393
14394 /**
14395  * @class Roo.bootstrap.DateField
14396  * @extends Roo.bootstrap.Input
14397  * Bootstrap DateField class
14398  * @cfg {Number} weekStart default 0
14399  * @cfg {String} viewMode default empty, (months|years)
14400  * @cfg {String} minViewMode default empty, (months|years)
14401  * @cfg {Number} startDate default -Infinity
14402  * @cfg {Number} endDate default Infinity
14403  * @cfg {Boolean} todayHighlight default false
14404  * @cfg {Boolean} todayBtn default false
14405  * @cfg {Boolean} calendarWeeks default false
14406  * @cfg {Object} daysOfWeekDisabled default empty
14407  * @cfg {Boolean} singleMode default false (true | false)
14408  * 
14409  * @cfg {Boolean} keyboardNavigation default true
14410  * @cfg {String} language default en
14411  * 
14412  * @constructor
14413  * Create a new DateField
14414  * @param {Object} config The config object
14415  */
14416
14417 Roo.bootstrap.DateField = function(config){
14418     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14419      this.addEvents({
14420             /**
14421              * @event show
14422              * Fires when this field show.
14423              * @param {Roo.bootstrap.DateField} this
14424              * @param {Mixed} date The date value
14425              */
14426             show : true,
14427             /**
14428              * @event show
14429              * Fires when this field hide.
14430              * @param {Roo.bootstrap.DateField} this
14431              * @param {Mixed} date The date value
14432              */
14433             hide : true,
14434             /**
14435              * @event select
14436              * Fires when select a date.
14437              * @param {Roo.bootstrap.DateField} this
14438              * @param {Mixed} date The date value
14439              */
14440             select : true
14441         });
14442 };
14443
14444 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14445     
14446     /**
14447      * @cfg {String} format
14448      * The default date format string which can be overriden for localization support.  The format must be
14449      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14450      */
14451     format : "m/d/y",
14452     /**
14453      * @cfg {String} altFormats
14454      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14455      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14456      */
14457     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14458     
14459     weekStart : 0,
14460     
14461     viewMode : '',
14462     
14463     minViewMode : '',
14464     
14465     todayHighlight : false,
14466     
14467     todayBtn: false,
14468     
14469     language: 'en',
14470     
14471     keyboardNavigation: true,
14472     
14473     calendarWeeks: false,
14474     
14475     startDate: -Infinity,
14476     
14477     endDate: Infinity,
14478     
14479     daysOfWeekDisabled: [],
14480     
14481     _events: [],
14482     
14483     singleMode : false,
14484     
14485     UTCDate: function()
14486     {
14487         return new Date(Date.UTC.apply(Date, arguments));
14488     },
14489     
14490     UTCToday: function()
14491     {
14492         var today = new Date();
14493         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14494     },
14495     
14496     getDate: function() {
14497             var d = this.getUTCDate();
14498             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14499     },
14500     
14501     getUTCDate: function() {
14502             return this.date;
14503     },
14504     
14505     setDate: function(d) {
14506             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14507     },
14508     
14509     setUTCDate: function(d) {
14510             this.date = d;
14511             this.setValue(this.formatDate(this.date));
14512     },
14513         
14514     onRender: function(ct, position)
14515     {
14516         
14517         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14518         
14519         this.language = this.language || 'en';
14520         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14521         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14522         
14523         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14524         this.format = this.format || 'm/d/y';
14525         this.isInline = false;
14526         this.isInput = true;
14527         this.component = this.el.select('.add-on', true).first() || false;
14528         this.component = (this.component && this.component.length === 0) ? false : this.component;
14529         this.hasInput = this.component && this.inputEL().length;
14530         
14531         if (typeof(this.minViewMode === 'string')) {
14532             switch (this.minViewMode) {
14533                 case 'months':
14534                     this.minViewMode = 1;
14535                     break;
14536                 case 'years':
14537                     this.minViewMode = 2;
14538                     break;
14539                 default:
14540                     this.minViewMode = 0;
14541                     break;
14542             }
14543         }
14544         
14545         if (typeof(this.viewMode === 'string')) {
14546             switch (this.viewMode) {
14547                 case 'months':
14548                     this.viewMode = 1;
14549                     break;
14550                 case 'years':
14551                     this.viewMode = 2;
14552                     break;
14553                 default:
14554                     this.viewMode = 0;
14555                     break;
14556             }
14557         }
14558                 
14559         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14560         
14561 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14562         
14563         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14564         
14565         this.picker().on('mousedown', this.onMousedown, this);
14566         this.picker().on('click', this.onClick, this);
14567         
14568         this.picker().addClass('datepicker-dropdown');
14569         
14570         this.startViewMode = this.viewMode;
14571         
14572         if(this.singleMode){
14573             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14574                 v.setVisibilityMode(Roo.Element.DISPLAY)
14575                 v.hide();
14576             })
14577             
14578             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14579                 v.setStyle('width', '189px');
14580             });
14581         }
14582         
14583         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14584             if(!this.calendarWeeks){
14585                 v.remove();
14586                 return;
14587             };
14588             
14589             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14590             v.attr('colspan', function(i, val){
14591                 return parseInt(val) + 1;
14592             });
14593         })
14594                         
14595         
14596         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14597         
14598         this.setStartDate(this.startDate);
14599         this.setEndDate(this.endDate);
14600         
14601         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14602         
14603         this.fillDow();
14604         this.fillMonths();
14605         this.update();
14606         this.showMode();
14607         
14608         if(this.isInline) {
14609             this.show();
14610         }
14611     },
14612     
14613     picker : function()
14614     {
14615         return this.pickerEl;
14616 //        return this.el.select('.datepicker', true).first();
14617     },
14618     
14619     fillDow: function()
14620     {
14621         var dowCnt = this.weekStart;
14622         
14623         var dow = {
14624             tag: 'tr',
14625             cn: [
14626                 
14627             ]
14628         };
14629         
14630         if(this.calendarWeeks){
14631             dow.cn.push({
14632                 tag: 'th',
14633                 cls: 'cw',
14634                 html: '&nbsp;'
14635             })
14636         }
14637         
14638         while (dowCnt < this.weekStart + 7) {
14639             dow.cn.push({
14640                 tag: 'th',
14641                 cls: 'dow',
14642                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14643             });
14644         }
14645         
14646         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14647     },
14648     
14649     fillMonths: function()
14650     {    
14651         var i = 0
14652         var months = this.picker().select('>.datepicker-months td', true).first();
14653         
14654         months.dom.innerHTML = '';
14655         
14656         while (i < 12) {
14657             var month = {
14658                 tag: 'span',
14659                 cls: 'month',
14660                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14661             }
14662             
14663             months.createChild(month);
14664         }
14665         
14666     },
14667     
14668     update: function()
14669     {
14670         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;
14671         
14672         if (this.date < this.startDate) {
14673             this.viewDate = new Date(this.startDate);
14674         } else if (this.date > this.endDate) {
14675             this.viewDate = new Date(this.endDate);
14676         } else {
14677             this.viewDate = new Date(this.date);
14678         }
14679         
14680         this.fill();
14681     },
14682     
14683     fill: function() 
14684     {
14685         var d = new Date(this.viewDate),
14686                 year = d.getUTCFullYear(),
14687                 month = d.getUTCMonth(),
14688                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14689                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14690                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14691                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14692                 currentDate = this.date && this.date.valueOf(),
14693                 today = this.UTCToday();
14694         
14695         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14696         
14697 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14698         
14699 //        this.picker.select('>tfoot th.today').
14700 //                                              .text(dates[this.language].today)
14701 //                                              .toggle(this.todayBtn !== false);
14702     
14703         this.updateNavArrows();
14704         this.fillMonths();
14705                                                 
14706         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14707         
14708         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14709          
14710         prevMonth.setUTCDate(day);
14711         
14712         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14713         
14714         var nextMonth = new Date(prevMonth);
14715         
14716         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14717         
14718         nextMonth = nextMonth.valueOf();
14719         
14720         var fillMonths = false;
14721         
14722         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14723         
14724         while(prevMonth.valueOf() < nextMonth) {
14725             var clsName = '';
14726             
14727             if (prevMonth.getUTCDay() === this.weekStart) {
14728                 if(fillMonths){
14729                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14730                 }
14731                     
14732                 fillMonths = {
14733                     tag: 'tr',
14734                     cn: []
14735                 };
14736                 
14737                 if(this.calendarWeeks){
14738                     // ISO 8601: First week contains first thursday.
14739                     // ISO also states week starts on Monday, but we can be more abstract here.
14740                     var
14741                     // Start of current week: based on weekstart/current date
14742                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14743                     // Thursday of this week
14744                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14745                     // First Thursday of year, year from thursday
14746                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14747                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14748                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14749                     
14750                     fillMonths.cn.push({
14751                         tag: 'td',
14752                         cls: 'cw',
14753                         html: calWeek
14754                     });
14755                 }
14756             }
14757             
14758             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14759                 clsName += ' old';
14760             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14761                 clsName += ' new';
14762             }
14763             if (this.todayHighlight &&
14764                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14765                 prevMonth.getUTCMonth() == today.getMonth() &&
14766                 prevMonth.getUTCDate() == today.getDate()) {
14767                 clsName += ' today';
14768             }
14769             
14770             if (currentDate && prevMonth.valueOf() === currentDate) {
14771                 clsName += ' active';
14772             }
14773             
14774             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14775                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14776                     clsName += ' disabled';
14777             }
14778             
14779             fillMonths.cn.push({
14780                 tag: 'td',
14781                 cls: 'day ' + clsName,
14782                 html: prevMonth.getDate()
14783             })
14784             
14785             prevMonth.setDate(prevMonth.getDate()+1);
14786         }
14787           
14788         var currentYear = this.date && this.date.getUTCFullYear();
14789         var currentMonth = this.date && this.date.getUTCMonth();
14790         
14791         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14792         
14793         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14794             v.removeClass('active');
14795             
14796             if(currentYear === year && k === currentMonth){
14797                 v.addClass('active');
14798             }
14799             
14800             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14801                 v.addClass('disabled');
14802             }
14803             
14804         });
14805         
14806         
14807         year = parseInt(year/10, 10) * 10;
14808         
14809         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14810         
14811         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14812         
14813         year -= 1;
14814         for (var i = -1; i < 11; i++) {
14815             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14816                 tag: 'span',
14817                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14818                 html: year
14819             })
14820             
14821             year += 1;
14822         }
14823     },
14824     
14825     showMode: function(dir) 
14826     {
14827         if (dir) {
14828             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14829         }
14830         
14831         Roo.each(this.picker().select('>div',true).elements, function(v){
14832             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14833             v.hide();
14834         });
14835         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14836     },
14837     
14838     place: function()
14839     {
14840         if(this.isInline) return;
14841         
14842         this.picker().removeClass(['bottom', 'top']);
14843         
14844         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14845             /*
14846              * place to the top of element!
14847              *
14848              */
14849             
14850             this.picker().addClass('top');
14851             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14852             
14853             return;
14854         }
14855         
14856         this.picker().addClass('bottom');
14857         
14858         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14859     },
14860     
14861     parseDate : function(value)
14862     {
14863         if(!value || value instanceof Date){
14864             return value;
14865         }
14866         var v = Date.parseDate(value, this.format);
14867         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14868             v = Date.parseDate(value, 'Y-m-d');
14869         }
14870         if(!v && this.altFormats){
14871             if(!this.altFormatsArray){
14872                 this.altFormatsArray = this.altFormats.split("|");
14873             }
14874             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14875                 v = Date.parseDate(value, this.altFormatsArray[i]);
14876             }
14877         }
14878         return v;
14879     },
14880     
14881     formatDate : function(date, fmt)
14882     {   
14883         return (!date || !(date instanceof Date)) ?
14884         date : date.dateFormat(fmt || this.format);
14885     },
14886     
14887     onFocus : function()
14888     {
14889         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14890         this.show();
14891     },
14892     
14893     onBlur : function()
14894     {
14895         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14896         
14897         var d = this.inputEl().getValue();
14898         
14899         this.setValue(d);
14900                 
14901         this.hide();
14902     },
14903     
14904     show : function()
14905     {
14906         this.picker().show();
14907         this.update();
14908         this.place();
14909         
14910         this.fireEvent('show', this, this.date);
14911     },
14912     
14913     hide : function()
14914     {
14915         if(this.isInline) return;
14916         this.picker().hide();
14917         this.viewMode = this.startViewMode;
14918         this.showMode();
14919         
14920         this.fireEvent('hide', this, this.date);
14921         
14922     },
14923     
14924     onMousedown: function(e)
14925     {
14926         e.stopPropagation();
14927         e.preventDefault();
14928     },
14929     
14930     keyup: function(e)
14931     {
14932         Roo.bootstrap.DateField.superclass.keyup.call(this);
14933         this.update();
14934     },
14935
14936     setValue: function(v)
14937     {
14938         
14939         // v can be a string or a date..
14940         
14941         
14942         var d = new Date(this.parseDate(v) ).clearTime();
14943         
14944         if(isNaN(d.getTime())){
14945             this.date = this.viewDate = '';
14946             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14947             return;
14948         }
14949         
14950         v = this.formatDate(d);
14951         
14952         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14953         
14954         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14955      
14956         this.update();
14957
14958         this.fireEvent('select', this, this.date);
14959         
14960     },
14961     
14962     getValue: function()
14963     {
14964         return this.formatDate(this.date);
14965     },
14966     
14967     fireKey: function(e)
14968     {
14969         if (!this.picker().isVisible()){
14970             if (e.keyCode == 27) // allow escape to hide and re-show picker
14971                 this.show();
14972             return;
14973         }
14974         
14975         var dateChanged = false,
14976         dir, day, month,
14977         newDate, newViewDate;
14978         
14979         switch(e.keyCode){
14980             case 27: // escape
14981                 this.hide();
14982                 e.preventDefault();
14983                 break;
14984             case 37: // left
14985             case 39: // right
14986                 if (!this.keyboardNavigation) break;
14987                 dir = e.keyCode == 37 ? -1 : 1;
14988                 
14989                 if (e.ctrlKey){
14990                     newDate = this.moveYear(this.date, dir);
14991                     newViewDate = this.moveYear(this.viewDate, dir);
14992                 } else if (e.shiftKey){
14993                     newDate = this.moveMonth(this.date, dir);
14994                     newViewDate = this.moveMonth(this.viewDate, dir);
14995                 } else {
14996                     newDate = new Date(this.date);
14997                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14998                     newViewDate = new Date(this.viewDate);
14999                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15000                 }
15001                 if (this.dateWithinRange(newDate)){
15002                     this.date = newDate;
15003                     this.viewDate = newViewDate;
15004                     this.setValue(this.formatDate(this.date));
15005 //                    this.update();
15006                     e.preventDefault();
15007                     dateChanged = true;
15008                 }
15009                 break;
15010             case 38: // up
15011             case 40: // down
15012                 if (!this.keyboardNavigation) break;
15013                 dir = e.keyCode == 38 ? -1 : 1;
15014                 if (e.ctrlKey){
15015                     newDate = this.moveYear(this.date, dir);
15016                     newViewDate = this.moveYear(this.viewDate, dir);
15017                 } else if (e.shiftKey){
15018                     newDate = this.moveMonth(this.date, dir);
15019                     newViewDate = this.moveMonth(this.viewDate, dir);
15020                 } else {
15021                     newDate = new Date(this.date);
15022                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15023                     newViewDate = new Date(this.viewDate);
15024                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15025                 }
15026                 if (this.dateWithinRange(newDate)){
15027                     this.date = newDate;
15028                     this.viewDate = newViewDate;
15029                     this.setValue(this.formatDate(this.date));
15030 //                    this.update();
15031                     e.preventDefault();
15032                     dateChanged = true;
15033                 }
15034                 break;
15035             case 13: // enter
15036                 this.setValue(this.formatDate(this.date));
15037                 this.hide();
15038                 e.preventDefault();
15039                 break;
15040             case 9: // tab
15041                 this.setValue(this.formatDate(this.date));
15042                 this.hide();
15043                 break;
15044             case 16: // shift
15045             case 17: // ctrl
15046             case 18: // alt
15047                 break;
15048             default :
15049                 this.hide();
15050                 
15051         }
15052     },
15053     
15054     
15055     onClick: function(e) 
15056     {
15057         e.stopPropagation();
15058         e.preventDefault();
15059         
15060         var target = e.getTarget();
15061         
15062         if(target.nodeName.toLowerCase() === 'i'){
15063             target = Roo.get(target).dom.parentNode;
15064         }
15065         
15066         var nodeName = target.nodeName;
15067         var className = target.className;
15068         var html = target.innerHTML;
15069         //Roo.log(nodeName);
15070         
15071         switch(nodeName.toLowerCase()) {
15072             case 'th':
15073                 switch(className) {
15074                     case 'switch':
15075                         this.showMode(1);
15076                         break;
15077                     case 'prev':
15078                     case 'next':
15079                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15080                         switch(this.viewMode){
15081                                 case 0:
15082                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15083                                         break;
15084                                 case 1:
15085                                 case 2:
15086                                         this.viewDate = this.moveYear(this.viewDate, dir);
15087                                         break;
15088                         }
15089                         this.fill();
15090                         break;
15091                     case 'today':
15092                         var date = new Date();
15093                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15094 //                        this.fill()
15095                         this.setValue(this.formatDate(this.date));
15096                         
15097                         this.hide();
15098                         break;
15099                 }
15100                 break;
15101             case 'span':
15102                 if (className.indexOf('disabled') < 0) {
15103                     this.viewDate.setUTCDate(1);
15104                     if (className.indexOf('month') > -1) {
15105                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15106                     } else {
15107                         var year = parseInt(html, 10) || 0;
15108                         this.viewDate.setUTCFullYear(year);
15109                         
15110                     }
15111                     
15112                     if(this.singleMode){
15113                         this.setValue(this.formatDate(this.viewDate));
15114                         this.hide();
15115                         return;
15116                     }
15117                     
15118                     this.showMode(-1);
15119                     this.fill();
15120                 }
15121                 break;
15122                 
15123             case 'td':
15124                 //Roo.log(className);
15125                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15126                     var day = parseInt(html, 10) || 1;
15127                     var year = this.viewDate.getUTCFullYear(),
15128                         month = this.viewDate.getUTCMonth();
15129
15130                     if (className.indexOf('old') > -1) {
15131                         if(month === 0 ){
15132                             month = 11;
15133                             year -= 1;
15134                         }else{
15135                             month -= 1;
15136                         }
15137                     } else if (className.indexOf('new') > -1) {
15138                         if (month == 11) {
15139                             month = 0;
15140                             year += 1;
15141                         } else {
15142                             month += 1;
15143                         }
15144                     }
15145                     //Roo.log([year,month,day]);
15146                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15147                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15148 //                    this.fill();
15149                     //Roo.log(this.formatDate(this.date));
15150                     this.setValue(this.formatDate(this.date));
15151                     this.hide();
15152                 }
15153                 break;
15154         }
15155     },
15156     
15157     setStartDate: function(startDate)
15158     {
15159         this.startDate = startDate || -Infinity;
15160         if (this.startDate !== -Infinity) {
15161             this.startDate = this.parseDate(this.startDate);
15162         }
15163         this.update();
15164         this.updateNavArrows();
15165     },
15166
15167     setEndDate: function(endDate)
15168     {
15169         this.endDate = endDate || Infinity;
15170         if (this.endDate !== Infinity) {
15171             this.endDate = this.parseDate(this.endDate);
15172         }
15173         this.update();
15174         this.updateNavArrows();
15175     },
15176     
15177     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15178     {
15179         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15180         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15181             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15182         }
15183         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15184             return parseInt(d, 10);
15185         });
15186         this.update();
15187         this.updateNavArrows();
15188     },
15189     
15190     updateNavArrows: function() 
15191     {
15192         if(this.singleMode){
15193             return;
15194         }
15195         
15196         var d = new Date(this.viewDate),
15197         year = d.getUTCFullYear(),
15198         month = d.getUTCMonth();
15199         
15200         Roo.each(this.picker().select('.prev', true).elements, function(v){
15201             v.show();
15202             switch (this.viewMode) {
15203                 case 0:
15204
15205                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15206                         v.hide();
15207                     }
15208                     break;
15209                 case 1:
15210                 case 2:
15211                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15212                         v.hide();
15213                     }
15214                     break;
15215             }
15216         });
15217         
15218         Roo.each(this.picker().select('.next', true).elements, function(v){
15219             v.show();
15220             switch (this.viewMode) {
15221                 case 0:
15222
15223                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15224                         v.hide();
15225                     }
15226                     break;
15227                 case 1:
15228                 case 2:
15229                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15230                         v.hide();
15231                     }
15232                     break;
15233             }
15234         })
15235     },
15236     
15237     moveMonth: function(date, dir)
15238     {
15239         if (!dir) return date;
15240         var new_date = new Date(date.valueOf()),
15241         day = new_date.getUTCDate(),
15242         month = new_date.getUTCMonth(),
15243         mag = Math.abs(dir),
15244         new_month, test;
15245         dir = dir > 0 ? 1 : -1;
15246         if (mag == 1){
15247             test = dir == -1
15248             // If going back one month, make sure month is not current month
15249             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15250             ? function(){
15251                 return new_date.getUTCMonth() == month;
15252             }
15253             // If going forward one month, make sure month is as expected
15254             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15255             : function(){
15256                 return new_date.getUTCMonth() != new_month;
15257             };
15258             new_month = month + dir;
15259             new_date.setUTCMonth(new_month);
15260             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15261             if (new_month < 0 || new_month > 11)
15262                 new_month = (new_month + 12) % 12;
15263         } else {
15264             // For magnitudes >1, move one month at a time...
15265             for (var i=0; i<mag; i++)
15266                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15267                 new_date = this.moveMonth(new_date, dir);
15268             // ...then reset the day, keeping it in the new month
15269             new_month = new_date.getUTCMonth();
15270             new_date.setUTCDate(day);
15271             test = function(){
15272                 return new_month != new_date.getUTCMonth();
15273             };
15274         }
15275         // Common date-resetting loop -- if date is beyond end of month, make it
15276         // end of month
15277         while (test()){
15278             new_date.setUTCDate(--day);
15279             new_date.setUTCMonth(new_month);
15280         }
15281         return new_date;
15282     },
15283
15284     moveYear: function(date, dir)
15285     {
15286         return this.moveMonth(date, dir*12);
15287     },
15288
15289     dateWithinRange: function(date)
15290     {
15291         return date >= this.startDate && date <= this.endDate;
15292     },
15293
15294     
15295     remove: function() 
15296     {
15297         this.picker().remove();
15298     }
15299    
15300 });
15301
15302 Roo.apply(Roo.bootstrap.DateField,  {
15303     
15304     head : {
15305         tag: 'thead',
15306         cn: [
15307         {
15308             tag: 'tr',
15309             cn: [
15310             {
15311                 tag: 'th',
15312                 cls: 'prev',
15313                 html: '<i class="fa fa-arrow-left"/>'
15314             },
15315             {
15316                 tag: 'th',
15317                 cls: 'switch',
15318                 colspan: '5'
15319             },
15320             {
15321                 tag: 'th',
15322                 cls: 'next',
15323                 html: '<i class="fa fa-arrow-right"/>'
15324             }
15325
15326             ]
15327         }
15328         ]
15329     },
15330     
15331     content : {
15332         tag: 'tbody',
15333         cn: [
15334         {
15335             tag: 'tr',
15336             cn: [
15337             {
15338                 tag: 'td',
15339                 colspan: '7'
15340             }
15341             ]
15342         }
15343         ]
15344     },
15345     
15346     footer : {
15347         tag: 'tfoot',
15348         cn: [
15349         {
15350             tag: 'tr',
15351             cn: [
15352             {
15353                 tag: 'th',
15354                 colspan: '7',
15355                 cls: 'today'
15356             }
15357                     
15358             ]
15359         }
15360         ]
15361     },
15362     
15363     dates:{
15364         en: {
15365             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15366             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15367             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15368             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15369             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15370             today: "Today"
15371         }
15372     },
15373     
15374     modes: [
15375     {
15376         clsName: 'days',
15377         navFnc: 'Month',
15378         navStep: 1
15379     },
15380     {
15381         clsName: 'months',
15382         navFnc: 'FullYear',
15383         navStep: 1
15384     },
15385     {
15386         clsName: 'years',
15387         navFnc: 'FullYear',
15388         navStep: 10
15389     }]
15390 });
15391
15392 Roo.apply(Roo.bootstrap.DateField,  {
15393   
15394     template : {
15395         tag: 'div',
15396         cls: 'datepicker dropdown-menu',
15397         cn: [
15398         {
15399             tag: 'div',
15400             cls: 'datepicker-days',
15401             cn: [
15402             {
15403                 tag: 'table',
15404                 cls: 'table-condensed',
15405                 cn:[
15406                 Roo.bootstrap.DateField.head,
15407                 {
15408                     tag: 'tbody'
15409                 },
15410                 Roo.bootstrap.DateField.footer
15411                 ]
15412             }
15413             ]
15414         },
15415         {
15416             tag: 'div',
15417             cls: 'datepicker-months',
15418             cn: [
15419             {
15420                 tag: 'table',
15421                 cls: 'table-condensed',
15422                 cn:[
15423                 Roo.bootstrap.DateField.head,
15424                 Roo.bootstrap.DateField.content,
15425                 Roo.bootstrap.DateField.footer
15426                 ]
15427             }
15428             ]
15429         },
15430         {
15431             tag: 'div',
15432             cls: 'datepicker-years',
15433             cn: [
15434             {
15435                 tag: 'table',
15436                 cls: 'table-condensed',
15437                 cn:[
15438                 Roo.bootstrap.DateField.head,
15439                 Roo.bootstrap.DateField.content,
15440                 Roo.bootstrap.DateField.footer
15441                 ]
15442             }
15443             ]
15444         }
15445         ]
15446     }
15447 });
15448
15449  
15450
15451  /*
15452  * - LGPL
15453  *
15454  * TimeField
15455  * 
15456  */
15457
15458 /**
15459  * @class Roo.bootstrap.TimeField
15460  * @extends Roo.bootstrap.Input
15461  * Bootstrap DateField class
15462  * 
15463  * 
15464  * @constructor
15465  * Create a new TimeField
15466  * @param {Object} config The config object
15467  */
15468
15469 Roo.bootstrap.TimeField = function(config){
15470     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15471     this.addEvents({
15472             /**
15473              * @event show
15474              * Fires when this field show.
15475              * @param {Roo.bootstrap.DateField} this
15476              * @param {Mixed} date The date value
15477              */
15478             show : true,
15479             /**
15480              * @event show
15481              * Fires when this field hide.
15482              * @param {Roo.bootstrap.DateField} this
15483              * @param {Mixed} date The date value
15484              */
15485             hide : true,
15486             /**
15487              * @event select
15488              * Fires when select a date.
15489              * @param {Roo.bootstrap.DateField} this
15490              * @param {Mixed} date The date value
15491              */
15492             select : true
15493         });
15494 };
15495
15496 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15497     
15498     /**
15499      * @cfg {String} format
15500      * The default time format string which can be overriden for localization support.  The format must be
15501      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15502      */
15503     format : "H:i",
15504        
15505     onRender: function(ct, position)
15506     {
15507         
15508         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15509                 
15510         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15511         
15512         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15513         
15514         this.pop = this.picker().select('>.datepicker-time',true).first();
15515         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15516         
15517         this.picker().on('mousedown', this.onMousedown, this);
15518         this.picker().on('click', this.onClick, this);
15519         
15520         this.picker().addClass('datepicker-dropdown');
15521     
15522         this.fillTime();
15523         this.update();
15524             
15525         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15526         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15527         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15528         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15529         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15530         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15531
15532     },
15533     
15534     fireKey: function(e){
15535         if (!this.picker().isVisible()){
15536             if (e.keyCode == 27) // allow escape to hide and re-show picker
15537                 this.show();
15538             return;
15539         }
15540
15541         e.preventDefault();
15542         
15543         switch(e.keyCode){
15544             case 27: // escape
15545                 this.hide();
15546                 break;
15547             case 37: // left
15548             case 39: // right
15549                 this.onTogglePeriod();
15550                 break;
15551             case 38: // up
15552                 this.onIncrementMinutes();
15553                 break;
15554             case 40: // down
15555                 this.onDecrementMinutes();
15556                 break;
15557             case 13: // enter
15558             case 9: // tab
15559                 this.setTime();
15560                 break;
15561         }
15562     },
15563     
15564     onClick: function(e) {
15565         e.stopPropagation();
15566         e.preventDefault();
15567     },
15568     
15569     picker : function()
15570     {
15571         return this.el.select('.datepicker', true).first();
15572     },
15573     
15574     fillTime: function()
15575     {    
15576         var time = this.pop.select('tbody', true).first();
15577         
15578         time.dom.innerHTML = '';
15579         
15580         time.createChild({
15581             tag: 'tr',
15582             cn: [
15583                 {
15584                     tag: 'td',
15585                     cn: [
15586                         {
15587                             tag: 'a',
15588                             href: '#',
15589                             cls: 'btn',
15590                             cn: [
15591                                 {
15592                                     tag: 'span',
15593                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15594                                 }
15595                             ]
15596                         } 
15597                     ]
15598                 },
15599                 {
15600                     tag: 'td',
15601                     cls: 'separator'
15602                 },
15603                 {
15604                     tag: 'td',
15605                     cn: [
15606                         {
15607                             tag: 'a',
15608                             href: '#',
15609                             cls: 'btn',
15610                             cn: [
15611                                 {
15612                                     tag: 'span',
15613                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15614                                 }
15615                             ]
15616                         }
15617                     ]
15618                 },
15619                 {
15620                     tag: 'td',
15621                     cls: 'separator'
15622                 }
15623             ]
15624         });
15625         
15626         time.createChild({
15627             tag: 'tr',
15628             cn: [
15629                 {
15630                     tag: 'td',
15631                     cn: [
15632                         {
15633                             tag: 'span',
15634                             cls: 'timepicker-hour',
15635                             html: '00'
15636                         }  
15637                     ]
15638                 },
15639                 {
15640                     tag: 'td',
15641                     cls: 'separator',
15642                     html: ':'
15643                 },
15644                 {
15645                     tag: 'td',
15646                     cn: [
15647                         {
15648                             tag: 'span',
15649                             cls: 'timepicker-minute',
15650                             html: '00'
15651                         }  
15652                     ]
15653                 },
15654                 {
15655                     tag: 'td',
15656                     cls: 'separator'
15657                 },
15658                 {
15659                     tag: 'td',
15660                     cn: [
15661                         {
15662                             tag: 'button',
15663                             type: 'button',
15664                             cls: 'btn btn-primary period',
15665                             html: 'AM'
15666                             
15667                         }
15668                     ]
15669                 }
15670             ]
15671         });
15672         
15673         time.createChild({
15674             tag: 'tr',
15675             cn: [
15676                 {
15677                     tag: 'td',
15678                     cn: [
15679                         {
15680                             tag: 'a',
15681                             href: '#',
15682                             cls: 'btn',
15683                             cn: [
15684                                 {
15685                                     tag: 'span',
15686                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15687                                 }
15688                             ]
15689                         }
15690                     ]
15691                 },
15692                 {
15693                     tag: 'td',
15694                     cls: 'separator'
15695                 },
15696                 {
15697                     tag: 'td',
15698                     cn: [
15699                         {
15700                             tag: 'a',
15701                             href: '#',
15702                             cls: 'btn',
15703                             cn: [
15704                                 {
15705                                     tag: 'span',
15706                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15707                                 }
15708                             ]
15709                         }
15710                     ]
15711                 },
15712                 {
15713                     tag: 'td',
15714                     cls: 'separator'
15715                 }
15716             ]
15717         });
15718         
15719     },
15720     
15721     update: function()
15722     {
15723         
15724         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15725         
15726         this.fill();
15727     },
15728     
15729     fill: function() 
15730     {
15731         var hours = this.time.getHours();
15732         var minutes = this.time.getMinutes();
15733         var period = 'AM';
15734         
15735         if(hours > 11){
15736             period = 'PM';
15737         }
15738         
15739         if(hours == 0){
15740             hours = 12;
15741         }
15742         
15743         
15744         if(hours > 12){
15745             hours = hours - 12;
15746         }
15747         
15748         if(hours < 10){
15749             hours = '0' + hours;
15750         }
15751         
15752         if(minutes < 10){
15753             minutes = '0' + minutes;
15754         }
15755         
15756         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15757         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15758         this.pop.select('button', true).first().dom.innerHTML = period;
15759         
15760     },
15761     
15762     place: function()
15763     {   
15764         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15765         
15766         var cls = ['bottom'];
15767         
15768         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15769             cls.pop();
15770             cls.push('top');
15771         }
15772         
15773         cls.push('right');
15774         
15775         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15776             cls.pop();
15777             cls.push('left');
15778         }
15779         
15780         this.picker().addClass(cls.join('-'));
15781         
15782         var _this = this;
15783         
15784         Roo.each(cls, function(c){
15785             if(c == 'bottom'){
15786                 _this.picker().setTop(_this.inputEl().getHeight());
15787                 return;
15788             }
15789             if(c == 'top'){
15790                 _this.picker().setTop(0 - _this.picker().getHeight());
15791                 return;
15792             }
15793             
15794             if(c == 'left'){
15795                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15796                 return;
15797             }
15798             if(c == 'right'){
15799                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15800                 return;
15801             }
15802         });
15803         
15804     },
15805   
15806     onFocus : function()
15807     {
15808         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15809         this.show();
15810     },
15811     
15812     onBlur : function()
15813     {
15814         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15815         this.hide();
15816     },
15817     
15818     show : function()
15819     {
15820         this.picker().show();
15821         this.pop.show();
15822         this.update();
15823         this.place();
15824         
15825         this.fireEvent('show', this, this.date);
15826     },
15827     
15828     hide : function()
15829     {
15830         this.picker().hide();
15831         this.pop.hide();
15832         
15833         this.fireEvent('hide', this, this.date);
15834     },
15835     
15836     setTime : function()
15837     {
15838         this.hide();
15839         this.setValue(this.time.format(this.format));
15840         
15841         this.fireEvent('select', this, this.date);
15842         
15843         
15844     },
15845     
15846     onMousedown: function(e){
15847         e.stopPropagation();
15848         e.preventDefault();
15849     },
15850     
15851     onIncrementHours: function()
15852     {
15853         Roo.log('onIncrementHours');
15854         this.time = this.time.add(Date.HOUR, 1);
15855         this.update();
15856         
15857     },
15858     
15859     onDecrementHours: function()
15860     {
15861         Roo.log('onDecrementHours');
15862         this.time = this.time.add(Date.HOUR, -1);
15863         this.update();
15864     },
15865     
15866     onIncrementMinutes: function()
15867     {
15868         Roo.log('onIncrementMinutes');
15869         this.time = this.time.add(Date.MINUTE, 1);
15870         this.update();
15871     },
15872     
15873     onDecrementMinutes: function()
15874     {
15875         Roo.log('onDecrementMinutes');
15876         this.time = this.time.add(Date.MINUTE, -1);
15877         this.update();
15878     },
15879     
15880     onTogglePeriod: function()
15881     {
15882         Roo.log('onTogglePeriod');
15883         this.time = this.time.add(Date.HOUR, 12);
15884         this.update();
15885     }
15886     
15887    
15888 });
15889
15890 Roo.apply(Roo.bootstrap.TimeField,  {
15891     
15892     content : {
15893         tag: 'tbody',
15894         cn: [
15895             {
15896                 tag: 'tr',
15897                 cn: [
15898                 {
15899                     tag: 'td',
15900                     colspan: '7'
15901                 }
15902                 ]
15903             }
15904         ]
15905     },
15906     
15907     footer : {
15908         tag: 'tfoot',
15909         cn: [
15910             {
15911                 tag: 'tr',
15912                 cn: [
15913                 {
15914                     tag: 'th',
15915                     colspan: '7',
15916                     cls: '',
15917                     cn: [
15918                         {
15919                             tag: 'button',
15920                             cls: 'btn btn-info ok',
15921                             html: 'OK'
15922                         }
15923                     ]
15924                 }
15925
15926                 ]
15927             }
15928         ]
15929     }
15930 });
15931
15932 Roo.apply(Roo.bootstrap.TimeField,  {
15933   
15934     template : {
15935         tag: 'div',
15936         cls: 'datepicker dropdown-menu',
15937         cn: [
15938             {
15939                 tag: 'div',
15940                 cls: 'datepicker-time',
15941                 cn: [
15942                 {
15943                     tag: 'table',
15944                     cls: 'table-condensed',
15945                     cn:[
15946                     Roo.bootstrap.TimeField.content,
15947                     Roo.bootstrap.TimeField.footer
15948                     ]
15949                 }
15950                 ]
15951             }
15952         ]
15953     }
15954 });
15955
15956  
15957
15958  /*
15959  * - LGPL
15960  *
15961  * CheckBox
15962  * 
15963  */
15964
15965 /**
15966  * @class Roo.bootstrap.CheckBox
15967  * @extends Roo.bootstrap.Input
15968  * Bootstrap CheckBox class
15969  * 
15970  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15971  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15972  * @cfg {String} boxLabel The text that appears beside the checkbox
15973  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15974  * @cfg {Boolean} checked initnal the element
15975  * 
15976  * 
15977  * @constructor
15978  * Create a new CheckBox
15979  * @param {Object} config The config object
15980  */
15981
15982 Roo.bootstrap.CheckBox = function(config){
15983     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15984    
15985         this.addEvents({
15986             /**
15987             * @event check
15988             * Fires when the element is checked or unchecked.
15989             * @param {Roo.bootstrap.CheckBox} this This input
15990             * @param {Boolean} checked The new checked value
15991             */
15992            check : true
15993         });
15994 };
15995
15996 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15997     
15998     inputType: 'checkbox',
15999     inputValue: 1,
16000     valueOff: 0,
16001     boxLabel: false,
16002     checked: false,
16003     weight : false,
16004     
16005     getAutoCreate : function()
16006     {
16007         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16008         
16009         var id = Roo.id();
16010         
16011         var cfg = {};
16012         
16013         cfg.cls = 'form-group checkbox' //input-group
16014         
16015         
16016         
16017         
16018         var input =  {
16019             tag: 'input',
16020             id : id,
16021             type : this.inputType,
16022             value : (!this.checked) ? this.valueOff : this.inputValue,
16023             cls : 'roo-checkbox', //'form-box',
16024             placeholder : this.placeholder || ''
16025             
16026         };
16027         
16028         if (this.weight) { // Validity check?
16029             cfg.cls += " checkbox-" + this.weight;
16030         }
16031         
16032         if (this.disabled) {
16033             input.disabled=true;
16034         }
16035         
16036         if(this.checked){
16037             input.checked = this.checked;
16038         }
16039         
16040         if (this.name) {
16041             input.name = this.name;
16042         }
16043         
16044         if (this.size) {
16045             input.cls += ' input-' + this.size;
16046         }
16047         
16048         var settings=this;
16049         ['xs','sm','md','lg'].map(function(size){
16050             if (settings[size]) {
16051                 cfg.cls += ' col-' + size + '-' + settings[size];
16052             }
16053         });
16054         
16055        
16056         
16057         var inputblock = input;
16058         
16059         
16060         
16061         
16062         if (this.before || this.after) {
16063             
16064             inputblock = {
16065                 cls : 'input-group',
16066                 cn :  [] 
16067             };
16068             if (this.before) {
16069                 inputblock.cn.push({
16070                     tag :'span',
16071                     cls : 'input-group-addon',
16072                     html : this.before
16073                 });
16074             }
16075             inputblock.cn.push(input);
16076             if (this.after) {
16077                 inputblock.cn.push({
16078                     tag :'span',
16079                     cls : 'input-group-addon',
16080                     html : this.after
16081                 });
16082             }
16083             
16084         };
16085         
16086         if (align ==='left' && this.fieldLabel.length) {
16087                 Roo.log("left and has label");
16088                 cfg.cn = [
16089                     
16090                     {
16091                         tag: 'label',
16092                         'for' :  id,
16093                         cls : 'control-label col-md-' + this.labelWidth,
16094                         html : this.fieldLabel
16095                         
16096                     },
16097                     {
16098                         cls : "col-md-" + (12 - this.labelWidth), 
16099                         cn: [
16100                             inputblock
16101                         ]
16102                     }
16103                     
16104                 ];
16105         } else if ( this.fieldLabel.length) {
16106                 Roo.log(" label");
16107                 cfg.cn = [
16108                    
16109                     {
16110                         tag: this.boxLabel ? 'span' : 'label',
16111                         'for': id,
16112                         cls: 'control-label box-input-label',
16113                         //cls : 'input-group-addon',
16114                         html : this.fieldLabel
16115                         
16116                     },
16117                     
16118                     inputblock
16119                     
16120                 ];
16121
16122         } else {
16123             
16124                 Roo.log(" no label && no align");
16125                 cfg.cn = [  inputblock ] ;
16126                 
16127                 
16128         };
16129          if(this.boxLabel){
16130             cfg.cn.push( {
16131                 tag: 'label',
16132                 'for': id,
16133                 cls: 'box-label',
16134                 html: this.boxLabel
16135                 
16136             });
16137         }
16138         
16139         
16140        
16141         return cfg;
16142         
16143     },
16144     
16145     /**
16146      * return the real input element.
16147      */
16148     inputEl: function ()
16149     {
16150         return this.el.select('input.roo-checkbox',true).first();
16151     },
16152     
16153     label: function()
16154     {
16155         return this.el.select('label.control-label',true).first();
16156     },
16157     
16158     initEvents : function()
16159     {
16160 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16161         
16162         this.inputEl().on('click', this.onClick,  this);
16163         
16164     },
16165     
16166     onClick : function()
16167     {   
16168         this.setChecked(!this.checked);
16169     },
16170     
16171     setChecked : function(state,suppressEvent)
16172     {
16173         this.checked = state;
16174         
16175         this.inputEl().dom.checked = state;
16176         
16177         if(suppressEvent !== true){
16178             this.fireEvent('check', this, state);
16179         }
16180         
16181         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16182         
16183     },
16184     
16185     setValue : function(v,suppressEvent)
16186     {
16187         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16188     }
16189     
16190 });
16191
16192  
16193 /*
16194  * - LGPL
16195  *
16196  * Radio
16197  * 
16198  */
16199
16200 /**
16201  * @class Roo.bootstrap.Radio
16202  * @extends Roo.bootstrap.CheckBox
16203  * Bootstrap Radio class
16204
16205  * @constructor
16206  * Create a new Radio
16207  * @param {Object} config The config object
16208  */
16209
16210 Roo.bootstrap.Radio = function(config){
16211     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16212    
16213 };
16214
16215 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16216     
16217     inputType: 'radio',
16218     inputValue: '',
16219     valueOff: '',
16220     
16221     getAutoCreate : function()
16222     {
16223         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16224         
16225         var id = Roo.id();
16226         
16227         var cfg = {};
16228         
16229         cfg.cls = 'form-group radio' //input-group
16230         
16231         var input =  {
16232             tag: 'input',
16233             id : id,
16234             type : this.inputType,
16235             value : (!this.checked) ? this.valueOff : this.inputValue,
16236             cls : 'roo-radio',
16237             placeholder : this.placeholder || ''
16238             
16239         };
16240           if (this.weight) { // Validity check?
16241             cfg.cls += " radio-" + this.weight;
16242         }
16243         if (this.disabled) {
16244             input.disabled=true;
16245         }
16246         
16247         if(this.checked){
16248             input.checked = this.checked;
16249         }
16250         
16251         if (this.name) {
16252             input.name = this.name;
16253         }
16254         
16255         if (this.size) {
16256             input.cls += ' input-' + this.size;
16257         }
16258         
16259         var settings=this;
16260         ['xs','sm','md','lg'].map(function(size){
16261             if (settings[size]) {
16262                 cfg.cls += ' col-' + size + '-' + settings[size];
16263             }
16264         });
16265         
16266         var inputblock = input;
16267         
16268         if (this.before || this.after) {
16269             
16270             inputblock = {
16271                 cls : 'input-group',
16272                 cn :  [] 
16273             };
16274             if (this.before) {
16275                 inputblock.cn.push({
16276                     tag :'span',
16277                     cls : 'input-group-addon',
16278                     html : this.before
16279                 });
16280             }
16281             inputblock.cn.push(input);
16282             if (this.after) {
16283                 inputblock.cn.push({
16284                     tag :'span',
16285                     cls : 'input-group-addon',
16286                     html : this.after
16287                 });
16288             }
16289             
16290         };
16291         
16292         if (align ==='left' && this.fieldLabel.length) {
16293                 Roo.log("left and has label");
16294                 cfg.cn = [
16295                     
16296                     {
16297                         tag: 'label',
16298                         'for' :  id,
16299                         cls : 'control-label col-md-' + this.labelWidth,
16300                         html : this.fieldLabel
16301                         
16302                     },
16303                     {
16304                         cls : "col-md-" + (12 - this.labelWidth), 
16305                         cn: [
16306                             inputblock
16307                         ]
16308                     }
16309                     
16310                 ];
16311         } else if ( this.fieldLabel.length) {
16312                 Roo.log(" label");
16313                  cfg.cn = [
16314                    
16315                     {
16316                         tag: 'label',
16317                         'for': id,
16318                         cls: 'control-label box-input-label',
16319                         //cls : 'input-group-addon',
16320                         html : this.fieldLabel
16321                         
16322                     },
16323                     
16324                     inputblock
16325                     
16326                 ];
16327
16328         } else {
16329             
16330                    Roo.log(" no label && no align");
16331                 cfg.cn = [
16332                     
16333                         inputblock
16334                     
16335                 ];
16336                 
16337                 
16338         };
16339         
16340         if(this.boxLabel){
16341             cfg.cn.push({
16342                 tag: 'label',
16343                 'for': id,
16344                 cls: 'box-label',
16345                 html: this.boxLabel
16346             })
16347         }
16348         
16349         return cfg;
16350         
16351     },
16352     inputEl: function ()
16353     {
16354         return this.el.select('input.roo-radio',true).first();
16355     },
16356     onClick : function()
16357     {   
16358         this.setChecked(true);
16359     },
16360     
16361     setChecked : function(state,suppressEvent)
16362     {
16363         if(state){
16364             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16365                 v.dom.checked = false;
16366             });
16367         }
16368         
16369         this.checked = state;
16370         this.inputEl().dom.checked = state;
16371         
16372         if(suppressEvent !== true){
16373             this.fireEvent('check', this, state);
16374         }
16375         
16376         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16377         
16378     },
16379     
16380     getGroupValue : function()
16381     {
16382         var value = ''
16383         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16384             if(v.dom.checked == true){
16385                 value = v.dom.value;
16386             }
16387         });
16388         
16389         return value;
16390     },
16391     
16392     /**
16393      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16394      * @return {Mixed} value The field value
16395      */
16396     getValue : function(){
16397         return this.getGroupValue();
16398     }
16399     
16400 });
16401
16402  
16403 //<script type="text/javascript">
16404
16405 /*
16406  * Based  Ext JS Library 1.1.1
16407  * Copyright(c) 2006-2007, Ext JS, LLC.
16408  * LGPL
16409  *
16410  */
16411  
16412 /**
16413  * @class Roo.HtmlEditorCore
16414  * @extends Roo.Component
16415  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16416  *
16417  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16418  */
16419
16420 Roo.HtmlEditorCore = function(config){
16421     
16422     
16423     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16424     
16425     
16426     this.addEvents({
16427         /**
16428          * @event initialize
16429          * Fires when the editor is fully initialized (including the iframe)
16430          * @param {Roo.HtmlEditorCore} this
16431          */
16432         initialize: true,
16433         /**
16434          * @event activate
16435          * Fires when the editor is first receives the focus. Any insertion must wait
16436          * until after this event.
16437          * @param {Roo.HtmlEditorCore} this
16438          */
16439         activate: true,
16440          /**
16441          * @event beforesync
16442          * Fires before the textarea is updated with content from the editor iframe. Return false
16443          * to cancel the sync.
16444          * @param {Roo.HtmlEditorCore} this
16445          * @param {String} html
16446          */
16447         beforesync: true,
16448          /**
16449          * @event beforepush
16450          * Fires before the iframe editor is updated with content from the textarea. Return false
16451          * to cancel the push.
16452          * @param {Roo.HtmlEditorCore} this
16453          * @param {String} html
16454          */
16455         beforepush: true,
16456          /**
16457          * @event sync
16458          * Fires when the textarea is updated with content from the editor iframe.
16459          * @param {Roo.HtmlEditorCore} this
16460          * @param {String} html
16461          */
16462         sync: true,
16463          /**
16464          * @event push
16465          * Fires when the iframe editor is updated with content from the textarea.
16466          * @param {Roo.HtmlEditorCore} this
16467          * @param {String} html
16468          */
16469         push: true,
16470         
16471         /**
16472          * @event editorevent
16473          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16474          * @param {Roo.HtmlEditorCore} this
16475          */
16476         editorevent: true
16477     });
16478     
16479     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16480     
16481     // defaults : white / black...
16482     this.applyBlacklists();
16483     
16484     
16485     
16486 };
16487
16488
16489 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16490
16491
16492      /**
16493      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16494      */
16495     
16496     owner : false,
16497     
16498      /**
16499      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16500      *                        Roo.resizable.
16501      */
16502     resizable : false,
16503      /**
16504      * @cfg {Number} height (in pixels)
16505      */   
16506     height: 300,
16507    /**
16508      * @cfg {Number} width (in pixels)
16509      */   
16510     width: 500,
16511     
16512     /**
16513      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16514      * 
16515      */
16516     stylesheets: false,
16517     
16518     // id of frame..
16519     frameId: false,
16520     
16521     // private properties
16522     validationEvent : false,
16523     deferHeight: true,
16524     initialized : false,
16525     activated : false,
16526     sourceEditMode : false,
16527     onFocus : Roo.emptyFn,
16528     iframePad:3,
16529     hideMode:'offsets',
16530     
16531     clearUp: true,
16532     
16533     // blacklist + whitelisted elements..
16534     black: false,
16535     white: false,
16536      
16537     
16538
16539     /**
16540      * Protected method that will not generally be called directly. It
16541      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16542      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16543      */
16544     getDocMarkup : function(){
16545         // body styles..
16546         var st = '';
16547         Roo.log(this.stylesheets);
16548         
16549         // inherit styels from page...?? 
16550         if (this.stylesheets === false) {
16551             
16552             Roo.get(document.head).select('style').each(function(node) {
16553                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16554             });
16555             
16556             Roo.get(document.head).select('link').each(function(node) { 
16557                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16558             });
16559             
16560         } else if (!this.stylesheets.length) {
16561                 // simple..
16562                 st = '<style type="text/css">' +
16563                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16564                    '</style>';
16565         } else {
16566             Roo.each(this.stylesheets, function(s) {
16567                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16568             });
16569             
16570         }
16571         
16572         st +=  '<style type="text/css">' +
16573             'IMG { cursor: pointer } ' +
16574         '</style>';
16575
16576         
16577         return '<html><head>' + st  +
16578             //<style type="text/css">' +
16579             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16580             //'</style>' +
16581             ' </head><body class="roo-htmleditor-body"></body></html>';
16582     },
16583
16584     // private
16585     onRender : function(ct, position)
16586     {
16587         var _t = this;
16588         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16589         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16590         
16591         
16592         this.el.dom.style.border = '0 none';
16593         this.el.dom.setAttribute('tabIndex', -1);
16594         this.el.addClass('x-hidden hide');
16595         
16596         
16597         
16598         if(Roo.isIE){ // fix IE 1px bogus margin
16599             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16600         }
16601        
16602         
16603         this.frameId = Roo.id();
16604         
16605          
16606         
16607         var iframe = this.owner.wrap.createChild({
16608             tag: 'iframe',
16609             cls: 'form-control', // bootstrap..
16610             id: this.frameId,
16611             name: this.frameId,
16612             frameBorder : 'no',
16613             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16614         }, this.el
16615         );
16616         
16617         
16618         this.iframe = iframe.dom;
16619
16620          this.assignDocWin();
16621         
16622         this.doc.designMode = 'on';
16623        
16624         this.doc.open();
16625         this.doc.write(this.getDocMarkup());
16626         this.doc.close();
16627
16628         
16629         var task = { // must defer to wait for browser to be ready
16630             run : function(){
16631                 //console.log("run task?" + this.doc.readyState);
16632                 this.assignDocWin();
16633                 if(this.doc.body || this.doc.readyState == 'complete'){
16634                     try {
16635                         this.doc.designMode="on";
16636                     } catch (e) {
16637                         return;
16638                     }
16639                     Roo.TaskMgr.stop(task);
16640                     this.initEditor.defer(10, this);
16641                 }
16642             },
16643             interval : 10,
16644             duration: 10000,
16645             scope: this
16646         };
16647         Roo.TaskMgr.start(task);
16648
16649         
16650          
16651     },
16652
16653     // private
16654     onResize : function(w, h)
16655     {
16656          Roo.log('resize: ' +w + ',' + h );
16657         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16658         if(!this.iframe){
16659             return;
16660         }
16661         if(typeof w == 'number'){
16662             
16663             this.iframe.style.width = w + 'px';
16664         }
16665         if(typeof h == 'number'){
16666             
16667             this.iframe.style.height = h + 'px';
16668             if(this.doc){
16669                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16670             }
16671         }
16672         
16673     },
16674
16675     /**
16676      * Toggles the editor between standard and source edit mode.
16677      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16678      */
16679     toggleSourceEdit : function(sourceEditMode){
16680         
16681         this.sourceEditMode = sourceEditMode === true;
16682         
16683         if(this.sourceEditMode){
16684  
16685             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16686             
16687         }else{
16688             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16689             //this.iframe.className = '';
16690             this.deferFocus();
16691         }
16692         //this.setSize(this.owner.wrap.getSize());
16693         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16694     },
16695
16696     
16697   
16698
16699     /**
16700      * Protected method that will not generally be called directly. If you need/want
16701      * custom HTML cleanup, this is the method you should override.
16702      * @param {String} html The HTML to be cleaned
16703      * return {String} The cleaned HTML
16704      */
16705     cleanHtml : function(html){
16706         html = String(html);
16707         if(html.length > 5){
16708             if(Roo.isSafari){ // strip safari nonsense
16709                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16710             }
16711         }
16712         if(html == '&nbsp;'){
16713             html = '';
16714         }
16715         return html;
16716     },
16717
16718     /**
16719      * HTML Editor -> Textarea
16720      * Protected method that will not generally be called directly. Syncs the contents
16721      * of the editor iframe with the textarea.
16722      */
16723     syncValue : function(){
16724         if(this.initialized){
16725             var bd = (this.doc.body || this.doc.documentElement);
16726             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16727             var html = bd.innerHTML;
16728             if(Roo.isSafari){
16729                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16730                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16731                 if(m && m[1]){
16732                     html = '<div style="'+m[0]+'">' + html + '</div>';
16733                 }
16734             }
16735             html = this.cleanHtml(html);
16736             // fix up the special chars.. normaly like back quotes in word...
16737             // however we do not want to do this with chinese..
16738             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16739                 var cc = b.charCodeAt();
16740                 if (
16741                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16742                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16743                     (cc >= 0xf900 && cc < 0xfb00 )
16744                 ) {
16745                         return b;
16746                 }
16747                 return "&#"+cc+";" 
16748             });
16749             if(this.owner.fireEvent('beforesync', this, html) !== false){
16750                 this.el.dom.value = html;
16751                 this.owner.fireEvent('sync', this, html);
16752             }
16753         }
16754     },
16755
16756     /**
16757      * Protected method that will not generally be called directly. Pushes the value of the textarea
16758      * into the iframe editor.
16759      */
16760     pushValue : function(){
16761         if(this.initialized){
16762             var v = this.el.dom.value.trim();
16763             
16764 //            if(v.length < 1){
16765 //                v = '&#160;';
16766 //            }
16767             
16768             if(this.owner.fireEvent('beforepush', this, v) !== false){
16769                 var d = (this.doc.body || this.doc.documentElement);
16770                 d.innerHTML = v;
16771                 this.cleanUpPaste();
16772                 this.el.dom.value = d.innerHTML;
16773                 this.owner.fireEvent('push', this, v);
16774             }
16775         }
16776     },
16777
16778     // private
16779     deferFocus : function(){
16780         this.focus.defer(10, this);
16781     },
16782
16783     // doc'ed in Field
16784     focus : function(){
16785         if(this.win && !this.sourceEditMode){
16786             this.win.focus();
16787         }else{
16788             this.el.focus();
16789         }
16790     },
16791     
16792     assignDocWin: function()
16793     {
16794         var iframe = this.iframe;
16795         
16796          if(Roo.isIE){
16797             this.doc = iframe.contentWindow.document;
16798             this.win = iframe.contentWindow;
16799         } else {
16800 //            if (!Roo.get(this.frameId)) {
16801 //                return;
16802 //            }
16803 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16804 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16805             
16806             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16807                 return;
16808             }
16809             
16810             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16811             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16812         }
16813     },
16814     
16815     // private
16816     initEditor : function(){
16817         //console.log("INIT EDITOR");
16818         this.assignDocWin();
16819         
16820         
16821         
16822         this.doc.designMode="on";
16823         this.doc.open();
16824         this.doc.write(this.getDocMarkup());
16825         this.doc.close();
16826         
16827         var dbody = (this.doc.body || this.doc.documentElement);
16828         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16829         // this copies styles from the containing element into thsi one..
16830         // not sure why we need all of this..
16831         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16832         
16833         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16834         //ss['background-attachment'] = 'fixed'; // w3c
16835         dbody.bgProperties = 'fixed'; // ie
16836         //Roo.DomHelper.applyStyles(dbody, ss);
16837         Roo.EventManager.on(this.doc, {
16838             //'mousedown': this.onEditorEvent,
16839             'mouseup': this.onEditorEvent,
16840             'dblclick': this.onEditorEvent,
16841             'click': this.onEditorEvent,
16842             'keyup': this.onEditorEvent,
16843             buffer:100,
16844             scope: this
16845         });
16846         if(Roo.isGecko){
16847             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16848         }
16849         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16850             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16851         }
16852         this.initialized = true;
16853
16854         this.owner.fireEvent('initialize', this);
16855         this.pushValue();
16856     },
16857
16858     // private
16859     onDestroy : function(){
16860         
16861         
16862         
16863         if(this.rendered){
16864             
16865             //for (var i =0; i < this.toolbars.length;i++) {
16866             //    // fixme - ask toolbars for heights?
16867             //    this.toolbars[i].onDestroy();
16868            // }
16869             
16870             //this.wrap.dom.innerHTML = '';
16871             //this.wrap.remove();
16872         }
16873     },
16874
16875     // private
16876     onFirstFocus : function(){
16877         
16878         this.assignDocWin();
16879         
16880         
16881         this.activated = true;
16882          
16883     
16884         if(Roo.isGecko){ // prevent silly gecko errors
16885             this.win.focus();
16886             var s = this.win.getSelection();
16887             if(!s.focusNode || s.focusNode.nodeType != 3){
16888                 var r = s.getRangeAt(0);
16889                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16890                 r.collapse(true);
16891                 this.deferFocus();
16892             }
16893             try{
16894                 this.execCmd('useCSS', true);
16895                 this.execCmd('styleWithCSS', false);
16896             }catch(e){}
16897         }
16898         this.owner.fireEvent('activate', this);
16899     },
16900
16901     // private
16902     adjustFont: function(btn){
16903         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16904         //if(Roo.isSafari){ // safari
16905         //    adjust *= 2;
16906        // }
16907         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16908         if(Roo.isSafari){ // safari
16909             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16910             v =  (v < 10) ? 10 : v;
16911             v =  (v > 48) ? 48 : v;
16912             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16913             
16914         }
16915         
16916         
16917         v = Math.max(1, v+adjust);
16918         
16919         this.execCmd('FontSize', v  );
16920     },
16921
16922     onEditorEvent : function(e){
16923         this.owner.fireEvent('editorevent', this, e);
16924       //  this.updateToolbar();
16925         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16926     },
16927
16928     insertTag : function(tg)
16929     {
16930         // could be a bit smarter... -> wrap the current selected tRoo..
16931         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16932             
16933             range = this.createRange(this.getSelection());
16934             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16935             wrappingNode.appendChild(range.extractContents());
16936             range.insertNode(wrappingNode);
16937
16938             return;
16939             
16940             
16941             
16942         }
16943         this.execCmd("formatblock",   tg);
16944         
16945     },
16946     
16947     insertText : function(txt)
16948     {
16949         
16950         
16951         var range = this.createRange();
16952         range.deleteContents();
16953                //alert(Sender.getAttribute('label'));
16954                
16955         range.insertNode(this.doc.createTextNode(txt));
16956     } ,
16957     
16958      
16959
16960     /**
16961      * Executes a Midas editor command on the editor document and performs necessary focus and
16962      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16963      * @param {String} cmd The Midas command
16964      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16965      */
16966     relayCmd : function(cmd, value){
16967         this.win.focus();
16968         this.execCmd(cmd, value);
16969         this.owner.fireEvent('editorevent', this);
16970         //this.updateToolbar();
16971         this.owner.deferFocus();
16972     },
16973
16974     /**
16975      * Executes a Midas editor command directly on the editor document.
16976      * For visual commands, you should use {@link #relayCmd} instead.
16977      * <b>This should only be called after the editor is initialized.</b>
16978      * @param {String} cmd The Midas command
16979      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16980      */
16981     execCmd : function(cmd, value){
16982         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16983         this.syncValue();
16984     },
16985  
16986  
16987    
16988     /**
16989      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16990      * to insert tRoo.
16991      * @param {String} text | dom node.. 
16992      */
16993     insertAtCursor : function(text)
16994     {
16995         
16996         
16997         
16998         if(!this.activated){
16999             return;
17000         }
17001         /*
17002         if(Roo.isIE){
17003             this.win.focus();
17004             var r = this.doc.selection.createRange();
17005             if(r){
17006                 r.collapse(true);
17007                 r.pasteHTML(text);
17008                 this.syncValue();
17009                 this.deferFocus();
17010             
17011             }
17012             return;
17013         }
17014         */
17015         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17016             this.win.focus();
17017             
17018             
17019             // from jquery ui (MIT licenced)
17020             var range, node;
17021             var win = this.win;
17022             
17023             if (win.getSelection && win.getSelection().getRangeAt) {
17024                 range = win.getSelection().getRangeAt(0);
17025                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17026                 range.insertNode(node);
17027             } else if (win.document.selection && win.document.selection.createRange) {
17028                 // no firefox support
17029                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17030                 win.document.selection.createRange().pasteHTML(txt);
17031             } else {
17032                 // no firefox support
17033                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17034                 this.execCmd('InsertHTML', txt);
17035             } 
17036             
17037             this.syncValue();
17038             
17039             this.deferFocus();
17040         }
17041     },
17042  // private
17043     mozKeyPress : function(e){
17044         if(e.ctrlKey){
17045             var c = e.getCharCode(), cmd;
17046           
17047             if(c > 0){
17048                 c = String.fromCharCode(c).toLowerCase();
17049                 switch(c){
17050                     case 'b':
17051                         cmd = 'bold';
17052                         break;
17053                     case 'i':
17054                         cmd = 'italic';
17055                         break;
17056                     
17057                     case 'u':
17058                         cmd = 'underline';
17059                         break;
17060                     
17061                     case 'v':
17062                         this.cleanUpPaste.defer(100, this);
17063                         return;
17064                         
17065                 }
17066                 if(cmd){
17067                     this.win.focus();
17068                     this.execCmd(cmd);
17069                     this.deferFocus();
17070                     e.preventDefault();
17071                 }
17072                 
17073             }
17074         }
17075     },
17076
17077     // private
17078     fixKeys : function(){ // load time branching for fastest keydown performance
17079         if(Roo.isIE){
17080             return function(e){
17081                 var k = e.getKey(), r;
17082                 if(k == e.TAB){
17083                     e.stopEvent();
17084                     r = this.doc.selection.createRange();
17085                     if(r){
17086                         r.collapse(true);
17087                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17088                         this.deferFocus();
17089                     }
17090                     return;
17091                 }
17092                 
17093                 if(k == e.ENTER){
17094                     r = this.doc.selection.createRange();
17095                     if(r){
17096                         var target = r.parentElement();
17097                         if(!target || target.tagName.toLowerCase() != 'li'){
17098                             e.stopEvent();
17099                             r.pasteHTML('<br />');
17100                             r.collapse(false);
17101                             r.select();
17102                         }
17103                     }
17104                 }
17105                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17106                     this.cleanUpPaste.defer(100, this);
17107                     return;
17108                 }
17109                 
17110                 
17111             };
17112         }else if(Roo.isOpera){
17113             return function(e){
17114                 var k = e.getKey();
17115                 if(k == e.TAB){
17116                     e.stopEvent();
17117                     this.win.focus();
17118                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17119                     this.deferFocus();
17120                 }
17121                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17122                     this.cleanUpPaste.defer(100, this);
17123                     return;
17124                 }
17125                 
17126             };
17127         }else if(Roo.isSafari){
17128             return function(e){
17129                 var k = e.getKey();
17130                 
17131                 if(k == e.TAB){
17132                     e.stopEvent();
17133                     this.execCmd('InsertText','\t');
17134                     this.deferFocus();
17135                     return;
17136                 }
17137                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17138                     this.cleanUpPaste.defer(100, this);
17139                     return;
17140                 }
17141                 
17142              };
17143         }
17144     }(),
17145     
17146     getAllAncestors: function()
17147     {
17148         var p = this.getSelectedNode();
17149         var a = [];
17150         if (!p) {
17151             a.push(p); // push blank onto stack..
17152             p = this.getParentElement();
17153         }
17154         
17155         
17156         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17157             a.push(p);
17158             p = p.parentNode;
17159         }
17160         a.push(this.doc.body);
17161         return a;
17162     },
17163     lastSel : false,
17164     lastSelNode : false,
17165     
17166     
17167     getSelection : function() 
17168     {
17169         this.assignDocWin();
17170         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17171     },
17172     
17173     getSelectedNode: function() 
17174     {
17175         // this may only work on Gecko!!!
17176         
17177         // should we cache this!!!!
17178         
17179         
17180         
17181          
17182         var range = this.createRange(this.getSelection()).cloneRange();
17183         
17184         if (Roo.isIE) {
17185             var parent = range.parentElement();
17186             while (true) {
17187                 var testRange = range.duplicate();
17188                 testRange.moveToElementText(parent);
17189                 if (testRange.inRange(range)) {
17190                     break;
17191                 }
17192                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17193                     break;
17194                 }
17195                 parent = parent.parentElement;
17196             }
17197             return parent;
17198         }
17199         
17200         // is ancestor a text element.
17201         var ac =  range.commonAncestorContainer;
17202         if (ac.nodeType == 3) {
17203             ac = ac.parentNode;
17204         }
17205         
17206         var ar = ac.childNodes;
17207          
17208         var nodes = [];
17209         var other_nodes = [];
17210         var has_other_nodes = false;
17211         for (var i=0;i<ar.length;i++) {
17212             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17213                 continue;
17214             }
17215             // fullly contained node.
17216             
17217             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17218                 nodes.push(ar[i]);
17219                 continue;
17220             }
17221             
17222             // probably selected..
17223             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17224                 other_nodes.push(ar[i]);
17225                 continue;
17226             }
17227             // outer..
17228             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17229                 continue;
17230             }
17231             
17232             
17233             has_other_nodes = true;
17234         }
17235         if (!nodes.length && other_nodes.length) {
17236             nodes= other_nodes;
17237         }
17238         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17239             return false;
17240         }
17241         
17242         return nodes[0];
17243     },
17244     createRange: function(sel)
17245     {
17246         // this has strange effects when using with 
17247         // top toolbar - not sure if it's a great idea.
17248         //this.editor.contentWindow.focus();
17249         if (typeof sel != "undefined") {
17250             try {
17251                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17252             } catch(e) {
17253                 return this.doc.createRange();
17254             }
17255         } else {
17256             return this.doc.createRange();
17257         }
17258     },
17259     getParentElement: function()
17260     {
17261         
17262         this.assignDocWin();
17263         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17264         
17265         var range = this.createRange(sel);
17266          
17267         try {
17268             var p = range.commonAncestorContainer;
17269             while (p.nodeType == 3) { // text node
17270                 p = p.parentNode;
17271             }
17272             return p;
17273         } catch (e) {
17274             return null;
17275         }
17276     
17277     },
17278     /***
17279      *
17280      * Range intersection.. the hard stuff...
17281      *  '-1' = before
17282      *  '0' = hits..
17283      *  '1' = after.
17284      *         [ -- selected range --- ]
17285      *   [fail]                        [fail]
17286      *
17287      *    basically..
17288      *      if end is before start or  hits it. fail.
17289      *      if start is after end or hits it fail.
17290      *
17291      *   if either hits (but other is outside. - then it's not 
17292      *   
17293      *    
17294      **/
17295     
17296     
17297     // @see http://www.thismuchiknow.co.uk/?p=64.
17298     rangeIntersectsNode : function(range, node)
17299     {
17300         var nodeRange = node.ownerDocument.createRange();
17301         try {
17302             nodeRange.selectNode(node);
17303         } catch (e) {
17304             nodeRange.selectNodeContents(node);
17305         }
17306     
17307         var rangeStartRange = range.cloneRange();
17308         rangeStartRange.collapse(true);
17309     
17310         var rangeEndRange = range.cloneRange();
17311         rangeEndRange.collapse(false);
17312     
17313         var nodeStartRange = nodeRange.cloneRange();
17314         nodeStartRange.collapse(true);
17315     
17316         var nodeEndRange = nodeRange.cloneRange();
17317         nodeEndRange.collapse(false);
17318     
17319         return rangeStartRange.compareBoundaryPoints(
17320                  Range.START_TO_START, nodeEndRange) == -1 &&
17321                rangeEndRange.compareBoundaryPoints(
17322                  Range.START_TO_START, nodeStartRange) == 1;
17323         
17324          
17325     },
17326     rangeCompareNode : function(range, node)
17327     {
17328         var nodeRange = node.ownerDocument.createRange();
17329         try {
17330             nodeRange.selectNode(node);
17331         } catch (e) {
17332             nodeRange.selectNodeContents(node);
17333         }
17334         
17335         
17336         range.collapse(true);
17337     
17338         nodeRange.collapse(true);
17339      
17340         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17341         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17342          
17343         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17344         
17345         var nodeIsBefore   =  ss == 1;
17346         var nodeIsAfter    = ee == -1;
17347         
17348         if (nodeIsBefore && nodeIsAfter)
17349             return 0; // outer
17350         if (!nodeIsBefore && nodeIsAfter)
17351             return 1; //right trailed.
17352         
17353         if (nodeIsBefore && !nodeIsAfter)
17354             return 2;  // left trailed.
17355         // fully contined.
17356         return 3;
17357     },
17358
17359     // private? - in a new class?
17360     cleanUpPaste :  function()
17361     {
17362         // cleans up the whole document..
17363         Roo.log('cleanuppaste');
17364         
17365         this.cleanUpChildren(this.doc.body);
17366         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17367         if (clean != this.doc.body.innerHTML) {
17368             this.doc.body.innerHTML = clean;
17369         }
17370         
17371     },
17372     
17373     cleanWordChars : function(input) {// change the chars to hex code
17374         var he = Roo.HtmlEditorCore;
17375         
17376         var output = input;
17377         Roo.each(he.swapCodes, function(sw) { 
17378             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17379             
17380             output = output.replace(swapper, sw[1]);
17381         });
17382         
17383         return output;
17384     },
17385     
17386     
17387     cleanUpChildren : function (n)
17388     {
17389         if (!n.childNodes.length) {
17390             return;
17391         }
17392         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17393            this.cleanUpChild(n.childNodes[i]);
17394         }
17395     },
17396     
17397     
17398         
17399     
17400     cleanUpChild : function (node)
17401     {
17402         var ed = this;
17403         //console.log(node);
17404         if (node.nodeName == "#text") {
17405             // clean up silly Windows -- stuff?
17406             return; 
17407         }
17408         if (node.nodeName == "#comment") {
17409             node.parentNode.removeChild(node);
17410             // clean up silly Windows -- stuff?
17411             return; 
17412         }
17413         var lcname = node.tagName.toLowerCase();
17414         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17415         // whitelist of tags..
17416         
17417         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17418             // remove node.
17419             node.parentNode.removeChild(node);
17420             return;
17421             
17422         }
17423         
17424         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17425         
17426         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17427         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17428         
17429         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17430         //    remove_keep_children = true;
17431         //}
17432         
17433         if (remove_keep_children) {
17434             this.cleanUpChildren(node);
17435             // inserts everything just before this node...
17436             while (node.childNodes.length) {
17437                 var cn = node.childNodes[0];
17438                 node.removeChild(cn);
17439                 node.parentNode.insertBefore(cn, node);
17440             }
17441             node.parentNode.removeChild(node);
17442             return;
17443         }
17444         
17445         if (!node.attributes || !node.attributes.length) {
17446             this.cleanUpChildren(node);
17447             return;
17448         }
17449         
17450         function cleanAttr(n,v)
17451         {
17452             
17453             if (v.match(/^\./) || v.match(/^\//)) {
17454                 return;
17455             }
17456             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17457                 return;
17458             }
17459             if (v.match(/^#/)) {
17460                 return;
17461             }
17462 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17463             node.removeAttribute(n);
17464             
17465         }
17466         
17467         var cwhite = this.cwhite;
17468         var cblack = this.cblack;
17469             
17470         function cleanStyle(n,v)
17471         {
17472             if (v.match(/expression/)) { //XSS?? should we even bother..
17473                 node.removeAttribute(n);
17474                 return;
17475             }
17476             
17477             var parts = v.split(/;/);
17478             var clean = [];
17479             
17480             Roo.each(parts, function(p) {
17481                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17482                 if (!p.length) {
17483                     return true;
17484                 }
17485                 var l = p.split(':').shift().replace(/\s+/g,'');
17486                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17487                 
17488                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17489 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17490                     //node.removeAttribute(n);
17491                     return true;
17492                 }
17493                 //Roo.log()
17494                 // only allow 'c whitelisted system attributes'
17495                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17496 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17497                     //node.removeAttribute(n);
17498                     return true;
17499                 }
17500                 
17501                 
17502                  
17503                 
17504                 clean.push(p);
17505                 return true;
17506             });
17507             if (clean.length) { 
17508                 node.setAttribute(n, clean.join(';'));
17509             } else {
17510                 node.removeAttribute(n);
17511             }
17512             
17513         }
17514         
17515         
17516         for (var i = node.attributes.length-1; i > -1 ; i--) {
17517             var a = node.attributes[i];
17518             //console.log(a);
17519             
17520             if (a.name.toLowerCase().substr(0,2)=='on')  {
17521                 node.removeAttribute(a.name);
17522                 continue;
17523             }
17524             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17525                 node.removeAttribute(a.name);
17526                 continue;
17527             }
17528             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17529                 cleanAttr(a.name,a.value); // fixme..
17530                 continue;
17531             }
17532             if (a.name == 'style') {
17533                 cleanStyle(a.name,a.value);
17534                 continue;
17535             }
17536             /// clean up MS crap..
17537             // tecnically this should be a list of valid class'es..
17538             
17539             
17540             if (a.name == 'class') {
17541                 if (a.value.match(/^Mso/)) {
17542                     node.className = '';
17543                 }
17544                 
17545                 if (a.value.match(/body/)) {
17546                     node.className = '';
17547                 }
17548                 continue;
17549             }
17550             
17551             // style cleanup!?
17552             // class cleanup?
17553             
17554         }
17555         
17556         
17557         this.cleanUpChildren(node);
17558         
17559         
17560     },
17561     /**
17562      * Clean up MS wordisms...
17563      */
17564     cleanWord : function(node)
17565     {
17566         var _t = this;
17567         var cleanWordChildren = function()
17568         {
17569             if (!node.childNodes.length) {
17570                 return;
17571             }
17572             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17573                _t.cleanWord(node.childNodes[i]);
17574             }
17575         }
17576         
17577         
17578         if (!node) {
17579             this.cleanWord(this.doc.body);
17580             return;
17581         }
17582         if (node.nodeName == "#text") {
17583             // clean up silly Windows -- stuff?
17584             return; 
17585         }
17586         if (node.nodeName == "#comment") {
17587             node.parentNode.removeChild(node);
17588             // clean up silly Windows -- stuff?
17589             return; 
17590         }
17591         
17592         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17593             node.parentNode.removeChild(node);
17594             return;
17595         }
17596         
17597         // remove - but keep children..
17598         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17599             while (node.childNodes.length) {
17600                 var cn = node.childNodes[0];
17601                 node.removeChild(cn);
17602                 node.parentNode.insertBefore(cn, node);
17603             }
17604             node.parentNode.removeChild(node);
17605             cleanWordChildren();
17606             return;
17607         }
17608         // clean styles
17609         if (node.className.length) {
17610             
17611             var cn = node.className.split(/\W+/);
17612             var cna = [];
17613             Roo.each(cn, function(cls) {
17614                 if (cls.match(/Mso[a-zA-Z]+/)) {
17615                     return;
17616                 }
17617                 cna.push(cls);
17618             });
17619             node.className = cna.length ? cna.join(' ') : '';
17620             if (!cna.length) {
17621                 node.removeAttribute("class");
17622             }
17623         }
17624         
17625         if (node.hasAttribute("lang")) {
17626             node.removeAttribute("lang");
17627         }
17628         
17629         if (node.hasAttribute("style")) {
17630             
17631             var styles = node.getAttribute("style").split(";");
17632             var nstyle = [];
17633             Roo.each(styles, function(s) {
17634                 if (!s.match(/:/)) {
17635                     return;
17636                 }
17637                 var kv = s.split(":");
17638                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17639                     return;
17640                 }
17641                 // what ever is left... we allow.
17642                 nstyle.push(s);
17643             });
17644             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17645             if (!nstyle.length) {
17646                 node.removeAttribute('style');
17647             }
17648         }
17649         
17650         cleanWordChildren();
17651         
17652         
17653     },
17654     domToHTML : function(currentElement, depth, nopadtext) {
17655         
17656         depth = depth || 0;
17657         nopadtext = nopadtext || false;
17658     
17659         if (!currentElement) {
17660             return this.domToHTML(this.doc.body);
17661         }
17662         
17663         //Roo.log(currentElement);
17664         var j;
17665         var allText = false;
17666         var nodeName = currentElement.nodeName;
17667         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17668         
17669         if  (nodeName == '#text') {
17670             return currentElement.nodeValue;
17671         }
17672         
17673         
17674         var ret = '';
17675         if (nodeName != 'BODY') {
17676              
17677             var i = 0;
17678             // Prints the node tagName, such as <A>, <IMG>, etc
17679             if (tagName) {
17680                 var attr = [];
17681                 for(i = 0; i < currentElement.attributes.length;i++) {
17682                     // quoting?
17683                     var aname = currentElement.attributes.item(i).name;
17684                     if (!currentElement.attributes.item(i).value.length) {
17685                         continue;
17686                     }
17687                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17688                 }
17689                 
17690                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17691             } 
17692             else {
17693                 
17694                 // eack
17695             }
17696         } else {
17697             tagName = false;
17698         }
17699         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17700             return ret;
17701         }
17702         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17703             nopadtext = true;
17704         }
17705         
17706         
17707         // Traverse the tree
17708         i = 0;
17709         var currentElementChild = currentElement.childNodes.item(i);
17710         var allText = true;
17711         var innerHTML  = '';
17712         lastnode = '';
17713         while (currentElementChild) {
17714             // Formatting code (indent the tree so it looks nice on the screen)
17715             var nopad = nopadtext;
17716             if (lastnode == 'SPAN') {
17717                 nopad  = true;
17718             }
17719             // text
17720             if  (currentElementChild.nodeName == '#text') {
17721                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17722                 if (!nopad && toadd.length > 80) {
17723                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17724                 }
17725                 innerHTML  += toadd;
17726                 
17727                 i++;
17728                 currentElementChild = currentElement.childNodes.item(i);
17729                 lastNode = '';
17730                 continue;
17731             }
17732             allText = false;
17733             
17734             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17735                 
17736             // Recursively traverse the tree structure of the child node
17737             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17738             lastnode = currentElementChild.nodeName;
17739             i++;
17740             currentElementChild=currentElement.childNodes.item(i);
17741         }
17742         
17743         ret += innerHTML;
17744         
17745         if (!allText) {
17746                 // The remaining code is mostly for formatting the tree
17747             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17748         }
17749         
17750         
17751         if (tagName) {
17752             ret+= "</"+tagName+">";
17753         }
17754         return ret;
17755         
17756     },
17757         
17758     applyBlacklists : function()
17759     {
17760         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17761         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17762         
17763         this.white = [];
17764         this.black = [];
17765         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17766             if (b.indexOf(tag) > -1) {
17767                 return;
17768             }
17769             this.white.push(tag);
17770             
17771         }, this);
17772         
17773         Roo.each(w, function(tag) {
17774             if (b.indexOf(tag) > -1) {
17775                 return;
17776             }
17777             if (this.white.indexOf(tag) > -1) {
17778                 return;
17779             }
17780             this.white.push(tag);
17781             
17782         }, this);
17783         
17784         
17785         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17786             if (w.indexOf(tag) > -1) {
17787                 return;
17788             }
17789             this.black.push(tag);
17790             
17791         }, this);
17792         
17793         Roo.each(b, function(tag) {
17794             if (w.indexOf(tag) > -1) {
17795                 return;
17796             }
17797             if (this.black.indexOf(tag) > -1) {
17798                 return;
17799             }
17800             this.black.push(tag);
17801             
17802         }, this);
17803         
17804         
17805         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17806         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17807         
17808         this.cwhite = [];
17809         this.cblack = [];
17810         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17811             if (b.indexOf(tag) > -1) {
17812                 return;
17813             }
17814             this.cwhite.push(tag);
17815             
17816         }, this);
17817         
17818         Roo.each(w, function(tag) {
17819             if (b.indexOf(tag) > -1) {
17820                 return;
17821             }
17822             if (this.cwhite.indexOf(tag) > -1) {
17823                 return;
17824             }
17825             this.cwhite.push(tag);
17826             
17827         }, this);
17828         
17829         
17830         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17831             if (w.indexOf(tag) > -1) {
17832                 return;
17833             }
17834             this.cblack.push(tag);
17835             
17836         }, this);
17837         
17838         Roo.each(b, function(tag) {
17839             if (w.indexOf(tag) > -1) {
17840                 return;
17841             }
17842             if (this.cblack.indexOf(tag) > -1) {
17843                 return;
17844             }
17845             this.cblack.push(tag);
17846             
17847         }, this);
17848     }
17849     
17850     // hide stuff that is not compatible
17851     /**
17852      * @event blur
17853      * @hide
17854      */
17855     /**
17856      * @event change
17857      * @hide
17858      */
17859     /**
17860      * @event focus
17861      * @hide
17862      */
17863     /**
17864      * @event specialkey
17865      * @hide
17866      */
17867     /**
17868      * @cfg {String} fieldClass @hide
17869      */
17870     /**
17871      * @cfg {String} focusClass @hide
17872      */
17873     /**
17874      * @cfg {String} autoCreate @hide
17875      */
17876     /**
17877      * @cfg {String} inputType @hide
17878      */
17879     /**
17880      * @cfg {String} invalidClass @hide
17881      */
17882     /**
17883      * @cfg {String} invalidText @hide
17884      */
17885     /**
17886      * @cfg {String} msgFx @hide
17887      */
17888     /**
17889      * @cfg {String} validateOnBlur @hide
17890      */
17891 });
17892
17893 Roo.HtmlEditorCore.white = [
17894         'area', 'br', 'img', 'input', 'hr', 'wbr',
17895         
17896        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17897        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17898        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17899        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17900        'table',   'ul',         'xmp', 
17901        
17902        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17903       'thead',   'tr', 
17904      
17905       'dir', 'menu', 'ol', 'ul', 'dl',
17906        
17907       'embed',  'object'
17908 ];
17909
17910
17911 Roo.HtmlEditorCore.black = [
17912     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17913         'applet', // 
17914         'base',   'basefont', 'bgsound', 'blink',  'body', 
17915         'frame',  'frameset', 'head',    'html',   'ilayer', 
17916         'iframe', 'layer',  'link',     'meta',    'object',   
17917         'script', 'style' ,'title',  'xml' // clean later..
17918 ];
17919 Roo.HtmlEditorCore.clean = [
17920     'script', 'style', 'title', 'xml'
17921 ];
17922 Roo.HtmlEditorCore.remove = [
17923     'font'
17924 ];
17925 // attributes..
17926
17927 Roo.HtmlEditorCore.ablack = [
17928     'on'
17929 ];
17930     
17931 Roo.HtmlEditorCore.aclean = [ 
17932     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17933 ];
17934
17935 // protocols..
17936 Roo.HtmlEditorCore.pwhite= [
17937         'http',  'https',  'mailto'
17938 ];
17939
17940 // white listed style attributes.
17941 Roo.HtmlEditorCore.cwhite= [
17942       //  'text-align', /// default is to allow most things..
17943       
17944          
17945 //        'font-size'//??
17946 ];
17947
17948 // black listed style attributes.
17949 Roo.HtmlEditorCore.cblack= [
17950       //  'font-size' -- this can be set by the project 
17951 ];
17952
17953
17954 Roo.HtmlEditorCore.swapCodes   =[ 
17955     [    8211, "--" ], 
17956     [    8212, "--" ], 
17957     [    8216,  "'" ],  
17958     [    8217, "'" ],  
17959     [    8220, '"' ],  
17960     [    8221, '"' ],  
17961     [    8226, "*" ],  
17962     [    8230, "..." ]
17963 ]; 
17964
17965     /*
17966  * - LGPL
17967  *
17968  * HtmlEditor
17969  * 
17970  */
17971
17972 /**
17973  * @class Roo.bootstrap.HtmlEditor
17974  * @extends Roo.bootstrap.TextArea
17975  * Bootstrap HtmlEditor class
17976
17977  * @constructor
17978  * Create a new HtmlEditor
17979  * @param {Object} config The config object
17980  */
17981
17982 Roo.bootstrap.HtmlEditor = function(config){
17983     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17984     if (!this.toolbars) {
17985         this.toolbars = [];
17986     }
17987     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17988     this.addEvents({
17989             /**
17990              * @event initialize
17991              * Fires when the editor is fully initialized (including the iframe)
17992              * @param {HtmlEditor} this
17993              */
17994             initialize: true,
17995             /**
17996              * @event activate
17997              * Fires when the editor is first receives the focus. Any insertion must wait
17998              * until after this event.
17999              * @param {HtmlEditor} this
18000              */
18001             activate: true,
18002              /**
18003              * @event beforesync
18004              * Fires before the textarea is updated with content from the editor iframe. Return false
18005              * to cancel the sync.
18006              * @param {HtmlEditor} this
18007              * @param {String} html
18008              */
18009             beforesync: true,
18010              /**
18011              * @event beforepush
18012              * Fires before the iframe editor is updated with content from the textarea. Return false
18013              * to cancel the push.
18014              * @param {HtmlEditor} this
18015              * @param {String} html
18016              */
18017             beforepush: true,
18018              /**
18019              * @event sync
18020              * Fires when the textarea is updated with content from the editor iframe.
18021              * @param {HtmlEditor} this
18022              * @param {String} html
18023              */
18024             sync: true,
18025              /**
18026              * @event push
18027              * Fires when the iframe editor is updated with content from the textarea.
18028              * @param {HtmlEditor} this
18029              * @param {String} html
18030              */
18031             push: true,
18032              /**
18033              * @event editmodechange
18034              * Fires when the editor switches edit modes
18035              * @param {HtmlEditor} this
18036              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18037              */
18038             editmodechange: true,
18039             /**
18040              * @event editorevent
18041              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18042              * @param {HtmlEditor} this
18043              */
18044             editorevent: true,
18045             /**
18046              * @event firstfocus
18047              * Fires when on first focus - needed by toolbars..
18048              * @param {HtmlEditor} this
18049              */
18050             firstfocus: true,
18051             /**
18052              * @event autosave
18053              * Auto save the htmlEditor value as a file into Events
18054              * @param {HtmlEditor} this
18055              */
18056             autosave: true,
18057             /**
18058              * @event savedpreview
18059              * preview the saved version of htmlEditor
18060              * @param {HtmlEditor} this
18061              */
18062             savedpreview: true
18063         });
18064 };
18065
18066
18067 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18068     
18069     
18070       /**
18071      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18072      */
18073     toolbars : false,
18074    
18075      /**
18076      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18077      *                        Roo.resizable.
18078      */
18079     resizable : false,
18080      /**
18081      * @cfg {Number} height (in pixels)
18082      */   
18083     height: 300,
18084    /**
18085      * @cfg {Number} width (in pixels)
18086      */   
18087     width: false,
18088     
18089     /**
18090      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18091      * 
18092      */
18093     stylesheets: false,
18094     
18095     // id of frame..
18096     frameId: false,
18097     
18098     // private properties
18099     validationEvent : false,
18100     deferHeight: true,
18101     initialized : false,
18102     activated : false,
18103     
18104     onFocus : Roo.emptyFn,
18105     iframePad:3,
18106     hideMode:'offsets',
18107     
18108     
18109     tbContainer : false,
18110     
18111     toolbarContainer :function() {
18112         return this.wrap.select('.x-html-editor-tb',true).first();
18113     },
18114
18115     /**
18116      * Protected method that will not generally be called directly. It
18117      * is called when the editor creates its toolbar. Override this method if you need to
18118      * add custom toolbar buttons.
18119      * @param {HtmlEditor} editor
18120      */
18121     createToolbar : function(){
18122         
18123         Roo.log("create toolbars");
18124         
18125         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18126         this.toolbars[0].render(this.toolbarContainer());
18127         
18128         return;
18129         
18130 //        if (!editor.toolbars || !editor.toolbars.length) {
18131 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18132 //        }
18133 //        
18134 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18135 //            editor.toolbars[i] = Roo.factory(
18136 //                    typeof(editor.toolbars[i]) == 'string' ?
18137 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18138 //                Roo.bootstrap.HtmlEditor);
18139 //            editor.toolbars[i].init(editor);
18140 //        }
18141     },
18142
18143      
18144     // private
18145     onRender : function(ct, position)
18146     {
18147        // Roo.log("Call onRender: " + this.xtype);
18148         var _t = this;
18149         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18150       
18151         this.wrap = this.inputEl().wrap({
18152             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18153         });
18154         
18155         this.editorcore.onRender(ct, position);
18156          
18157         if (this.resizable) {
18158             this.resizeEl = new Roo.Resizable(this.wrap, {
18159                 pinned : true,
18160                 wrap: true,
18161                 dynamic : true,
18162                 minHeight : this.height,
18163                 height: this.height,
18164                 handles : this.resizable,
18165                 width: this.width,
18166                 listeners : {
18167                     resize : function(r, w, h) {
18168                         _t.onResize(w,h); // -something
18169                     }
18170                 }
18171             });
18172             
18173         }
18174         this.createToolbar(this);
18175        
18176         
18177         if(!this.width && this.resizable){
18178             this.setSize(this.wrap.getSize());
18179         }
18180         if (this.resizeEl) {
18181             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18182             // should trigger onReize..
18183         }
18184         
18185     },
18186
18187     // private
18188     onResize : function(w, h)
18189     {
18190         Roo.log('resize: ' +w + ',' + h );
18191         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18192         var ew = false;
18193         var eh = false;
18194         
18195         if(this.inputEl() ){
18196             if(typeof w == 'number'){
18197                 var aw = w - this.wrap.getFrameWidth('lr');
18198                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18199                 ew = aw;
18200             }
18201             if(typeof h == 'number'){
18202                  var tbh = -11;  // fixme it needs to tool bar size!
18203                 for (var i =0; i < this.toolbars.length;i++) {
18204                     // fixme - ask toolbars for heights?
18205                     tbh += this.toolbars[i].el.getHeight();
18206                     //if (this.toolbars[i].footer) {
18207                     //    tbh += this.toolbars[i].footer.el.getHeight();
18208                     //}
18209                 }
18210               
18211                 
18212                 
18213                 
18214                 
18215                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18216                 ah -= 5; // knock a few pixes off for look..
18217                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18218                 var eh = ah;
18219             }
18220         }
18221         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18222         this.editorcore.onResize(ew,eh);
18223         
18224     },
18225
18226     /**
18227      * Toggles the editor between standard and source edit mode.
18228      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18229      */
18230     toggleSourceEdit : function(sourceEditMode)
18231     {
18232         this.editorcore.toggleSourceEdit(sourceEditMode);
18233         
18234         if(this.editorcore.sourceEditMode){
18235             Roo.log('editor - showing textarea');
18236             
18237 //            Roo.log('in');
18238 //            Roo.log(this.syncValue());
18239             this.syncValue();
18240             this.inputEl().removeClass(['hide', 'x-hidden']);
18241             this.inputEl().dom.removeAttribute('tabIndex');
18242             this.inputEl().focus();
18243         }else{
18244             Roo.log('editor - hiding textarea');
18245 //            Roo.log('out')
18246 //            Roo.log(this.pushValue()); 
18247             this.pushValue();
18248             
18249             this.inputEl().addClass(['hide', 'x-hidden']);
18250             this.inputEl().dom.setAttribute('tabIndex', -1);
18251             //this.deferFocus();
18252         }
18253          
18254         if(this.resizable){
18255             this.setSize(this.wrap.getSize());
18256         }
18257         
18258         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18259     },
18260  
18261     // private (for BoxComponent)
18262     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18263
18264     // private (for BoxComponent)
18265     getResizeEl : function(){
18266         return this.wrap;
18267     },
18268
18269     // private (for BoxComponent)
18270     getPositionEl : function(){
18271         return this.wrap;
18272     },
18273
18274     // private
18275     initEvents : function(){
18276         this.originalValue = this.getValue();
18277     },
18278
18279 //    /**
18280 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18281 //     * @method
18282 //     */
18283 //    markInvalid : Roo.emptyFn,
18284 //    /**
18285 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18286 //     * @method
18287 //     */
18288 //    clearInvalid : Roo.emptyFn,
18289
18290     setValue : function(v){
18291         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18292         this.editorcore.pushValue();
18293     },
18294
18295      
18296     // private
18297     deferFocus : function(){
18298         this.focus.defer(10, this);
18299     },
18300
18301     // doc'ed in Field
18302     focus : function(){
18303         this.editorcore.focus();
18304         
18305     },
18306       
18307
18308     // private
18309     onDestroy : function(){
18310         
18311         
18312         
18313         if(this.rendered){
18314             
18315             for (var i =0; i < this.toolbars.length;i++) {
18316                 // fixme - ask toolbars for heights?
18317                 this.toolbars[i].onDestroy();
18318             }
18319             
18320             this.wrap.dom.innerHTML = '';
18321             this.wrap.remove();
18322         }
18323     },
18324
18325     // private
18326     onFirstFocus : function(){
18327         //Roo.log("onFirstFocus");
18328         this.editorcore.onFirstFocus();
18329          for (var i =0; i < this.toolbars.length;i++) {
18330             this.toolbars[i].onFirstFocus();
18331         }
18332         
18333     },
18334     
18335     // private
18336     syncValue : function()
18337     {   
18338         this.editorcore.syncValue();
18339     },
18340     
18341     pushValue : function()
18342     {   
18343         this.editorcore.pushValue();
18344     }
18345      
18346     
18347     // hide stuff that is not compatible
18348     /**
18349      * @event blur
18350      * @hide
18351      */
18352     /**
18353      * @event change
18354      * @hide
18355      */
18356     /**
18357      * @event focus
18358      * @hide
18359      */
18360     /**
18361      * @event specialkey
18362      * @hide
18363      */
18364     /**
18365      * @cfg {String} fieldClass @hide
18366      */
18367     /**
18368      * @cfg {String} focusClass @hide
18369      */
18370     /**
18371      * @cfg {String} autoCreate @hide
18372      */
18373     /**
18374      * @cfg {String} inputType @hide
18375      */
18376     /**
18377      * @cfg {String} invalidClass @hide
18378      */
18379     /**
18380      * @cfg {String} invalidText @hide
18381      */
18382     /**
18383      * @cfg {String} msgFx @hide
18384      */
18385     /**
18386      * @cfg {String} validateOnBlur @hide
18387      */
18388 });
18389  
18390     
18391    
18392    
18393    
18394       
18395 Roo.namespace('Roo.bootstrap.htmleditor');
18396 /**
18397  * @class Roo.bootstrap.HtmlEditorToolbar1
18398  * Basic Toolbar
18399  * 
18400  * Usage:
18401  *
18402  new Roo.bootstrap.HtmlEditor({
18403     ....
18404     toolbars : [
18405         new Roo.bootstrap.HtmlEditorToolbar1({
18406             disable : { fonts: 1 , format: 1, ..., ... , ...],
18407             btns : [ .... ]
18408         })
18409     }
18410      
18411  * 
18412  * @cfg {Object} disable List of elements to disable..
18413  * @cfg {Array} btns List of additional buttons.
18414  * 
18415  * 
18416  * NEEDS Extra CSS? 
18417  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18418  */
18419  
18420 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18421 {
18422     
18423     Roo.apply(this, config);
18424     
18425     // default disabled, based on 'good practice'..
18426     this.disable = this.disable || {};
18427     Roo.applyIf(this.disable, {
18428         fontSize : true,
18429         colors : true,
18430         specialElements : true
18431     });
18432     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18433     
18434     this.editor = config.editor;
18435     this.editorcore = config.editor.editorcore;
18436     
18437     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18438     
18439     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18440     // dont call parent... till later.
18441 }
18442 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18443      
18444     bar : true,
18445     
18446     editor : false,
18447     editorcore : false,
18448     
18449     
18450     formats : [
18451         "p" ,  
18452         "h1","h2","h3","h4","h5","h6", 
18453         "pre", "code", 
18454         "abbr", "acronym", "address", "cite", "samp", "var",
18455         'div','span'
18456     ],
18457     
18458     onRender : function(ct, position)
18459     {
18460        // Roo.log("Call onRender: " + this.xtype);
18461         
18462        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18463        Roo.log(this.el);
18464        this.el.dom.style.marginBottom = '0';
18465        var _this = this;
18466        var editorcore = this.editorcore;
18467        var editor= this.editor;
18468        
18469        var children = [];
18470        var btn = function(id,cmd , toggle, handler){
18471        
18472             var  event = toggle ? 'toggle' : 'click';
18473        
18474             var a = {
18475                 size : 'sm',
18476                 xtype: 'Button',
18477                 xns: Roo.bootstrap,
18478                 glyphicon : id,
18479                 cmd : id || cmd,
18480                 enableToggle:toggle !== false,
18481                 //html : 'submit'
18482                 pressed : toggle ? false : null,
18483                 listeners : {}
18484             }
18485             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18486                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18487             }
18488             children.push(a);
18489             return a;
18490        }
18491         
18492         var style = {
18493                 xtype: 'Button',
18494                 size : 'sm',
18495                 xns: Roo.bootstrap,
18496                 glyphicon : 'font',
18497                 //html : 'submit'
18498                 menu : {
18499                     xtype: 'Menu',
18500                     xns: Roo.bootstrap,
18501                     items:  []
18502                 }
18503         };
18504         Roo.each(this.formats, function(f) {
18505             style.menu.items.push({
18506                 xtype :'MenuItem',
18507                 xns: Roo.bootstrap,
18508                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18509                 tagname : f,
18510                 listeners : {
18511                     click : function()
18512                     {
18513                         editorcore.insertTag(this.tagname);
18514                         editor.focus();
18515                     }
18516                 }
18517                 
18518             });
18519         });
18520          children.push(style);   
18521             
18522             
18523         btn('bold',false,true);
18524         btn('italic',false,true);
18525         btn('align-left', 'justifyleft',true);
18526         btn('align-center', 'justifycenter',true);
18527         btn('align-right' , 'justifyright',true);
18528         btn('link', false, false, function(btn) {
18529             //Roo.log("create link?");
18530             var url = prompt(this.createLinkText, this.defaultLinkValue);
18531             if(url && url != 'http:/'+'/'){
18532                 this.editorcore.relayCmd('createlink', url);
18533             }
18534         }),
18535         btn('list','insertunorderedlist',true);
18536         btn('pencil', false,true, function(btn){
18537                 Roo.log(this);
18538                 
18539                 this.toggleSourceEdit(btn.pressed);
18540         });
18541         /*
18542         var cog = {
18543                 xtype: 'Button',
18544                 size : 'sm',
18545                 xns: Roo.bootstrap,
18546                 glyphicon : 'cog',
18547                 //html : 'submit'
18548                 menu : {
18549                     xtype: 'Menu',
18550                     xns: Roo.bootstrap,
18551                     items:  []
18552                 }
18553         };
18554         
18555         cog.menu.items.push({
18556             xtype :'MenuItem',
18557             xns: Roo.bootstrap,
18558             html : Clean styles,
18559             tagname : f,
18560             listeners : {
18561                 click : function()
18562                 {
18563                     editorcore.insertTag(this.tagname);
18564                     editor.focus();
18565                 }
18566             }
18567             
18568         });
18569        */
18570         
18571          
18572        this.xtype = 'NavSimplebar';
18573         
18574         for(var i=0;i< children.length;i++) {
18575             
18576             this.buttons.add(this.addxtypeChild(children[i]));
18577             
18578         }
18579         
18580         editor.on('editorevent', this.updateToolbar, this);
18581     },
18582     onBtnClick : function(id)
18583     {
18584        this.editorcore.relayCmd(id);
18585        this.editorcore.focus();
18586     },
18587     
18588     /**
18589      * Protected method that will not generally be called directly. It triggers
18590      * a toolbar update by reading the markup state of the current selection in the editor.
18591      */
18592     updateToolbar: function(){
18593
18594         if(!this.editorcore.activated){
18595             this.editor.onFirstFocus(); // is this neeed?
18596             return;
18597         }
18598
18599         var btns = this.buttons; 
18600         var doc = this.editorcore.doc;
18601         btns.get('bold').setActive(doc.queryCommandState('bold'));
18602         btns.get('italic').setActive(doc.queryCommandState('italic'));
18603         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18604         
18605         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18606         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18607         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18608         
18609         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18610         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18611          /*
18612         
18613         var ans = this.editorcore.getAllAncestors();
18614         if (this.formatCombo) {
18615             
18616             
18617             var store = this.formatCombo.store;
18618             this.formatCombo.setValue("");
18619             for (var i =0; i < ans.length;i++) {
18620                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18621                     // select it..
18622                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18623                     break;
18624                 }
18625             }
18626         }
18627         
18628         
18629         
18630         // hides menus... - so this cant be on a menu...
18631         Roo.bootstrap.MenuMgr.hideAll();
18632         */
18633         Roo.bootstrap.MenuMgr.hideAll();
18634         //this.editorsyncValue();
18635     },
18636     onFirstFocus: function() {
18637         this.buttons.each(function(item){
18638            item.enable();
18639         });
18640     },
18641     toggleSourceEdit : function(sourceEditMode){
18642         
18643           
18644         if(sourceEditMode){
18645             Roo.log("disabling buttons");
18646            this.buttons.each( function(item){
18647                 if(item.cmd != 'pencil'){
18648                     item.disable();
18649                 }
18650             });
18651           
18652         }else{
18653             Roo.log("enabling buttons");
18654             if(this.editorcore.initialized){
18655                 this.buttons.each( function(item){
18656                     item.enable();
18657                 });
18658             }
18659             
18660         }
18661         Roo.log("calling toggole on editor");
18662         // tell the editor that it's been pressed..
18663         this.editor.toggleSourceEdit(sourceEditMode);
18664        
18665     }
18666 });
18667
18668
18669
18670
18671
18672 /**
18673  * @class Roo.bootstrap.Table.AbstractSelectionModel
18674  * @extends Roo.util.Observable
18675  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18676  * implemented by descendant classes.  This class should not be directly instantiated.
18677  * @constructor
18678  */
18679 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18680     this.locked = false;
18681     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18682 };
18683
18684
18685 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18686     /** @ignore Called by the grid automatically. Do not call directly. */
18687     init : function(grid){
18688         this.grid = grid;
18689         this.initEvents();
18690     },
18691
18692     /**
18693      * Locks the selections.
18694      */
18695     lock : function(){
18696         this.locked = true;
18697     },
18698
18699     /**
18700      * Unlocks the selections.
18701      */
18702     unlock : function(){
18703         this.locked = false;
18704     },
18705
18706     /**
18707      * Returns true if the selections are locked.
18708      * @return {Boolean}
18709      */
18710     isLocked : function(){
18711         return this.locked;
18712     }
18713 });
18714 /**
18715  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18716  * @class Roo.bootstrap.Table.RowSelectionModel
18717  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18718  * It supports multiple selections and keyboard selection/navigation. 
18719  * @constructor
18720  * @param {Object} config
18721  */
18722
18723 Roo.bootstrap.Table.RowSelectionModel = function(config){
18724     Roo.apply(this, config);
18725     this.selections = new Roo.util.MixedCollection(false, function(o){
18726         return o.id;
18727     });
18728
18729     this.last = false;
18730     this.lastActive = false;
18731
18732     this.addEvents({
18733         /**
18734              * @event selectionchange
18735              * Fires when the selection changes
18736              * @param {SelectionModel} this
18737              */
18738             "selectionchange" : true,
18739         /**
18740              * @event afterselectionchange
18741              * Fires after the selection changes (eg. by key press or clicking)
18742              * @param {SelectionModel} this
18743              */
18744             "afterselectionchange" : true,
18745         /**
18746              * @event beforerowselect
18747              * Fires when a row is selected being selected, return false to cancel.
18748              * @param {SelectionModel} this
18749              * @param {Number} rowIndex The selected index
18750              * @param {Boolean} keepExisting False if other selections will be cleared
18751              */
18752             "beforerowselect" : true,
18753         /**
18754              * @event rowselect
18755              * Fires when a row is selected.
18756              * @param {SelectionModel} this
18757              * @param {Number} rowIndex The selected index
18758              * @param {Roo.data.Record} r The record
18759              */
18760             "rowselect" : true,
18761         /**
18762              * @event rowdeselect
18763              * Fires when a row is deselected.
18764              * @param {SelectionModel} this
18765              * @param {Number} rowIndex The selected index
18766              */
18767         "rowdeselect" : true
18768     });
18769     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18770     this.locked = false;
18771 };
18772
18773 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18774     /**
18775      * @cfg {Boolean} singleSelect
18776      * True to allow selection of only one row at a time (defaults to false)
18777      */
18778     singleSelect : false,
18779
18780     // private
18781     initEvents : function(){
18782
18783         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18784             this.grid.on("mousedown", this.handleMouseDown, this);
18785         }else{ // allow click to work like normal
18786             this.grid.on("rowclick", this.handleDragableRowClick, this);
18787         }
18788
18789         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18790             "up" : function(e){
18791                 if(!e.shiftKey){
18792                     this.selectPrevious(e.shiftKey);
18793                 }else if(this.last !== false && this.lastActive !== false){
18794                     var last = this.last;
18795                     this.selectRange(this.last,  this.lastActive-1);
18796                     this.grid.getView().focusRow(this.lastActive);
18797                     if(last !== false){
18798                         this.last = last;
18799                     }
18800                 }else{
18801                     this.selectFirstRow();
18802                 }
18803                 this.fireEvent("afterselectionchange", this);
18804             },
18805             "down" : function(e){
18806                 if(!e.shiftKey){
18807                     this.selectNext(e.shiftKey);
18808                 }else if(this.last !== false && this.lastActive !== false){
18809                     var last = this.last;
18810                     this.selectRange(this.last,  this.lastActive+1);
18811                     this.grid.getView().focusRow(this.lastActive);
18812                     if(last !== false){
18813                         this.last = last;
18814                     }
18815                 }else{
18816                     this.selectFirstRow();
18817                 }
18818                 this.fireEvent("afterselectionchange", this);
18819             },
18820             scope: this
18821         });
18822
18823         var view = this.grid.view;
18824         view.on("refresh", this.onRefresh, this);
18825         view.on("rowupdated", this.onRowUpdated, this);
18826         view.on("rowremoved", this.onRemove, this);
18827     },
18828
18829     // private
18830     onRefresh : function(){
18831         var ds = this.grid.dataSource, i, v = this.grid.view;
18832         var s = this.selections;
18833         s.each(function(r){
18834             if((i = ds.indexOfId(r.id)) != -1){
18835                 v.onRowSelect(i);
18836             }else{
18837                 s.remove(r);
18838             }
18839         });
18840     },
18841
18842     // private
18843     onRemove : function(v, index, r){
18844         this.selections.remove(r);
18845     },
18846
18847     // private
18848     onRowUpdated : function(v, index, r){
18849         if(this.isSelected(r)){
18850             v.onRowSelect(index);
18851         }
18852     },
18853
18854     /**
18855      * Select records.
18856      * @param {Array} records The records to select
18857      * @param {Boolean} keepExisting (optional) True to keep existing selections
18858      */
18859     selectRecords : function(records, keepExisting){
18860         if(!keepExisting){
18861             this.clearSelections();
18862         }
18863         var ds = this.grid.dataSource;
18864         for(var i = 0, len = records.length; i < len; i++){
18865             this.selectRow(ds.indexOf(records[i]), true);
18866         }
18867     },
18868
18869     /**
18870      * Gets the number of selected rows.
18871      * @return {Number}
18872      */
18873     getCount : function(){
18874         return this.selections.length;
18875     },
18876
18877     /**
18878      * Selects the first row in the grid.
18879      */
18880     selectFirstRow : function(){
18881         this.selectRow(0);
18882     },
18883
18884     /**
18885      * Select the last row.
18886      * @param {Boolean} keepExisting (optional) True to keep existing selections
18887      */
18888     selectLastRow : function(keepExisting){
18889         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18890     },
18891
18892     /**
18893      * Selects the row immediately following the last selected row.
18894      * @param {Boolean} keepExisting (optional) True to keep existing selections
18895      */
18896     selectNext : function(keepExisting){
18897         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18898             this.selectRow(this.last+1, keepExisting);
18899             this.grid.getView().focusRow(this.last);
18900         }
18901     },
18902
18903     /**
18904      * Selects the row that precedes the last selected row.
18905      * @param {Boolean} keepExisting (optional) True to keep existing selections
18906      */
18907     selectPrevious : function(keepExisting){
18908         if(this.last){
18909             this.selectRow(this.last-1, keepExisting);
18910             this.grid.getView().focusRow(this.last);
18911         }
18912     },
18913
18914     /**
18915      * Returns the selected records
18916      * @return {Array} Array of selected records
18917      */
18918     getSelections : function(){
18919         return [].concat(this.selections.items);
18920     },
18921
18922     /**
18923      * Returns the first selected record.
18924      * @return {Record}
18925      */
18926     getSelected : function(){
18927         return this.selections.itemAt(0);
18928     },
18929
18930
18931     /**
18932      * Clears all selections.
18933      */
18934     clearSelections : function(fast){
18935         if(this.locked) return;
18936         if(fast !== true){
18937             var ds = this.grid.dataSource;
18938             var s = this.selections;
18939             s.each(function(r){
18940                 this.deselectRow(ds.indexOfId(r.id));
18941             }, this);
18942             s.clear();
18943         }else{
18944             this.selections.clear();
18945         }
18946         this.last = false;
18947     },
18948
18949
18950     /**
18951      * Selects all rows.
18952      */
18953     selectAll : function(){
18954         if(this.locked) return;
18955         this.selections.clear();
18956         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18957             this.selectRow(i, true);
18958         }
18959     },
18960
18961     /**
18962      * Returns True if there is a selection.
18963      * @return {Boolean}
18964      */
18965     hasSelection : function(){
18966         return this.selections.length > 0;
18967     },
18968
18969     /**
18970      * Returns True if the specified row is selected.
18971      * @param {Number/Record} record The record or index of the record to check
18972      * @return {Boolean}
18973      */
18974     isSelected : function(index){
18975         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18976         return (r && this.selections.key(r.id) ? true : false);
18977     },
18978
18979     /**
18980      * Returns True if the specified record id is selected.
18981      * @param {String} id The id of record to check
18982      * @return {Boolean}
18983      */
18984     isIdSelected : function(id){
18985         return (this.selections.key(id) ? true : false);
18986     },
18987
18988     // private
18989     handleMouseDown : function(e, t){
18990         var view = this.grid.getView(), rowIndex;
18991         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18992             return;
18993         };
18994         if(e.shiftKey && this.last !== false){
18995             var last = this.last;
18996             this.selectRange(last, rowIndex, e.ctrlKey);
18997             this.last = last; // reset the last
18998             view.focusRow(rowIndex);
18999         }else{
19000             var isSelected = this.isSelected(rowIndex);
19001             if(e.button !== 0 && isSelected){
19002                 view.focusRow(rowIndex);
19003             }else if(e.ctrlKey && isSelected){
19004                 this.deselectRow(rowIndex);
19005             }else if(!isSelected){
19006                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19007                 view.focusRow(rowIndex);
19008             }
19009         }
19010         this.fireEvent("afterselectionchange", this);
19011     },
19012     // private
19013     handleDragableRowClick :  function(grid, rowIndex, e) 
19014     {
19015         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19016             this.selectRow(rowIndex, false);
19017             grid.view.focusRow(rowIndex);
19018              this.fireEvent("afterselectionchange", this);
19019         }
19020     },
19021     
19022     /**
19023      * Selects multiple rows.
19024      * @param {Array} rows Array of the indexes of the row to select
19025      * @param {Boolean} keepExisting (optional) True to keep existing selections
19026      */
19027     selectRows : function(rows, keepExisting){
19028         if(!keepExisting){
19029             this.clearSelections();
19030         }
19031         for(var i = 0, len = rows.length; i < len; i++){
19032             this.selectRow(rows[i], true);
19033         }
19034     },
19035
19036     /**
19037      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19038      * @param {Number} startRow The index of the first row in the range
19039      * @param {Number} endRow The index of the last row in the range
19040      * @param {Boolean} keepExisting (optional) True to retain existing selections
19041      */
19042     selectRange : function(startRow, endRow, keepExisting){
19043         if(this.locked) return;
19044         if(!keepExisting){
19045             this.clearSelections();
19046         }
19047         if(startRow <= endRow){
19048             for(var i = startRow; i <= endRow; i++){
19049                 this.selectRow(i, true);
19050             }
19051         }else{
19052             for(var i = startRow; i >= endRow; i--){
19053                 this.selectRow(i, true);
19054             }
19055         }
19056     },
19057
19058     /**
19059      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19060      * @param {Number} startRow The index of the first row in the range
19061      * @param {Number} endRow The index of the last row in the range
19062      */
19063     deselectRange : function(startRow, endRow, preventViewNotify){
19064         if(this.locked) return;
19065         for(var i = startRow; i <= endRow; i++){
19066             this.deselectRow(i, preventViewNotify);
19067         }
19068     },
19069
19070     /**
19071      * Selects a row.
19072      * @param {Number} row The index of the row to select
19073      * @param {Boolean} keepExisting (optional) True to keep existing selections
19074      */
19075     selectRow : function(index, keepExisting, preventViewNotify){
19076         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19077         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19078             if(!keepExisting || this.singleSelect){
19079                 this.clearSelections();
19080             }
19081             var r = this.grid.dataSource.getAt(index);
19082             this.selections.add(r);
19083             this.last = this.lastActive = index;
19084             if(!preventViewNotify){
19085                 this.grid.getView().onRowSelect(index);
19086             }
19087             this.fireEvent("rowselect", this, index, r);
19088             this.fireEvent("selectionchange", this);
19089         }
19090     },
19091
19092     /**
19093      * Deselects a row.
19094      * @param {Number} row The index of the row to deselect
19095      */
19096     deselectRow : function(index, preventViewNotify){
19097         if(this.locked) return;
19098         if(this.last == index){
19099             this.last = false;
19100         }
19101         if(this.lastActive == index){
19102             this.lastActive = false;
19103         }
19104         var r = this.grid.dataSource.getAt(index);
19105         this.selections.remove(r);
19106         if(!preventViewNotify){
19107             this.grid.getView().onRowDeselect(index);
19108         }
19109         this.fireEvent("rowdeselect", this, index);
19110         this.fireEvent("selectionchange", this);
19111     },
19112
19113     // private
19114     restoreLast : function(){
19115         if(this._last){
19116             this.last = this._last;
19117         }
19118     },
19119
19120     // private
19121     acceptsNav : function(row, col, cm){
19122         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19123     },
19124
19125     // private
19126     onEditorKey : function(field, e){
19127         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19128         if(k == e.TAB){
19129             e.stopEvent();
19130             ed.completeEdit();
19131             if(e.shiftKey){
19132                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19133             }else{
19134                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19135             }
19136         }else if(k == e.ENTER && !e.ctrlKey){
19137             e.stopEvent();
19138             ed.completeEdit();
19139             if(e.shiftKey){
19140                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19141             }else{
19142                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19143             }
19144         }else if(k == e.ESC){
19145             ed.cancelEdit();
19146         }
19147         if(newCell){
19148             g.startEditing(newCell[0], newCell[1]);
19149         }
19150     }
19151 });/*
19152  * Based on:
19153  * Ext JS Library 1.1.1
19154  * Copyright(c) 2006-2007, Ext JS, LLC.
19155  *
19156  * Originally Released Under LGPL - original licence link has changed is not relivant.
19157  *
19158  * Fork - LGPL
19159  * <script type="text/javascript">
19160  */
19161  
19162 /**
19163  * @class Roo.bootstrap.PagingToolbar
19164  * @extends Roo.Row
19165  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19166  * @constructor
19167  * Create a new PagingToolbar
19168  * @param {Object} config The config object
19169  */
19170 Roo.bootstrap.PagingToolbar = function(config)
19171 {
19172     // old args format still supported... - xtype is prefered..
19173         // created from xtype...
19174     var ds = config.dataSource;
19175     this.toolbarItems = [];
19176     if (config.items) {
19177         this.toolbarItems = config.items;
19178 //        config.items = [];
19179     }
19180     
19181     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19182     this.ds = ds;
19183     this.cursor = 0;
19184     if (ds) { 
19185         this.bind(ds);
19186     }
19187     
19188     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19189     
19190 };
19191
19192 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19193     /**
19194      * @cfg {Roo.data.Store} dataSource
19195      * The underlying data store providing the paged data
19196      */
19197     /**
19198      * @cfg {String/HTMLElement/Element} container
19199      * container The id or element that will contain the toolbar
19200      */
19201     /**
19202      * @cfg {Boolean} displayInfo
19203      * True to display the displayMsg (defaults to false)
19204      */
19205     /**
19206      * @cfg {Number} pageSize
19207      * The number of records to display per page (defaults to 20)
19208      */
19209     pageSize: 20,
19210     /**
19211      * @cfg {String} displayMsg
19212      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19213      */
19214     displayMsg : 'Displaying {0} - {1} of {2}',
19215     /**
19216      * @cfg {String} emptyMsg
19217      * The message to display when no records are found (defaults to "No data to display")
19218      */
19219     emptyMsg : 'No data to display',
19220     /**
19221      * Customizable piece of the default paging text (defaults to "Page")
19222      * @type String
19223      */
19224     beforePageText : "Page",
19225     /**
19226      * Customizable piece of the default paging text (defaults to "of %0")
19227      * @type String
19228      */
19229     afterPageText : "of {0}",
19230     /**
19231      * Customizable piece of the default paging text (defaults to "First Page")
19232      * @type String
19233      */
19234     firstText : "First Page",
19235     /**
19236      * Customizable piece of the default paging text (defaults to "Previous Page")
19237      * @type String
19238      */
19239     prevText : "Previous Page",
19240     /**
19241      * Customizable piece of the default paging text (defaults to "Next Page")
19242      * @type String
19243      */
19244     nextText : "Next Page",
19245     /**
19246      * Customizable piece of the default paging text (defaults to "Last Page")
19247      * @type String
19248      */
19249     lastText : "Last Page",
19250     /**
19251      * Customizable piece of the default paging text (defaults to "Refresh")
19252      * @type String
19253      */
19254     refreshText : "Refresh",
19255
19256     buttons : false,
19257     // private
19258     onRender : function(ct, position) 
19259     {
19260         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19261         this.navgroup.parentId = this.id;
19262         this.navgroup.onRender(this.el, null);
19263         // add the buttons to the navgroup
19264         
19265         if(this.displayInfo){
19266             Roo.log(this.el.select('ul.navbar-nav',true).first());
19267             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19268             this.displayEl = this.el.select('.x-paging-info', true).first();
19269 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19270 //            this.displayEl = navel.el.select('span',true).first();
19271         }
19272         
19273         var _this = this;
19274         
19275         if(this.buttons){
19276             Roo.each(_this.buttons, function(e){
19277                Roo.factory(e).onRender(_this.el, null);
19278             });
19279         }
19280             
19281         Roo.each(_this.toolbarItems, function(e) {
19282             _this.navgroup.addItem(e);
19283         });
19284         
19285         this.first = this.navgroup.addItem({
19286             tooltip: this.firstText,
19287             cls: "prev",
19288             icon : 'fa fa-backward',
19289             disabled: true,
19290             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19291         });
19292         
19293         this.prev =  this.navgroup.addItem({
19294             tooltip: this.prevText,
19295             cls: "prev",
19296             icon : 'fa fa-step-backward',
19297             disabled: true,
19298             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19299         });
19300     //this.addSeparator();
19301         
19302         
19303         var field = this.navgroup.addItem( {
19304             tagtype : 'span',
19305             cls : 'x-paging-position',
19306             
19307             html : this.beforePageText  +
19308                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19309                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19310          } ); //?? escaped?
19311         
19312         this.field = field.el.select('input', true).first();
19313         this.field.on("keydown", this.onPagingKeydown, this);
19314         this.field.on("focus", function(){this.dom.select();});
19315     
19316     
19317         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19318         //this.field.setHeight(18);
19319         //this.addSeparator();
19320         this.next = this.navgroup.addItem({
19321             tooltip: this.nextText,
19322             cls: "next",
19323             html : ' <i class="fa fa-step-forward">',
19324             disabled: true,
19325             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19326         });
19327         this.last = this.navgroup.addItem({
19328             tooltip: this.lastText,
19329             icon : 'fa fa-forward',
19330             cls: "next",
19331             disabled: true,
19332             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19333         });
19334     //this.addSeparator();
19335         this.loading = this.navgroup.addItem({
19336             tooltip: this.refreshText,
19337             icon: 'fa fa-refresh',
19338             
19339             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19340         });
19341
19342     },
19343
19344     // private
19345     updateInfo : function(){
19346         if(this.displayEl){
19347             var count = this.ds.getCount();
19348             var msg = count == 0 ?
19349                 this.emptyMsg :
19350                 String.format(
19351                     this.displayMsg,
19352                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19353                 );
19354             this.displayEl.update(msg);
19355         }
19356     },
19357
19358     // private
19359     onLoad : function(ds, r, o){
19360        this.cursor = o.params ? o.params.start : 0;
19361        var d = this.getPageData(),
19362             ap = d.activePage,
19363             ps = d.pages;
19364         
19365        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19366        this.field.dom.value = ap;
19367        this.first.setDisabled(ap == 1);
19368        this.prev.setDisabled(ap == 1);
19369        this.next.setDisabled(ap == ps);
19370        this.last.setDisabled(ap == ps);
19371        this.loading.enable();
19372        this.updateInfo();
19373     },
19374
19375     // private
19376     getPageData : function(){
19377         var total = this.ds.getTotalCount();
19378         return {
19379             total : total,
19380             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19381             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19382         };
19383     },
19384
19385     // private
19386     onLoadError : function(){
19387         this.loading.enable();
19388     },
19389
19390     // private
19391     onPagingKeydown : function(e){
19392         var k = e.getKey();
19393         var d = this.getPageData();
19394         if(k == e.RETURN){
19395             var v = this.field.dom.value, pageNum;
19396             if(!v || isNaN(pageNum = parseInt(v, 10))){
19397                 this.field.dom.value = d.activePage;
19398                 return;
19399             }
19400             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19401             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19402             e.stopEvent();
19403         }
19404         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))
19405         {
19406           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19407           this.field.dom.value = pageNum;
19408           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19409           e.stopEvent();
19410         }
19411         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19412         {
19413           var v = this.field.dom.value, pageNum; 
19414           var increment = (e.shiftKey) ? 10 : 1;
19415           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19416             increment *= -1;
19417           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19418             this.field.dom.value = d.activePage;
19419             return;
19420           }
19421           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19422           {
19423             this.field.dom.value = parseInt(v, 10) + increment;
19424             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19425             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19426           }
19427           e.stopEvent();
19428         }
19429     },
19430
19431     // private
19432     beforeLoad : function(){
19433         if(this.loading){
19434             this.loading.disable();
19435         }
19436     },
19437
19438     // private
19439     onClick : function(which){
19440         var ds = this.ds;
19441         if (!ds) {
19442             return;
19443         }
19444         switch(which){
19445             case "first":
19446                 ds.load({params:{start: 0, limit: this.pageSize}});
19447             break;
19448             case "prev":
19449                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19450             break;
19451             case "next":
19452                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19453             break;
19454             case "last":
19455                 var total = ds.getTotalCount();
19456                 var extra = total % this.pageSize;
19457                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19458                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19459             break;
19460             case "refresh":
19461                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19462             break;
19463         }
19464     },
19465
19466     /**
19467      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19468      * @param {Roo.data.Store} store The data store to unbind
19469      */
19470     unbind : function(ds){
19471         ds.un("beforeload", this.beforeLoad, this);
19472         ds.un("load", this.onLoad, this);
19473         ds.un("loadexception", this.onLoadError, this);
19474         ds.un("remove", this.updateInfo, this);
19475         ds.un("add", this.updateInfo, this);
19476         this.ds = undefined;
19477     },
19478
19479     /**
19480      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19481      * @param {Roo.data.Store} store The data store to bind
19482      */
19483     bind : function(ds){
19484         ds.on("beforeload", this.beforeLoad, this);
19485         ds.on("load", this.onLoad, this);
19486         ds.on("loadexception", this.onLoadError, this);
19487         ds.on("remove", this.updateInfo, this);
19488         ds.on("add", this.updateInfo, this);
19489         this.ds = ds;
19490     }
19491 });/*
19492  * - LGPL
19493  *
19494  * element
19495  * 
19496  */
19497
19498 /**
19499  * @class Roo.bootstrap.MessageBar
19500  * @extends Roo.bootstrap.Component
19501  * Bootstrap MessageBar class
19502  * @cfg {String} html contents of the MessageBar
19503  * @cfg {String} weight (info | success | warning | danger) default info
19504  * @cfg {String} beforeClass insert the bar before the given class
19505  * @cfg {Boolean} closable (true | false) default false
19506  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19507  * 
19508  * @constructor
19509  * Create a new Element
19510  * @param {Object} config The config object
19511  */
19512
19513 Roo.bootstrap.MessageBar = function(config){
19514     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19515 };
19516
19517 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19518     
19519     html: '',
19520     weight: 'info',
19521     closable: false,
19522     fixed: false,
19523     beforeClass: 'bootstrap-sticky-wrap',
19524     
19525     getAutoCreate : function(){
19526         
19527         var cfg = {
19528             tag: 'div',
19529             cls: 'alert alert-dismissable alert-' + this.weight,
19530             cn: [
19531                 {
19532                     tag: 'span',
19533                     cls: 'message',
19534                     html: this.html || ''
19535                 }
19536             ]
19537         }
19538         
19539         if(this.fixed){
19540             cfg.cls += ' alert-messages-fixed';
19541         }
19542         
19543         if(this.closable){
19544             cfg.cn.push({
19545                 tag: 'button',
19546                 cls: 'close',
19547                 html: 'x'
19548             });
19549         }
19550         
19551         return cfg;
19552     },
19553     
19554     onRender : function(ct, position)
19555     {
19556         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19557         
19558         if(!this.el){
19559             var cfg = Roo.apply({},  this.getAutoCreate());
19560             cfg.id = Roo.id();
19561             
19562             if (this.cls) {
19563                 cfg.cls += ' ' + this.cls;
19564             }
19565             if (this.style) {
19566                 cfg.style = this.style;
19567             }
19568             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19569             
19570             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19571         }
19572         
19573         this.el.select('>button.close').on('click', this.hide, this);
19574         
19575     },
19576     
19577     show : function()
19578     {
19579         if (!this.rendered) {
19580             this.render();
19581         }
19582         
19583         this.el.show();
19584         
19585         this.fireEvent('show', this);
19586         
19587     },
19588     
19589     hide : function()
19590     {
19591         if (!this.rendered) {
19592             this.render();
19593         }
19594         
19595         this.el.hide();
19596         
19597         this.fireEvent('hide', this);
19598     },
19599     
19600     update : function()
19601     {
19602 //        var e = this.el.dom.firstChild;
19603 //        
19604 //        if(this.closable){
19605 //            e = e.nextSibling;
19606 //        }
19607 //        
19608 //        e.data = this.html || '';
19609
19610         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19611     }
19612    
19613 });
19614
19615  
19616
19617      /*
19618  * - LGPL
19619  *
19620  * Graph
19621  * 
19622  */
19623
19624
19625 /**
19626  * @class Roo.bootstrap.Graph
19627  * @extends Roo.bootstrap.Component
19628  * Bootstrap Graph class
19629 > Prameters
19630  -sm {number} sm 4
19631  -md {number} md 5
19632  @cfg {String} graphtype  bar | vbar | pie
19633  @cfg {number} g_x coodinator | centre x (pie)
19634  @cfg {number} g_y coodinator | centre y (pie)
19635  @cfg {number} g_r radius (pie)
19636  @cfg {number} g_height height of the chart (respected by all elements in the set)
19637  @cfg {number} g_width width of the chart (respected by all elements in the set)
19638  @cfg {Object} title The title of the chart
19639     
19640  -{Array}  values
19641  -opts (object) options for the chart 
19642      o {
19643      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19644      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19645      o vgutter (number)
19646      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.
19647      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19648      o to
19649      o stretch (boolean)
19650      o }
19651  -opts (object) options for the pie
19652      o{
19653      o cut
19654      o startAngle (number)
19655      o endAngle (number)
19656      } 
19657  *
19658  * @constructor
19659  * Create a new Input
19660  * @param {Object} config The config object
19661  */
19662
19663 Roo.bootstrap.Graph = function(config){
19664     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19665     
19666     this.addEvents({
19667         // img events
19668         /**
19669          * @event click
19670          * The img click event for the img.
19671          * @param {Roo.EventObject} e
19672          */
19673         "click" : true
19674     });
19675 };
19676
19677 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19678     
19679     sm: 4,
19680     md: 5,
19681     graphtype: 'bar',
19682     g_height: 250,
19683     g_width: 400,
19684     g_x: 50,
19685     g_y: 50,
19686     g_r: 30,
19687     opts:{
19688         //g_colors: this.colors,
19689         g_type: 'soft',
19690         g_gutter: '20%'
19691
19692     },
19693     title : false,
19694
19695     getAutoCreate : function(){
19696         
19697         var cfg = {
19698             tag: 'div',
19699             html : null
19700         }
19701         
19702         
19703         return  cfg;
19704     },
19705
19706     onRender : function(ct,position){
19707         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19708         this.raphael = Raphael(this.el.dom);
19709         
19710                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19711                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19712                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19713                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19714                 /*
19715                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19716                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19717                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19718                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19719                 
19720                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19721                 r.barchart(330, 10, 300, 220, data1);
19722                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19723                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19724                 */
19725                 
19726                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19727                 // r.barchart(30, 30, 560, 250,  xdata, {
19728                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19729                 //     axis : "0 0 1 1",
19730                 //     axisxlabels :  xdata
19731                 //     //yvalues : cols,
19732                    
19733                 // });
19734 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19735 //        
19736 //        this.load(null,xdata,{
19737 //                axis : "0 0 1 1",
19738 //                axisxlabels :  xdata
19739 //                });
19740
19741     },
19742
19743     load : function(graphtype,xdata,opts){
19744         this.raphael.clear();
19745         if(!graphtype) {
19746             graphtype = this.graphtype;
19747         }
19748         if(!opts){
19749             opts = this.opts;
19750         }
19751         var r = this.raphael,
19752             fin = function () {
19753                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19754             },
19755             fout = function () {
19756                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19757             },
19758             pfin = function() {
19759                 this.sector.stop();
19760                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19761
19762                 if (this.label) {
19763                     this.label[0].stop();
19764                     this.label[0].attr({ r: 7.5 });
19765                     this.label[1].attr({ "font-weight": 800 });
19766                 }
19767             },
19768             pfout = function() {
19769                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19770
19771                 if (this.label) {
19772                     this.label[0].animate({ r: 5 }, 500, "bounce");
19773                     this.label[1].attr({ "font-weight": 400 });
19774                 }
19775             };
19776
19777         switch(graphtype){
19778             case 'bar':
19779                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19780                 break;
19781             case 'hbar':
19782                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19783                 break;
19784             case 'pie':
19785 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19786 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19787 //            
19788                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19789                 
19790                 break;
19791
19792         }
19793         
19794         if(this.title){
19795             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19796         }
19797         
19798     },
19799     
19800     setTitle: function(o)
19801     {
19802         this.title = o;
19803     },
19804     
19805     initEvents: function() {
19806         
19807         if(!this.href){
19808             this.el.on('click', this.onClick, this);
19809         }
19810     },
19811     
19812     onClick : function(e)
19813     {
19814         Roo.log('img onclick');
19815         this.fireEvent('click', this, e);
19816     }
19817    
19818 });
19819
19820  
19821 /*
19822  * - LGPL
19823  *
19824  * numberBox
19825  * 
19826  */
19827 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19828
19829 /**
19830  * @class Roo.bootstrap.dash.NumberBox
19831  * @extends Roo.bootstrap.Component
19832  * Bootstrap NumberBox class
19833  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19834  * @cfg {String} headline Box headline
19835  * @cfg {String} content Box content
19836  * @cfg {String} icon Box icon
19837  * @cfg {String} footer Footer text
19838  * @cfg {String} fhref Footer href
19839  * 
19840  * @constructor
19841  * Create a new NumberBox
19842  * @param {Object} config The config object
19843  */
19844
19845
19846 Roo.bootstrap.dash.NumberBox = function(config){
19847     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19848     
19849 };
19850
19851 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19852     
19853     bgcolor : 'aqua',
19854     headline : '',
19855     content : '',
19856     icon : '',
19857     footer : '',
19858     fhref : '',
19859     ficon : '',
19860     
19861     getAutoCreate : function(){
19862         
19863         var cfg = {
19864             tag : 'div',
19865             cls : 'small-box bg-' + this.bgcolor,
19866             cn : [
19867                 {
19868                     tag : 'div',
19869                     cls : 'inner',
19870                     cn :[
19871                         {
19872                             tag : 'h3',
19873                             cls : 'roo-headline',
19874                             html : this.headline
19875                         },
19876                         {
19877                             tag : 'p',
19878                             cls : 'roo-content',
19879                             html : this.content
19880                         }
19881                     ]
19882                 }
19883             ]
19884         }
19885         
19886         if(this.icon){
19887             cfg.cn.push({
19888                 tag : 'div',
19889                 cls : 'icon',
19890                 cn :[
19891                     {
19892                         tag : 'i',
19893                         cls : 'ion ' + this.icon
19894                     }
19895                 ]
19896             });
19897         }
19898         
19899         if(this.footer){
19900             var footer = {
19901                 tag : 'a',
19902                 cls : 'small-box-footer',
19903                 href : this.fhref || '#',
19904                 html : this.footer
19905             };
19906             
19907             cfg.cn.push(footer);
19908             
19909         }
19910         
19911         return  cfg;
19912     },
19913
19914     onRender : function(ct,position){
19915         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19916
19917
19918        
19919                 
19920     },
19921
19922     setHeadline: function (value)
19923     {
19924         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19925     },
19926     
19927     setFooter: function (value, href)
19928     {
19929         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19930         
19931         if(href){
19932             this.el.select('a.small-box-footer',true).first().attr('href', href);
19933         }
19934         
19935     },
19936
19937     setContent: function (value)
19938     {
19939         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19940     },
19941
19942     initEvents: function() 
19943     {   
19944         
19945     }
19946     
19947 });
19948
19949  
19950 /*
19951  * - LGPL
19952  *
19953  * TabBox
19954  * 
19955  */
19956 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19957
19958 /**
19959  * @class Roo.bootstrap.dash.TabBox
19960  * @extends Roo.bootstrap.Component
19961  * Bootstrap TabBox class
19962  * @cfg {String} title Title of the TabBox
19963  * @cfg {String} icon Icon of the TabBox
19964  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19965  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19966  * 
19967  * @constructor
19968  * Create a new TabBox
19969  * @param {Object} config The config object
19970  */
19971
19972
19973 Roo.bootstrap.dash.TabBox = function(config){
19974     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19975     this.addEvents({
19976         // raw events
19977         /**
19978          * @event addpane
19979          * When a pane is added
19980          * @param {Roo.bootstrap.dash.TabPane} pane
19981          */
19982         "addpane" : true,
19983         /**
19984          * @event activatepane
19985          * When a pane is activated
19986          * @param {Roo.bootstrap.dash.TabPane} pane
19987          */
19988         "activatepane" : true
19989         
19990          
19991     });
19992     
19993     this.panes = [];
19994 };
19995
19996 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19997
19998     title : '',
19999     icon : false,
20000     showtabs : true,
20001     tabScrollable : false,
20002     
20003     getChildContainer : function()
20004     {
20005         return this.el.select('.tab-content', true).first();
20006     },
20007     
20008     getAutoCreate : function(){
20009         
20010         var header = {
20011             tag: 'li',
20012             cls: 'pull-left header',
20013             html: this.title,
20014             cn : []
20015         };
20016         
20017         if(this.icon){
20018             header.cn.push({
20019                 tag: 'i',
20020                 cls: 'fa ' + this.icon
20021             });
20022         }
20023         
20024         var h = {
20025             tag: 'ul',
20026             cls: 'nav nav-tabs pull-right',
20027             cn: [
20028                 header
20029             ]
20030         };
20031         
20032         if(this.tabScrollable){
20033             h = {
20034                 tag: 'div',
20035                 cls: 'tab-header',
20036                 cn: [
20037                     {
20038                         tag: 'ul',
20039                         cls: 'nav nav-tabs pull-right',
20040                         cn: [
20041                             header
20042                         ]
20043                     }
20044                 ]
20045             }
20046         }
20047         
20048         var cfg = {
20049             tag: 'div',
20050             cls: 'nav-tabs-custom',
20051             cn: [
20052                 h,
20053                 {
20054                     tag: 'div',
20055                     cls: 'tab-content no-padding',
20056                     cn: []
20057                 }
20058             ]
20059         }
20060
20061         return  cfg;
20062     },
20063     initEvents : function()
20064     {
20065         //Roo.log('add add pane handler');
20066         this.on('addpane', this.onAddPane, this);
20067     },
20068      /**
20069      * Updates the box title
20070      * @param {String} html to set the title to.
20071      */
20072     setTitle : function(value)
20073     {
20074         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20075     },
20076     onAddPane : function(pane)
20077     {
20078         this.panes.push(pane);
20079         //Roo.log('addpane');
20080         //Roo.log(pane);
20081         // tabs are rendere left to right..
20082         if(!this.showtabs){
20083             return;
20084         }
20085         
20086         var ctr = this.el.select('.nav-tabs', true).first();
20087          
20088          
20089         var existing = ctr.select('.nav-tab',true);
20090         var qty = existing.getCount();;
20091         
20092         
20093         var tab = ctr.createChild({
20094             tag : 'li',
20095             cls : 'nav-tab' + (qty ? '' : ' active'),
20096             cn : [
20097                 {
20098                     tag : 'a',
20099                     href:'#',
20100                     html : pane.title
20101                 }
20102             ]
20103         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20104         pane.tab = tab;
20105         
20106         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20107         if (!qty) {
20108             pane.el.addClass('active');
20109         }
20110         
20111                 
20112     },
20113     onTabClick : function(ev,un,ob,pane)
20114     {
20115         //Roo.log('tab - prev default');
20116         ev.preventDefault();
20117         
20118         
20119         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20120         pane.tab.addClass('active');
20121         //Roo.log(pane.title);
20122         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20123         // technically we should have a deactivate event.. but maybe add later.
20124         // and it should not de-activate the selected tab...
20125         this.fireEvent('activatepane', pane);
20126         pane.el.addClass('active');
20127         pane.fireEvent('activate');
20128         
20129         
20130     },
20131     
20132     getActivePane : function()
20133     {
20134         var r = false;
20135         Roo.each(this.panes, function(p) {
20136             if(p.el.hasClass('active')){
20137                 r = p;
20138                 return false;
20139             }
20140             
20141             return;
20142         });
20143         
20144         return r;
20145     }
20146     
20147     
20148 });
20149
20150  
20151 /*
20152  * - LGPL
20153  *
20154  * Tab pane
20155  * 
20156  */
20157 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20158 /**
20159  * @class Roo.bootstrap.TabPane
20160  * @extends Roo.bootstrap.Component
20161  * Bootstrap TabPane class
20162  * @cfg {Boolean} active (false | true) Default false
20163  * @cfg {String} title title of panel
20164
20165  * 
20166  * @constructor
20167  * Create a new TabPane
20168  * @param {Object} config The config object
20169  */
20170
20171 Roo.bootstrap.dash.TabPane = function(config){
20172     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20173     
20174     this.addEvents({
20175         // raw events
20176         /**
20177          * @event activate
20178          * When a pane is activated
20179          * @param {Roo.bootstrap.dash.TabPane} pane
20180          */
20181         "activate" : true
20182          
20183     });
20184 };
20185
20186 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20187     
20188     active : false,
20189     title : '',
20190     
20191     // the tabBox that this is attached to.
20192     tab : false,
20193      
20194     getAutoCreate : function() 
20195     {
20196         var cfg = {
20197             tag: 'div',
20198             cls: 'tab-pane'
20199         }
20200         
20201         if(this.active){
20202             cfg.cls += ' active';
20203         }
20204         
20205         return cfg;
20206     },
20207     initEvents  : function()
20208     {
20209         //Roo.log('trigger add pane handler');
20210         this.parent().fireEvent('addpane', this)
20211     },
20212     
20213      /**
20214      * Updates the tab title 
20215      * @param {String} html to set the title to.
20216      */
20217     setTitle: function(str)
20218     {
20219         if (!this.tab) {
20220             return;
20221         }
20222         this.title = str;
20223         this.tab.select('a', true).first().dom.innerHTML = str;
20224         
20225     }
20226     
20227     
20228     
20229 });
20230
20231  
20232
20233
20234  /*
20235  * - LGPL
20236  *
20237  * menu
20238  * 
20239  */
20240 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20241
20242 /**
20243  * @class Roo.bootstrap.menu.Menu
20244  * @extends Roo.bootstrap.Component
20245  * Bootstrap Menu class - container for Menu
20246  * @cfg {String} html Text of the menu
20247  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20248  * @cfg {String} icon Font awesome icon
20249  * @cfg {String} pos Menu align to (top | bottom) default bottom
20250  * 
20251  * 
20252  * @constructor
20253  * Create a new Menu
20254  * @param {Object} config The config object
20255  */
20256
20257
20258 Roo.bootstrap.menu.Menu = function(config){
20259     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20260     
20261     this.addEvents({
20262         /**
20263          * @event beforeshow
20264          * Fires before this menu is displayed
20265          * @param {Roo.bootstrap.menu.Menu} this
20266          */
20267         beforeshow : true,
20268         /**
20269          * @event beforehide
20270          * Fires before this menu is hidden
20271          * @param {Roo.bootstrap.menu.Menu} this
20272          */
20273         beforehide : true,
20274         /**
20275          * @event show
20276          * Fires after this menu is displayed
20277          * @param {Roo.bootstrap.menu.Menu} this
20278          */
20279         show : true,
20280         /**
20281          * @event hide
20282          * Fires after this menu is hidden
20283          * @param {Roo.bootstrap.menu.Menu} this
20284          */
20285         hide : true,
20286         /**
20287          * @event click
20288          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20289          * @param {Roo.bootstrap.menu.Menu} this
20290          * @param {Roo.EventObject} e
20291          */
20292         click : true
20293     });
20294     
20295 };
20296
20297 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20298     
20299     submenu : false,
20300     html : '',
20301     weight : 'default',
20302     icon : false,
20303     pos : 'bottom',
20304     
20305     
20306     getChildContainer : function() {
20307         if(this.isSubMenu){
20308             return this.el;
20309         }
20310         
20311         return this.el.select('ul.dropdown-menu', true).first();  
20312     },
20313     
20314     getAutoCreate : function()
20315     {
20316         var text = [
20317             {
20318                 tag : 'span',
20319                 cls : 'roo-menu-text',
20320                 html : this.html
20321             }
20322         ];
20323         
20324         if(this.icon){
20325             text.unshift({
20326                 tag : 'i',
20327                 cls : 'fa ' + this.icon
20328             })
20329         }
20330         
20331         
20332         var cfg = {
20333             tag : 'div',
20334             cls : 'btn-group',
20335             cn : [
20336                 {
20337                     tag : 'button',
20338                     cls : 'dropdown-button btn btn-' + this.weight,
20339                     cn : text
20340                 },
20341                 {
20342                     tag : 'button',
20343                     cls : 'dropdown-toggle btn btn-' + this.weight,
20344                     cn : [
20345                         {
20346                             tag : 'span',
20347                             cls : 'caret'
20348                         }
20349                     ]
20350                 },
20351                 {
20352                     tag : 'ul',
20353                     cls : 'dropdown-menu'
20354                 }
20355             ]
20356             
20357         };
20358         
20359         if(this.pos == 'top'){
20360             cfg.cls += ' dropup';
20361         }
20362         
20363         if(this.isSubMenu){
20364             cfg = {
20365                 tag : 'ul',
20366                 cls : 'dropdown-menu'
20367             }
20368         }
20369         
20370         return cfg;
20371     },
20372     
20373     onRender : function(ct, position)
20374     {
20375         this.isSubMenu = ct.hasClass('dropdown-submenu');
20376         
20377         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20378     },
20379     
20380     initEvents : function() 
20381     {
20382         if(this.isSubMenu){
20383             return;
20384         }
20385         
20386         this.hidden = true;
20387         
20388         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20389         this.triggerEl.on('click', this.onTriggerPress, this);
20390         
20391         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20392         this.buttonEl.on('click', this.onClick, this);
20393         
20394     },
20395     
20396     list : function()
20397     {
20398         if(this.isSubMenu){
20399             return this.el;
20400         }
20401         
20402         return this.el.select('ul.dropdown-menu', true).first();
20403     },
20404     
20405     onClick : function(e)
20406     {
20407         this.fireEvent("click", this, e);
20408     },
20409     
20410     onTriggerPress  : function(e)
20411     {   
20412         if (this.isVisible()) {
20413             this.hide();
20414         } else {
20415             this.show();
20416         }
20417     },
20418     
20419     isVisible : function(){
20420         return !this.hidden;
20421     },
20422     
20423     show : function()
20424     {
20425         this.fireEvent("beforeshow", this);
20426         
20427         this.hidden = false;
20428         this.el.addClass('open');
20429         
20430         Roo.get(document).on("mouseup", this.onMouseUp, this);
20431         
20432         this.fireEvent("show", this);
20433         
20434         
20435     },
20436     
20437     hide : function()
20438     {
20439         this.fireEvent("beforehide", this);
20440         
20441         this.hidden = true;
20442         this.el.removeClass('open');
20443         
20444         Roo.get(document).un("mouseup", this.onMouseUp);
20445         
20446         this.fireEvent("hide", this);
20447     },
20448     
20449     onMouseUp : function()
20450     {
20451         this.hide();
20452     }
20453     
20454 });
20455
20456  
20457  /*
20458  * - LGPL
20459  *
20460  * menu item
20461  * 
20462  */
20463 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20464
20465 /**
20466  * @class Roo.bootstrap.menu.Item
20467  * @extends Roo.bootstrap.Component
20468  * Bootstrap MenuItem class
20469  * @cfg {Boolean} submenu (true | false) default false
20470  * @cfg {String} html text of the item
20471  * @cfg {String} href the link
20472  * @cfg {Boolean} disable (true | false) default false
20473  * @cfg {Boolean} preventDefault (true | false) default true
20474  * @cfg {String} icon Font awesome icon
20475  * @cfg {String} pos Submenu align to (left | right) default right 
20476  * 
20477  * 
20478  * @constructor
20479  * Create a new Item
20480  * @param {Object} config The config object
20481  */
20482
20483
20484 Roo.bootstrap.menu.Item = function(config){
20485     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20486     this.addEvents({
20487         /**
20488          * @event mouseover
20489          * Fires when the mouse is hovering over this menu
20490          * @param {Roo.bootstrap.menu.Item} this
20491          * @param {Roo.EventObject} e
20492          */
20493         mouseover : true,
20494         /**
20495          * @event mouseout
20496          * Fires when the mouse exits this menu
20497          * @param {Roo.bootstrap.menu.Item} this
20498          * @param {Roo.EventObject} e
20499          */
20500         mouseout : true,
20501         // raw events
20502         /**
20503          * @event click
20504          * The raw click event for the entire grid.
20505          * @param {Roo.EventObject} e
20506          */
20507         click : true
20508     });
20509 };
20510
20511 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20512     
20513     submenu : false,
20514     href : '',
20515     html : '',
20516     preventDefault: true,
20517     disable : false,
20518     icon : false,
20519     pos : 'right',
20520     
20521     getAutoCreate : function()
20522     {
20523         var text = [
20524             {
20525                 tag : 'span',
20526                 cls : 'roo-menu-item-text',
20527                 html : this.html
20528             }
20529         ];
20530         
20531         if(this.icon){
20532             text.unshift({
20533                 tag : 'i',
20534                 cls : 'fa ' + this.icon
20535             })
20536         }
20537         
20538         var cfg = {
20539             tag : 'li',
20540             cn : [
20541                 {
20542                     tag : 'a',
20543                     href : this.href || '#',
20544                     cn : text
20545                 }
20546             ]
20547         };
20548         
20549         if(this.disable){
20550             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20551         }
20552         
20553         if(this.submenu){
20554             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20555             
20556             if(this.pos == 'left'){
20557                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20558             }
20559         }
20560         
20561         return cfg;
20562     },
20563     
20564     initEvents : function() 
20565     {
20566         this.el.on('mouseover', this.onMouseOver, this);
20567         this.el.on('mouseout', this.onMouseOut, this);
20568         
20569         this.el.select('a', true).first().on('click', this.onClick, this);
20570         
20571     },
20572     
20573     onClick : function(e)
20574     {
20575         if(this.preventDefault){
20576             e.preventDefault();
20577         }
20578         
20579         this.fireEvent("click", this, e);
20580     },
20581     
20582     onMouseOver : function(e)
20583     {
20584         if(this.submenu && this.pos == 'left'){
20585             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20586         }
20587         
20588         this.fireEvent("mouseover", this, e);
20589     },
20590     
20591     onMouseOut : function(e)
20592     {
20593         this.fireEvent("mouseout", this, e);
20594     }
20595 });
20596
20597  
20598
20599  /*
20600  * - LGPL
20601  *
20602  * menu separator
20603  * 
20604  */
20605 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20606
20607 /**
20608  * @class Roo.bootstrap.menu.Separator
20609  * @extends Roo.bootstrap.Component
20610  * Bootstrap Separator class
20611  * 
20612  * @constructor
20613  * Create a new Separator
20614  * @param {Object} config The config object
20615  */
20616
20617
20618 Roo.bootstrap.menu.Separator = function(config){
20619     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20620 };
20621
20622 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20623     
20624     getAutoCreate : function(){
20625         var cfg = {
20626             tag : 'li',
20627             cls: 'divider'
20628         };
20629         
20630         return cfg;
20631     }
20632    
20633 });
20634
20635  
20636
20637  /*
20638  * - LGPL
20639  *
20640  * Tooltip
20641  * 
20642  */
20643
20644 /**
20645  * @class Roo.bootstrap.Tooltip
20646  * Bootstrap Tooltip class
20647  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20648  * to determine which dom element triggers the tooltip.
20649  * 
20650  * It needs to add support for additional attributes like tooltip-position
20651  * 
20652  * @constructor
20653  * Create a new Toolti
20654  * @param {Object} config The config object
20655  */
20656
20657 Roo.bootstrap.Tooltip = function(config){
20658     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20659 };
20660
20661 Roo.apply(Roo.bootstrap.Tooltip, {
20662     /**
20663      * @function init initialize tooltip monitoring.
20664      * @static
20665      */
20666     currentEl : false,
20667     currentTip : false,
20668     currentRegion : false,
20669     
20670     //  init : delay?
20671     
20672     init : function()
20673     {
20674         Roo.get(document).on('mouseover', this.enter ,this);
20675         Roo.get(document).on('mouseout', this.leave, this);
20676          
20677         
20678         this.currentTip = new Roo.bootstrap.Tooltip();
20679     },
20680     
20681     enter : function(ev)
20682     {
20683         var dom = ev.getTarget();
20684         //Roo.log(['enter',dom]);
20685         var el = Roo.fly(dom);
20686         if (this.currentEl) {
20687             //Roo.log(dom);
20688             //Roo.log(this.currentEl);
20689             //Roo.log(this.currentEl.contains(dom));
20690             if (this.currentEl == el) {
20691                 return;
20692             }
20693             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20694                 return;
20695             }
20696
20697         }
20698         
20699         
20700         
20701         if (this.currentTip.el) {
20702             this.currentTip.el.hide(); // force hiding...
20703         }    
20704         //Roo.log(el);
20705         if (!el.attr('tooltip')) { // parents who have tip?
20706             return;
20707         }
20708         this.currentEl = el;
20709         this.currentTip.bind(el);
20710         this.currentRegion = Roo.lib.Region.getRegion(dom);
20711         this.currentTip.enter();
20712         
20713     },
20714     leave : function(ev)
20715     {
20716         var dom = ev.getTarget();
20717         //Roo.log(['leave',dom]);
20718         if (!this.currentEl) {
20719             return;
20720         }
20721         
20722         
20723         if (dom != this.currentEl.dom) {
20724             return;
20725         }
20726         var xy = ev.getXY();
20727         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
20728             return;
20729         }
20730         // only activate leave if mouse cursor is outside... bounding box..
20731         
20732         
20733         
20734         
20735         if (this.currentTip) {
20736             this.currentTip.leave();
20737         }
20738         //Roo.log('clear currentEl');
20739         this.currentEl = false;
20740         
20741         
20742     },
20743     alignment : {
20744         'left' : ['r-l', [-2,0], 'right'],
20745         'right' : ['l-r', [2,0], 'left'],
20746         'bottom' : ['t-b', [0,2], 'top'],
20747         'top' : [ 'b-t', [0,-2], 'bottom']
20748     }
20749     
20750 });
20751
20752
20753 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
20754     
20755     
20756     bindEl : false,
20757     
20758     delay : null, // can be { show : 300 , hide: 500}
20759     
20760     timeout : null,
20761     
20762     hoverState : null, //???
20763     
20764     placement : 'bottom', 
20765     
20766     getAutoCreate : function(){
20767     
20768         var cfg = {
20769            cls : 'tooltip',
20770            role : 'tooltip',
20771            cn : [
20772                 {
20773                     cls : 'tooltip-arrow'
20774                 },
20775                 {
20776                     cls : 'tooltip-inner'
20777                 }
20778            ]
20779         };
20780         
20781         return cfg;
20782     },
20783     bind : function(el)
20784     {
20785         this.bindEl = el;
20786     },
20787       
20788     
20789     enter : function () {
20790        
20791         if (this.timeout != null) {
20792             clearTimeout(this.timeout);
20793         }
20794         
20795         this.hoverState = 'in'
20796          //Roo.log("enter - show");
20797         if (!this.delay || !this.delay.show) {
20798             this.show();
20799             return;
20800         }
20801         var _t = this;
20802         this.timeout = setTimeout(function () {
20803             if (_t.hoverState == 'in') {
20804                 _t.show();
20805             }
20806         }, this.delay.show);
20807     },
20808     leave : function()
20809     {
20810         clearTimeout(this.timeout);
20811     
20812         this.hoverState = 'out'
20813          if (!this.delay || !this.delay.hide) {
20814             this.hide();
20815             return 
20816         }
20817        
20818         var _t = this;
20819         this.timeout = setTimeout(function () {
20820             //Roo.log("leave - timeout");
20821             
20822             if (_t.hoverState == 'out') {
20823                 _t.hide();
20824                 Roo.bootstrap.Tooltip.currentEl = false;
20825             }
20826         }, delay)
20827     },
20828     
20829     show : function ()
20830     {
20831         if (!this.el) {
20832             this.render(document.body);
20833         }
20834         // set content.
20835         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
20836         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
20837         
20838         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
20839         
20840         var placement = typeof this.placement == 'function' ?
20841             this.placement.call(this, this.el, on_el) :
20842             this.placement;
20843             
20844         var autoToken = /\s?auto?\s?/i;
20845         var autoPlace = autoToken.test(placement);
20846         if (autoPlace) {
20847             placement = placement.replace(autoToken, '') || 'top';
20848         }
20849         
20850         //this.el.detach()
20851         //this.el.setXY([0,0]);
20852         this.el.show();
20853         //this.el.dom.style.display='block';
20854         this.el.addClass(placement);
20855         
20856         //this.el.appendTo(on_el);
20857         
20858         var p = this.getPosition();
20859         var box = this.el.getBox();
20860         
20861         if (autoPlace) {
20862             // fixme..
20863         }
20864         var align = Roo.bootstrap.Tooltip.alignment[placement]
20865         this.el.alignTo(this.bindEl, align[0],align[1]);
20866         //var arrow = this.el.select('.arrow',true).first();
20867         //arrow.set(align[2], 
20868         
20869         this.el.addClass('in fade');
20870         this.hoverState = null;
20871         
20872         if (this.el.hasClass('fade')) {
20873             // fade it?
20874         }
20875         
20876     },
20877     hide : function()
20878     {
20879          
20880         if (!this.el) {
20881             return;
20882         }
20883         //this.el.setXY([0,0]);
20884         this.el.removeClass('in');
20885         //this.el.hide();
20886         
20887     }
20888     
20889 });
20890  
20891
20892