roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * 
21  * @constructor
22  * Do not use directly - it does not do anything..
23  * @param {Object} config The config object
24  */
25
26
27
28 Roo.bootstrap.Component = function(config){
29     Roo.bootstrap.Component.superclass.constructor.call(this, config);
30 };
31
32 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
33     
34     
35     allowDomMove : false, // to stop relocations in parent onRender...
36     
37     cls : false,
38     
39     style : false,
40     
41     autoCreate : false,
42     
43     tooltip : null,
44     /**
45      * Initialize Events for the element
46      */
47     initEvents : function() { },
48     
49     xattr : false,
50     
51     parentId : false,
52     
53     can_build_overlaid : true,
54     
55     dataId : false,
56     
57     name : false,
58     
59     parent: function() {
60         // returns the parent component..
61         return Roo.ComponentMgr.get(this.parentId)
62         
63         
64     },
65     
66     // private
67     onRender : function(ct, position)
68     {
69        // Roo.log("Call onRender: " + this.xtype);
70         
71         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
72         
73         if(this.el){
74             if (this.el.attr('xtype')) {
75                 this.el.attr('xtypex', this.el.attr('xtype'));
76                 this.el.dom.removeAttribute('xtype');
77                 
78                 this.initEvents();
79             }
80             
81             return;
82         }
83         
84          
85         
86         var cfg = Roo.apply({},  this.getAutoCreate());
87         cfg.id = Roo.id();
88         
89         // fill in the extra attributes 
90         if (this.xattr && typeof(this.xattr) =='object') {
91             for (var i in this.xattr) {
92                 cfg[i] = this.xattr[i];
93             }
94         }
95         
96         if(this.dataId){
97             cfg.dataId = this.dataId;
98         }
99         
100         if (this.cls) {
101             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
102         }
103         
104         if (this.style) { // fixme needs to support more complex style data.
105             cfg.style = this.style;
106         }
107         
108         if(this.name){
109             cfg.name = this.name;
110         }
111         
112        
113         
114         this.el = ct.createChild(cfg, position);
115         
116         if (this.tooltip) {
117             this.tooltipEl().attr('tooltip', this.tooltip);
118         }
119         
120         if(this.tabIndex !== undefined){
121             this.el.dom.setAttribute('tabIndex', this.tabIndex);
122         }
123         this.initEvents();
124         
125         
126     },
127     /**
128      * Fetch the element to add children to
129      * @return {Roo.Element} defaults to this.el
130      */
131     getChildContainer : function()
132     {
133         return this.el;
134     },
135     /**
136      * Fetch the element to display the tooltip on.
137      * @return {Roo.Element} defaults to this.el
138      */
139     tooltipEl : function()
140     {
141         return this.el;
142     },
143         
144     addxtype  : function(tree,cntr)
145     {
146         var cn = this;
147         
148         cn = Roo.factory(tree);
149            
150         cn.parentType = this.xtype; //??
151         cn.parentId = this.id;
152         
153         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
154         
155         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
156         
157         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
158         
159         var build_from_html =  Roo.XComponent.build_from_html;
160           
161         var is_body  = (tree.xtype == 'Body') ;
162           
163         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
164           
165         var self_cntr_el = Roo.get(this[cntr](false));
166         
167         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
168             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
169                 return this.addxtypeChild(tree,cntr);
170             }
171             
172             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
173                 
174             if(echild){
175                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
176             }
177             
178             Roo.log('skipping render');
179             return cn;
180             
181         }
182         
183         var ret = false;
184         
185         while (true) {
186             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
187             
188             if (!echild) {
189                 break;
190             }
191             
192             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
193                 break;
194             }
195             
196             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
197         }
198         return ret;
199     },
200     
201     addxtypeChild : function (tree, cntr)
202     {
203         Roo.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.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
250                     Roo.log(self_cntr_el);
251                     Roo.log(echild);
252                     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.log('skipping render');
262                 Roo.log(tree);
263                 if (!cn.el) {
264                     Roo.log('skipping all children');
265                     skip_children = true;
266                 }
267                 
268              } else {
269                  
270                 // actually if flexy:foreach is found, we really want to create 
271                 // multiple copies here...
272                 //Roo.log('render');
273                 //Roo.log(this[cntr]());
274                 cn.render(this[cntr](true));
275              }
276             // then add the element..
277         }
278         
279         
280         // handle the kids..
281         
282         var nitems = [];
283         /*
284         if (typeof (tree.menu) != 'undefined') {
285             tree.menu.parentType = cn.xtype;
286             tree.menu.triggerEl = cn.el;
287             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
288             
289         }
290         */
291         if (!tree.items || !tree.items.length) {
292             cn.items = nitems;
293             return cn;
294         }
295         var items = tree.items;
296         delete tree.items;
297         
298         //Roo.log(items.length);
299             // add the items..
300         if (!skip_children) {    
301             for(var i =0;i < items.length;i++) {
302                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
303             }
304         }
305         
306         cn.items = nitems;
307         
308         return cn;
309     }
310     
311     
312     
313     
314 });
315
316  /*
317  * - LGPL
318  *
319  * Body
320  * 
321  */
322
323 /**
324  * @class Roo.bootstrap.Body
325  * @extends Roo.bootstrap.Component
326  * Bootstrap Body class
327  * 
328  * @constructor
329  * Create a new body
330  * @param {Object} config The config object
331  */
332
333 Roo.bootstrap.Body = function(config){
334     Roo.bootstrap.Body.superclass.constructor.call(this, config);
335     this.el = Roo.get(document.body);
336     if (this.cls && this.cls.length) {
337         Roo.get(document.body).addClass(this.cls);
338     }
339 };
340
341 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
342       
343         autoCreate : {
344         cls: 'container'
345     },
346     onRender : function(ct, position)
347     {
348        /* Roo.log("Roo.bootstrap.Body - onRender");
349         if (this.cls && this.cls.length) {
350             Roo.get(document.body).addClass(this.cls);
351         }
352         // style??? xttr???
353         */
354     }
355     
356     
357  
358    
359 });
360
361  /*
362  * - LGPL
363  *
364  * button group
365  * 
366  */
367
368
369 /**
370  * @class Roo.bootstrap.ButtonGroup
371  * @extends Roo.bootstrap.Component
372  * Bootstrap ButtonGroup class
373  * @cfg {String} size lg | sm | xs (default empty normal)
374  * @cfg {String} align vertical | justified  (default none)
375  * @cfg {String} direction up | down (default down)
376  * @cfg {Boolean} toolbar false | true
377  * @cfg {Boolean} btn true | false
378  * 
379  * 
380  * @constructor
381  * Create a new Input
382  * @param {Object} config The config object
383  */
384
385 Roo.bootstrap.ButtonGroup = function(config){
386     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
387 };
388
389 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
390     
391     size: '',
392     align: '',
393     direction: '',
394     toolbar: false,
395     btn: true,
396
397     getAutoCreate : function(){
398         var cfg = {
399             cls: 'btn-group',
400             html : null
401         }
402         
403         cfg.html = this.html || cfg.html;
404         
405         if (this.toolbar) {
406             cfg = {
407                 cls: 'btn-toolbar',
408                 html: null
409             }
410             
411             return cfg;
412         }
413         
414         if (['vertical','justified'].indexOf(this.align)!==-1) {
415             cfg.cls = 'btn-group-' + this.align;
416             
417             if (this.align == 'justified') {
418                 console.log(this.items);
419             }
420         }
421         
422         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
423             cfg.cls += ' btn-group-' + this.size;
424         }
425         
426         if (this.direction == 'up') {
427             cfg.cls += ' dropup' ;
428         }
429         
430         return cfg;
431     }
432    
433 });
434
435  /*
436  * - LGPL
437  *
438  * button
439  * 
440  */
441
442 /**
443  * @class Roo.bootstrap.Button
444  * @extends Roo.bootstrap.Component
445  * Bootstrap Button class
446  * @cfg {String} html The button content
447  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
448  * @cfg {String} size empty | lg | sm | xs
449  * @cfg {String} tag empty | a | input | submit
450  * @cfg {String} href empty or href
451  * @cfg {Boolean} disabled false | true
452  * @cfg {Boolean} isClose false | true
453  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
454  * @cfg {String} badge text for badge
455  * @cfg {String} theme default (or empty) | glow
456  * @cfg {Boolean} inverse false | true
457  * @cfg {Boolean} toggle false | true
458  * @cfg {String} ontext text for on toggle state
459  * @cfg {String} offtext text for off toggle state
460  * @cfg {Boolean} defaulton true | false
461  * @cfg {Boolean} preventDefault (true | false) default true
462  * @cfg {Boolean} removeClass true | false remove the standard class..
463  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
464  * 
465  * @constructor
466  * Create a new button
467  * @param {Object} config The config object
468  */
469
470
471 Roo.bootstrap.Button = function(config){
472     Roo.bootstrap.Button.superclass.constructor.call(this, config);
473     this.addEvents({
474         // raw events
475         /**
476          * @event click
477          * When a butotn is pressed
478          * @param {Roo.EventObject} e
479          */
480         "click" : true,
481          /**
482          * @event toggle
483          * After the button has been toggles
484          * @param {Roo.EventObject} e
485          * @param {boolean} pressed (also available as button.pressed)
486          */
487         "toggle" : true
488     });
489 };
490
491 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
492     html: false,
493     active: false,
494     weight: '',
495     size: '',
496     tag: 'button',
497     href: '',
498     disabled: false,
499     isClose: false,
500     glyphicon: '',
501     badge: '',
502     theme: 'default',
503     inverse: false,
504     
505     toggle: false,
506     ontext: 'ON',
507     offtext: 'OFF',
508     defaulton: true,
509     preventDefault: true,
510     removeClass: false,
511     name: false,
512     target: false,
513     
514     
515     pressed : null,
516      
517     
518     getAutoCreate : function(){
519         
520         var cfg = {
521             tag : 'button',
522             cls : 'roo-button',
523             html: ''
524         };
525         
526         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
527             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
528             this.tag = 'button';
529         } else {
530             cfg.tag = this.tag;
531         }
532         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
533         
534         if (this.toggle == true) {
535             cfg={
536                 tag: 'div',
537                 cls: 'slider-frame roo-button',
538                 cn: [
539                     {
540                         tag: 'span',
541                         'data-on-text':'ON',
542                         'data-off-text':'OFF',
543                         cls: 'slider-button',
544                         html: this.offtext
545                     }
546                 ]
547             };
548             
549             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
550                 cfg.cls += ' '+this.weight;
551             }
552             
553             return cfg;
554         }
555         
556         if (this.isClose) {
557             cfg.cls += ' close';
558             
559             cfg["aria-hidden"] = true;
560             
561             cfg.html = "&times;";
562             
563             return cfg;
564         }
565         
566          
567         if (this.theme==='default') {
568             cfg.cls = 'btn roo-button';
569             
570             //if (this.parentType != 'Navbar') {
571             this.weight = this.weight.length ?  this.weight : 'default';
572             //}
573             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
574                 
575                 cfg.cls += ' btn-' + this.weight;
576             }
577         } else if (this.theme==='glow') {
578             
579             cfg.tag = 'a';
580             cfg.cls = 'btn-glow roo-button';
581             
582             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
583                 
584                 cfg.cls += ' ' + this.weight;
585             }
586         }
587    
588         
589         if (this.inverse) {
590             this.cls += ' inverse';
591         }
592         
593         
594         if (this.active) {
595             cfg.cls += ' active';
596         }
597         
598         if (this.disabled) {
599             cfg.disabled = 'disabled';
600         }
601         
602         if (this.items) {
603             Roo.log('changing to ul' );
604             cfg.tag = 'ul';
605             this.glyphicon = 'caret';
606         }
607         
608         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
609          
610         //gsRoo.log(this.parentType);
611         if (this.parentType === 'Navbar' && !this.parent().bar) {
612             Roo.log('changing to li?');
613             
614             cfg.tag = 'li';
615             
616             cfg.cls = '';
617             cfg.cn =  [{
618                 tag : 'a',
619                 cls : 'roo-button',
620                 html : this.html,
621                 href : this.href || '#'
622             }];
623             if (this.menu) {
624                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
625                 cfg.cls += ' dropdown';
626             }   
627             
628             delete cfg.html;
629             
630         }
631         
632        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
633         
634         if (this.glyphicon) {
635             cfg.html = ' ' + cfg.html;
636             
637             cfg.cn = [
638                 {
639                     tag: 'span',
640                     cls: 'glyphicon glyphicon-' + this.glyphicon
641                 }
642             ];
643         }
644         
645         if (this.badge) {
646             cfg.html += ' ';
647             
648             cfg.tag = 'a';
649             
650 //            cfg.cls='btn roo-button';
651             
652             cfg.href=this.href;
653             
654             var value = cfg.html;
655             
656             if(this.glyphicon){
657                 value = {
658                             tag: 'span',
659                             cls: 'glyphicon glyphicon-' + this.glyphicon,
660                             html: this.html
661                         };
662                 
663             }
664             
665             cfg.cn = [
666                 value,
667                 {
668                     tag: 'span',
669                     cls: 'badge',
670                     html: this.badge
671                 }
672             ];
673             
674             cfg.html='';
675         }
676         
677         if (this.menu) {
678             cfg.cls += ' dropdown';
679             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
680         }
681         
682         if (cfg.tag !== 'a' && this.href !== '') {
683             throw "Tag must be a to set href.";
684         } else if (this.href.length > 0) {
685             cfg.href = this.href;
686         }
687         
688         if(this.removeClass){
689             cfg.cls = '';
690         }
691         
692         if(this.target){
693             cfg.target = this.target;
694         }
695         
696         return cfg;
697     },
698     initEvents: function() {
699        // Roo.log('init events?');
700 //        Roo.log(this.el.dom);
701         // add the menu...
702         
703         if (typeof (this.menu) != 'undefined') {
704             this.menu.parentType = this.xtype;
705             this.menu.triggerEl = this.el;
706             this.addxtype(Roo.apply({}, this.menu));
707         }
708
709
710        if (this.el.hasClass('roo-button')) {
711             this.el.on('click', this.onClick, this);
712        } else {
713             this.el.select('.roo-button').on('click', this.onClick, this);
714        }
715        
716        if(this.removeClass){
717            this.el.on('click', this.onClick, this);
718        }
719        
720        this.el.enableDisplayMode();
721         
722     },
723     onClick : function(e)
724     {
725         if (this.disabled) {
726             return;
727         }
728         
729         Roo.log('button on click ');
730         if(this.preventDefault){
731             e.preventDefault();
732         }
733         if (this.pressed === true || this.pressed === false) {
734             this.pressed = !this.pressed;
735             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
736             this.fireEvent('toggle', this, e, this.pressed);
737         }
738         
739         
740         this.fireEvent('click', this, e);
741     },
742     
743     /**
744      * Enables this button
745      */
746     enable : function()
747     {
748         this.disabled = false;
749         this.el.removeClass('disabled');
750     },
751     
752     /**
753      * Disable this button
754      */
755     disable : function()
756     {
757         this.disabled = true;
758         this.el.addClass('disabled');
759     },
760      /**
761      * sets the active state on/off, 
762      * @param {Boolean} state (optional) Force a particular state
763      */
764     setActive : function(v) {
765         
766         this.el[v ? 'addClass' : 'removeClass']('active');
767     },
768      /**
769      * toggles the current active state 
770      */
771     toggleActive : function()
772     {
773        var active = this.el.hasClass('active');
774        this.setActive(!active);
775        
776         
777     },
778     setText : function(str)
779     {
780         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
781     },
782     getText : function()
783     {
784         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
785     },
786     hide: function() {
787        
788      
789         this.el.hide();   
790     },
791     show: function() {
792        
793         this.el.show();   
794     }
795     
796     
797 });
798
799  /*
800  * - LGPL
801  *
802  * column
803  * 
804  */
805
806 /**
807  * @class Roo.bootstrap.Column
808  * @extends Roo.bootstrap.Component
809  * Bootstrap Column class
810  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
811  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
812  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
813  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
814  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
815  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
816  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
817  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
818  *
819  * 
820  * @cfg {Boolean} hidden (true|false) hide the element
821  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
822  * @cfg {String} fa (ban|check|...) font awesome icon
823  * @cfg {Number} fasize (1|2|....) font awsome size
824
825  * @cfg {String} icon (info-sign|check|...) glyphicon name
826
827  * @cfg {String} html content of column.
828  * 
829  * @constructor
830  * Create a new Column
831  * @param {Object} config The config object
832  */
833
834 Roo.bootstrap.Column = function(config){
835     Roo.bootstrap.Column.superclass.constructor.call(this, config);
836 };
837
838 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
839     
840     xs: false,
841     sm: false,
842     md: false,
843     lg: false,
844     xsoff: false,
845     smoff: false,
846     mdoff: false,
847     lgoff: false,
848     html: '',
849     offset: 0,
850     alert: false,
851     fa: false,
852     icon : false,
853     hidden : false,
854     fasize : 1,
855     
856     getAutoCreate : function(){
857         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
858         
859         cfg = {
860             tag: 'div',
861             cls: 'column'
862         };
863         
864         var settings=this;
865         ['xs','sm','md','lg'].map(function(size){
866             //Roo.log( size + ':' + settings[size]);
867             
868             if (settings[size+'off'] !== false) {
869                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
870             }
871             
872             if (settings[size] === false) {
873                 return;
874             }
875             Roo.log(settings[size]);
876             if (!settings[size]) { // 0 = hidden
877                 cfg.cls += ' hidden-' + size;
878                 return;
879             }
880             cfg.cls += ' col-' + size + '-' + settings[size];
881             
882         });
883         
884         if (this.hidden) {
885             cfg.cls += ' hidden';
886         }
887         
888         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
889             cfg.cls +=' alert alert-' + this.alert;
890         }
891         
892         
893         if (this.html.length) {
894             cfg.html = this.html;
895         }
896         if (this.fa) {
897             var fasize = '';
898             if (this.fasize > 1) {
899                 fasize = ' fa-' + this.fasize + 'x';
900             }
901             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
902             
903             
904         }
905         if (this.icon) {
906             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
907         }
908         
909         return cfg;
910     }
911    
912 });
913
914  
915
916  /*
917  * - LGPL
918  *
919  * page container.
920  * 
921  */
922
923
924 /**
925  * @class Roo.bootstrap.Container
926  * @extends Roo.bootstrap.Component
927  * Bootstrap Container class
928  * @cfg {Boolean} jumbotron is it a jumbotron element
929  * @cfg {String} html content of element
930  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
931  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
932  * @cfg {String} header content of header (for panel)
933  * @cfg {String} footer content of footer (for panel)
934  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
935  * @cfg {String} tag (header|aside|section) type of HTML tag.
936  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
937  * @cfg {String} fa (ban|check|...) font awesome icon
938  * @cfg {String} icon (info-sign|check|...) glyphicon name
939  * @cfg {Boolean} hidden (true|false) hide the element
940
941  *     
942  * @constructor
943  * Create a new Container
944  * @param {Object} config The config object
945  */
946
947 Roo.bootstrap.Container = function(config){
948     Roo.bootstrap.Container.superclass.constructor.call(this, config);
949 };
950
951 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
952     
953     jumbotron : false,
954     well: '',
955     panel : '',
956     header: '',
957     footer : '',
958     sticky: '',
959     tag : false,
960     alert : false,
961     fa: false,
962     icon : false,
963   
964      
965     getChildContainer : function() {
966         
967         if(!this.el){
968             return false;
969         }
970         
971         if (this.panel.length) {
972             return this.el.select('.panel-body',true).first();
973         }
974         
975         return this.el;
976     },
977     
978     
979     getAutoCreate : function(){
980         
981         var cfg = {
982             tag : this.tag || 'div',
983             html : '',
984             cls : ''
985         };
986         if (this.jumbotron) {
987             cfg.cls = 'jumbotron';
988         }
989         
990         
991         
992         // - this is applied by the parent..
993         //if (this.cls) {
994         //    cfg.cls = this.cls + '';
995         //}
996         
997         if (this.sticky.length) {
998             
999             var bd = Roo.get(document.body);
1000             if (!bd.hasClass('bootstrap-sticky')) {
1001                 bd.addClass('bootstrap-sticky');
1002                 Roo.select('html',true).setStyle('height', '100%');
1003             }
1004              
1005             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1006         }
1007         
1008         
1009         if (this.well.length) {
1010             switch (this.well) {
1011                 case 'lg':
1012                 case 'sm':
1013                     cfg.cls +=' well well-' +this.well;
1014                     break;
1015                 default:
1016                     cfg.cls +=' well';
1017                     break;
1018             }
1019         }
1020         
1021         if (this.hidden) {
1022             cfg.cls += ' hidden';
1023         }
1024         
1025         
1026         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1027             cfg.cls +=' alert alert-' + this.alert;
1028         }
1029         
1030         var body = cfg;
1031         
1032         if (this.panel.length) {
1033             cfg.cls += ' panel panel-' + this.panel;
1034             cfg.cn = [];
1035             if (this.header.length) {
1036                 cfg.cn.push({
1037                     
1038                     cls : 'panel-heading',
1039                     cn : [{
1040                         tag: 'h3',
1041                         cls : 'panel-title',
1042                         html : this.header
1043                     }]
1044                     
1045                 });
1046             }
1047             body = false;
1048             cfg.cn.push({
1049                 cls : 'panel-body',
1050                 html : this.html
1051             });
1052             
1053             
1054             if (this.footer.length) {
1055                 cfg.cn.push({
1056                     cls : 'panel-footer',
1057                     html : this.footer
1058                     
1059                 });
1060             }
1061             
1062         }
1063         
1064         if (body) {
1065             body.html = this.html || cfg.html;
1066             // prefix with the icons..
1067             if (this.fa) {
1068                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1069             }
1070             if (this.icon) {
1071                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1072             }
1073             
1074             
1075         }
1076         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1077             cfg.cls =  'container';
1078         }
1079         
1080         return cfg;
1081     },
1082     
1083     titleEl : function()
1084     {
1085         if(!this.el || !this.panel.length || !this.header.length){
1086             return;
1087         }
1088         
1089         return this.el.select('.panel-title',true).first();
1090     },
1091     
1092     setTitle : function(v)
1093     {
1094         var titleEl = this.titleEl();
1095         
1096         if(!titleEl){
1097             return;
1098         }
1099         
1100         titleEl.dom.innerHTML = v;
1101     },
1102     
1103     getTitle : function()
1104     {
1105         
1106         var titleEl = this.titleEl();
1107         
1108         if(!titleEl){
1109             return '';
1110         }
1111         
1112         return titleEl.dom.innerHTML;
1113     }
1114    
1115 });
1116
1117  /*
1118  * - LGPL
1119  *
1120  * image
1121  * 
1122  */
1123
1124
1125 /**
1126  * @class Roo.bootstrap.Img
1127  * @extends Roo.bootstrap.Component
1128  * Bootstrap Img class
1129  * @cfg {Boolean} imgResponsive false | true
1130  * @cfg {String} border rounded | circle | thumbnail
1131  * @cfg {String} src image source
1132  * @cfg {String} alt image alternative text
1133  * @cfg {String} href a tag href
1134  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1135  * 
1136  * @constructor
1137  * Create a new Input
1138  * @param {Object} config The config object
1139  */
1140
1141 Roo.bootstrap.Img = function(config){
1142     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1143     
1144     this.addEvents({
1145         // img events
1146         /**
1147          * @event click
1148          * The img click event for the img.
1149          * @param {Roo.EventObject} e
1150          */
1151         "click" : true
1152     });
1153 };
1154
1155 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1156     
1157     imgResponsive: true,
1158     border: '',
1159     src: '',
1160     href: false,
1161     target: false,
1162
1163     getAutoCreate : function(){
1164         
1165         var cfg = {
1166             tag: 'img',
1167             cls: (this.imgResponsive) ? 'img-responsive' : '',
1168             html : null
1169         }
1170         
1171         cfg.html = this.html || cfg.html;
1172         
1173         cfg.src = this.src || cfg.src;
1174         
1175         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1176             cfg.cls += ' img-' + this.border;
1177         }
1178         
1179         if(this.alt){
1180             cfg.alt = this.alt;
1181         }
1182         
1183         if(this.href){
1184             var a = {
1185                 tag: 'a',
1186                 href: this.href,
1187                 cn: [
1188                     cfg
1189                 ]
1190             }
1191             
1192             if(this.target){
1193                 a.target = this.target;
1194             }
1195             
1196         }
1197         
1198         
1199         return (this.href) ? a : cfg;
1200     },
1201     
1202     initEvents: function() {
1203         
1204         if(!this.href){
1205             this.el.on('click', this.onClick, this);
1206         }
1207     },
1208     
1209     onClick : function(e)
1210     {
1211         Roo.log('img onclick');
1212         this.fireEvent('click', this, e);
1213     }
1214    
1215 });
1216
1217  /*
1218  * - LGPL
1219  *
1220  * image
1221  * 
1222  */
1223
1224
1225 /**
1226  * @class Roo.bootstrap.Link
1227  * @extends Roo.bootstrap.Component
1228  * Bootstrap Link Class
1229  * @cfg {String} alt image alternative text
1230  * @cfg {String} href a tag href
1231  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1232  * @cfg {String} html the content of the link.
1233  * @cfg {String} anchor name for the anchor link
1234
1235  * @cfg {Boolean} preventDefault (true | false) default false
1236
1237  * 
1238  * @constructor
1239  * Create a new Input
1240  * @param {Object} config The config object
1241  */
1242
1243 Roo.bootstrap.Link = function(config){
1244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1245     
1246     this.addEvents({
1247         // img events
1248         /**
1249          * @event click
1250          * The img click event for the img.
1251          * @param {Roo.EventObject} e
1252          */
1253         "click" : true
1254     });
1255 };
1256
1257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1258     
1259     href: false,
1260     target: false,
1261     preventDefault: false,
1262     anchor : false,
1263     alt : false,
1264
1265     getAutoCreate : function()
1266     {
1267         
1268         var cfg = {
1269             tag: 'a'
1270         };
1271         // anchor's do not require html/href...
1272         if (this.anchor === false) {
1273             cfg.html = this.html || 'html-missing';
1274             cfg.href = this.href || '#';
1275         } else {
1276             cfg.name = this.anchor;
1277             if (this.html !== false) {
1278                 cfg.html = this.html;
1279             }
1280             if (this.href !== false) {
1281                 cfg.href = this.href;
1282             }
1283         }
1284         
1285         if(this.alt !== false){
1286             cfg.alt = this.alt;
1287         }
1288         
1289         
1290         if(this.target !== false) {
1291             cfg.target = this.target;
1292         }
1293         
1294         return cfg;
1295     },
1296     
1297     initEvents: function() {
1298         
1299         if(!this.href || this.preventDefault){
1300             this.el.on('click', this.onClick, this);
1301         }
1302     },
1303     
1304     onClick : function(e)
1305     {
1306         if(this.preventDefault){
1307             e.preventDefault();
1308         }
1309         //Roo.log('img onclick');
1310         this.fireEvent('click', this, e);
1311     }
1312    
1313 });
1314
1315  /*
1316  * - LGPL
1317  *
1318  * header
1319  * 
1320  */
1321
1322 /**
1323  * @class Roo.bootstrap.Header
1324  * @extends Roo.bootstrap.Component
1325  * Bootstrap Header class
1326  * @cfg {String} html content of header
1327  * @cfg {Number} level (1|2|3|4|5|6) default 1
1328  * 
1329  * @constructor
1330  * Create a new Header
1331  * @param {Object} config The config object
1332  */
1333
1334
1335 Roo.bootstrap.Header  = function(config){
1336     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1337 };
1338
1339 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1340     
1341     //href : false,
1342     html : false,
1343     level : 1,
1344     
1345     
1346     
1347     getAutoCreate : function(){
1348         
1349         var cfg = {
1350             tag: 'h' + (1 *this.level),
1351             html: this.html || 'fill in html'
1352         } ;
1353         
1354         return cfg;
1355     }
1356    
1357 });
1358
1359  
1360
1361  /*
1362  * Based on:
1363  * Ext JS Library 1.1.1
1364  * Copyright(c) 2006-2007, Ext JS, LLC.
1365  *
1366  * Originally Released Under LGPL - original licence link has changed is not relivant.
1367  *
1368  * Fork - LGPL
1369  * <script type="text/javascript">
1370  */
1371  
1372 /**
1373  * @class Roo.bootstrap.MenuMgr
1374  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1375  * @singleton
1376  */
1377 Roo.bootstrap.MenuMgr = function(){
1378    var menus, active, groups = {}, attached = false, lastShow = new Date();
1379
1380    // private - called when first menu is created
1381    function init(){
1382        menus = {};
1383        active = new Roo.util.MixedCollection();
1384        Roo.get(document).addKeyListener(27, function(){
1385            if(active.length > 0){
1386                hideAll();
1387            }
1388        });
1389    }
1390
1391    // private
1392    function hideAll(){
1393        if(active && active.length > 0){
1394            var c = active.clone();
1395            c.each(function(m){
1396                m.hide();
1397            });
1398        }
1399    }
1400
1401    // private
1402    function onHide(m){
1403        active.remove(m);
1404        if(active.length < 1){
1405            Roo.get(document).un("mouseup", onMouseDown);
1406             
1407            attached = false;
1408        }
1409    }
1410
1411    // private
1412    function onShow(m){
1413        var last = active.last();
1414        lastShow = new Date();
1415        active.add(m);
1416        if(!attached){
1417           Roo.get(document).on("mouseup", onMouseDown);
1418            
1419            attached = true;
1420        }
1421        if(m.parentMenu){
1422           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1423           m.parentMenu.activeChild = m;
1424        }else if(last && last.isVisible()){
1425           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1426        }
1427    }
1428
1429    // private
1430    function onBeforeHide(m){
1431        if(m.activeChild){
1432            m.activeChild.hide();
1433        }
1434        if(m.autoHideTimer){
1435            clearTimeout(m.autoHideTimer);
1436            delete m.autoHideTimer;
1437        }
1438    }
1439
1440    // private
1441    function onBeforeShow(m){
1442        var pm = m.parentMenu;
1443        if(!pm && !m.allowOtherMenus){
1444            hideAll();
1445        }else if(pm && pm.activeChild && active != m){
1446            pm.activeChild.hide();
1447        }
1448    }
1449
1450    // private
1451    function onMouseDown(e){
1452         Roo.log("on MouseDown");
1453         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1454            hideAll();
1455         }
1456         
1457         
1458    }
1459
1460    // private
1461    function onBeforeCheck(mi, state){
1462        if(state){
1463            var g = groups[mi.group];
1464            for(var i = 0, l = g.length; i < l; i++){
1465                if(g[i] != mi){
1466                    g[i].setChecked(false);
1467                }
1468            }
1469        }
1470    }
1471
1472    return {
1473
1474        /**
1475         * Hides all menus that are currently visible
1476         */
1477        hideAll : function(){
1478             hideAll();  
1479        },
1480
1481        // private
1482        register : function(menu){
1483            if(!menus){
1484                init();
1485            }
1486            menus[menu.id] = menu;
1487            menu.on("beforehide", onBeforeHide);
1488            menu.on("hide", onHide);
1489            menu.on("beforeshow", onBeforeShow);
1490            menu.on("show", onShow);
1491            var g = menu.group;
1492            if(g && menu.events["checkchange"]){
1493                if(!groups[g]){
1494                    groups[g] = [];
1495                }
1496                groups[g].push(menu);
1497                menu.on("checkchange", onCheck);
1498            }
1499        },
1500
1501         /**
1502          * Returns a {@link Roo.menu.Menu} object
1503          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1504          * be used to generate and return a new Menu instance.
1505          */
1506        get : function(menu){
1507            if(typeof menu == "string"){ // menu id
1508                return menus[menu];
1509            }else if(menu.events){  // menu instance
1510                return menu;
1511            }
1512            /*else if(typeof menu.length == 'number'){ // array of menu items?
1513                return new Roo.bootstrap.Menu({items:menu});
1514            }else{ // otherwise, must be a config
1515                return new Roo.bootstrap.Menu(menu);
1516            }
1517            */
1518            return false;
1519        },
1520
1521        // private
1522        unregister : function(menu){
1523            delete menus[menu.id];
1524            menu.un("beforehide", onBeforeHide);
1525            menu.un("hide", onHide);
1526            menu.un("beforeshow", onBeforeShow);
1527            menu.un("show", onShow);
1528            var g = menu.group;
1529            if(g && menu.events["checkchange"]){
1530                groups[g].remove(menu);
1531                menu.un("checkchange", onCheck);
1532            }
1533        },
1534
1535        // private
1536        registerCheckable : function(menuItem){
1537            var g = menuItem.group;
1538            if(g){
1539                if(!groups[g]){
1540                    groups[g] = [];
1541                }
1542                groups[g].push(menuItem);
1543                menuItem.on("beforecheckchange", onBeforeCheck);
1544            }
1545        },
1546
1547        // private
1548        unregisterCheckable : function(menuItem){
1549            var g = menuItem.group;
1550            if(g){
1551                groups[g].remove(menuItem);
1552                menuItem.un("beforecheckchange", onBeforeCheck);
1553            }
1554        }
1555    };
1556 }();/*
1557  * - LGPL
1558  *
1559  * menu
1560  * 
1561  */
1562
1563 /**
1564  * @class Roo.bootstrap.Menu
1565  * @extends Roo.bootstrap.Component
1566  * Bootstrap Menu class - container for MenuItems
1567  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1568  * 
1569  * @constructor
1570  * Create a new Menu
1571  * @param {Object} config The config object
1572  */
1573
1574
1575 Roo.bootstrap.Menu = function(config){
1576     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1577     if (this.registerMenu) {
1578         Roo.bootstrap.MenuMgr.register(this);
1579     }
1580     this.addEvents({
1581         /**
1582          * @event beforeshow
1583          * Fires before this menu is displayed
1584          * @param {Roo.menu.Menu} this
1585          */
1586         beforeshow : true,
1587         /**
1588          * @event beforehide
1589          * Fires before this menu is hidden
1590          * @param {Roo.menu.Menu} this
1591          */
1592         beforehide : true,
1593         /**
1594          * @event show
1595          * Fires after this menu is displayed
1596          * @param {Roo.menu.Menu} this
1597          */
1598         show : true,
1599         /**
1600          * @event hide
1601          * Fires after this menu is hidden
1602          * @param {Roo.menu.Menu} this
1603          */
1604         hide : true,
1605         /**
1606          * @event click
1607          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1608          * @param {Roo.menu.Menu} this
1609          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1610          * @param {Roo.EventObject} e
1611          */
1612         click : true,
1613         /**
1614          * @event mouseover
1615          * Fires when the mouse is hovering over this menu
1616          * @param {Roo.menu.Menu} this
1617          * @param {Roo.EventObject} e
1618          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1619          */
1620         mouseover : true,
1621         /**
1622          * @event mouseout
1623          * Fires when the mouse exits this menu
1624          * @param {Roo.menu.Menu} this
1625          * @param {Roo.EventObject} e
1626          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1627          */
1628         mouseout : true,
1629         /**
1630          * @event itemclick
1631          * Fires when a menu item contained in this menu is clicked
1632          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1633          * @param {Roo.EventObject} e
1634          */
1635         itemclick: true
1636     });
1637     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1638 };
1639
1640 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1641     
1642    /// html : false,
1643     //align : '',
1644     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1645     type: false,
1646     /**
1647      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1648      */
1649     registerMenu : true,
1650     
1651     menuItems :false, // stores the menu items..
1652     
1653     hidden:true,
1654     
1655     parentMenu : false,
1656     
1657     getChildContainer : function() {
1658         return this.el;  
1659     },
1660     
1661     getAutoCreate : function(){
1662          
1663         //if (['right'].indexOf(this.align)!==-1) {
1664         //    cfg.cn[1].cls += ' pull-right'
1665         //}
1666         
1667         
1668         var cfg = {
1669             tag : 'ul',
1670             cls : 'dropdown-menu' ,
1671             style : 'z-index:1000'
1672             
1673         }
1674         
1675         if (this.type === 'submenu') {
1676             cfg.cls = 'submenu active';
1677         }
1678         if (this.type === 'treeview') {
1679             cfg.cls = 'treeview-menu';
1680         }
1681         
1682         return cfg;
1683     },
1684     initEvents : function() {
1685         
1686        // Roo.log("ADD event");
1687        // Roo.log(this.triggerEl.dom);
1688         this.triggerEl.on('click', this.onTriggerPress, this);
1689         this.triggerEl.addClass('dropdown-toggle');
1690         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1691
1692         this.el.on("mouseover", this.onMouseOver, this);
1693         this.el.on("mouseout", this.onMouseOut, this);
1694         
1695         
1696     },
1697     findTargetItem : function(e){
1698         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1699         if(!t){
1700             return false;
1701         }
1702         //Roo.log(t);         Roo.log(t.id);
1703         if(t && t.id){
1704             //Roo.log(this.menuitems);
1705             return this.menuitems.get(t.id);
1706             
1707             //return this.items.get(t.menuItemId);
1708         }
1709         
1710         return false;
1711     },
1712     onClick : function(e){
1713         Roo.log("menu.onClick");
1714         var t = this.findTargetItem(e);
1715         if(!t || t.isContainer){
1716             return;
1717         }
1718         Roo.log(e);
1719         /*
1720         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1721             if(t == this.activeItem && t.shouldDeactivate(e)){
1722                 this.activeItem.deactivate();
1723                 delete this.activeItem;
1724                 return;
1725             }
1726             if(t.canActivate){
1727                 this.setActiveItem(t, true);
1728             }
1729             return;
1730             
1731             
1732         }
1733         */
1734        
1735         Roo.log('pass click event');
1736         
1737         t.onClick(e);
1738         
1739         this.fireEvent("click", this, t, e);
1740         
1741         this.hide();
1742     },
1743      onMouseOver : function(e){
1744         var t  = this.findTargetItem(e);
1745         //Roo.log(t);
1746         //if(t){
1747         //    if(t.canActivate && !t.disabled){
1748         //        this.setActiveItem(t, true);
1749         //    }
1750         //}
1751         
1752         this.fireEvent("mouseover", this, e, t);
1753     },
1754     isVisible : function(){
1755         return !this.hidden;
1756     },
1757      onMouseOut : function(e){
1758         var t  = this.findTargetItem(e);
1759         
1760         //if(t ){
1761         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1762         //        this.activeItem.deactivate();
1763         //        delete this.activeItem;
1764         //    }
1765         //}
1766         this.fireEvent("mouseout", this, e, t);
1767     },
1768     
1769     
1770     /**
1771      * Displays this menu relative to another element
1772      * @param {String/HTMLElement/Roo.Element} element The element to align to
1773      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1774      * the element (defaults to this.defaultAlign)
1775      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1776      */
1777     show : function(el, pos, parentMenu){
1778         this.parentMenu = parentMenu;
1779         if(!this.el){
1780             this.render();
1781         }
1782         this.fireEvent("beforeshow", this);
1783         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1784     },
1785      /**
1786      * Displays this menu at a specific xy position
1787      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1788      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1789      */
1790     showAt : function(xy, parentMenu, /* private: */_e){
1791         this.parentMenu = parentMenu;
1792         if(!this.el){
1793             this.render();
1794         }
1795         if(_e !== false){
1796             this.fireEvent("beforeshow", this);
1797             
1798             //xy = this.el.adjustForConstraints(xy);
1799         }
1800         //this.el.setXY(xy);
1801         //this.el.show();
1802         this.hideMenuItems();
1803         this.hidden = false;
1804         this.triggerEl.addClass('open');
1805         this.focus();
1806         this.fireEvent("show", this);
1807     },
1808     
1809     focus : function(){
1810         return;
1811         if(!this.hidden){
1812             this.doFocus.defer(50, this);
1813         }
1814     },
1815
1816     doFocus : function(){
1817         if(!this.hidden){
1818             this.focusEl.focus();
1819         }
1820     },
1821
1822     /**
1823      * Hides this menu and optionally all parent menus
1824      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1825      */
1826     hide : function(deep){
1827         
1828         this.hideMenuItems();
1829         if(this.el && this.isVisible()){
1830             this.fireEvent("beforehide", this);
1831             if(this.activeItem){
1832                 this.activeItem.deactivate();
1833                 this.activeItem = null;
1834             }
1835             this.triggerEl.removeClass('open');;
1836             this.hidden = true;
1837             this.fireEvent("hide", this);
1838         }
1839         if(deep === true && this.parentMenu){
1840             this.parentMenu.hide(true);
1841         }
1842     },
1843     
1844     onTriggerPress  : function(e)
1845     {
1846         
1847         Roo.log('trigger press');
1848         //Roo.log(e.getTarget());
1849        // Roo.log(this.triggerEl.dom);
1850         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1851             return;
1852         }
1853         if (this.isVisible()) {
1854             Roo.log('hide');
1855             this.hide();
1856         } else {
1857             this.show(this.triggerEl, false, false);
1858         }
1859         
1860         
1861     },
1862     
1863          
1864        
1865     
1866     hideMenuItems : function()
1867     {
1868         //$(backdrop).remove()
1869         Roo.select('.open',true).each(function(aa) {
1870             
1871             aa.removeClass('open');
1872           //var parent = getParent($(this))
1873           //var relatedTarget = { relatedTarget: this }
1874           
1875            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1876           //if (e.isDefaultPrevented()) return
1877            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1878         })
1879     },
1880     addxtypeChild : function (tree, cntr) {
1881         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1882           
1883         this.menuitems.add(comp);
1884         return comp;
1885
1886     },
1887     getEl : function()
1888     {
1889         Roo.log(this.el);
1890         return this.el;
1891     }
1892 });
1893
1894  
1895  /*
1896  * - LGPL
1897  *
1898  * menu item
1899  * 
1900  */
1901
1902
1903 /**
1904  * @class Roo.bootstrap.MenuItem
1905  * @extends Roo.bootstrap.Component
1906  * Bootstrap MenuItem class
1907  * @cfg {String} html the menu label
1908  * @cfg {String} href the link
1909  * @cfg {Boolean} preventDefault (true | false) default true
1910  * @cfg {Boolean} isContainer (true | false) default false
1911  * 
1912  * 
1913  * @constructor
1914  * Create a new MenuItem
1915  * @param {Object} config The config object
1916  */
1917
1918
1919 Roo.bootstrap.MenuItem = function(config){
1920     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1921     this.addEvents({
1922         // raw events
1923         /**
1924          * @event click
1925          * The raw click event for the entire grid.
1926          * @param {Roo.EventObject} e
1927          */
1928         "click" : true
1929     });
1930 };
1931
1932 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1933     
1934     href : false,
1935     html : false,
1936     preventDefault: true,
1937     isContainer : false,
1938     
1939     getAutoCreate : function(){
1940         
1941         if(this.isContainer){
1942             return {
1943                 tag: 'li',
1944                 cls: 'dropdown-menu-item'
1945             };
1946         }
1947         
1948         var cfg= {
1949             tag: 'li',
1950             cls: 'dropdown-menu-item',
1951             cn: [
1952                     {
1953                         tag : 'a',
1954                         href : '#',
1955                         html : 'Link'
1956                     }
1957                 ]
1958         };
1959         if (this.parent().type == 'treeview') {
1960             cfg.cls = 'treeview-menu';
1961         }
1962         
1963         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1964         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1965         return cfg;
1966     },
1967     
1968     initEvents: function() {
1969         
1970         //this.el.select('a').on('click', this.onClick, this);
1971         
1972     },
1973     onClick : function(e)
1974     {
1975         Roo.log('item on click ');
1976         //if(this.preventDefault){
1977         //    e.preventDefault();
1978         //}
1979         //this.parent().hideMenuItems();
1980         
1981         this.fireEvent('click', this, e);
1982     },
1983     getEl : function()
1984     {
1985         return this.el;
1986     }
1987 });
1988
1989  
1990
1991  /*
1992  * - LGPL
1993  *
1994  * menu separator
1995  * 
1996  */
1997
1998
1999 /**
2000  * @class Roo.bootstrap.MenuSeparator
2001  * @extends Roo.bootstrap.Component
2002  * Bootstrap MenuSeparator class
2003  * 
2004  * @constructor
2005  * Create a new MenuItem
2006  * @param {Object} config The config object
2007  */
2008
2009
2010 Roo.bootstrap.MenuSeparator = function(config){
2011     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2012 };
2013
2014 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2015     
2016     getAutoCreate : function(){
2017         var cfg = {
2018             cls: 'divider',
2019             tag : 'li'
2020         };
2021         
2022         return cfg;
2023     }
2024    
2025 });
2026
2027  
2028
2029  
2030 /*
2031 <div class="modal fade">
2032   <div class="modal-dialog">
2033     <div class="modal-content">
2034       <div class="modal-header">
2035         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2036         <h4 class="modal-title">Modal title</h4>
2037       </div>
2038       <div class="modal-body">
2039         <p>One fine body&hellip;</p>
2040       </div>
2041       <div class="modal-footer">
2042         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2043         <button type="button" class="btn btn-primary">Save changes</button>
2044       </div>
2045     </div><!-- /.modal-content -->
2046   </div><!-- /.modal-dialog -->
2047 </div><!-- /.modal -->
2048 */
2049 /*
2050  * - LGPL
2051  *
2052  * page contgainer.
2053  * 
2054  */
2055
2056 /**
2057  * @class Roo.bootstrap.Modal
2058  * @extends Roo.bootstrap.Component
2059  * Bootstrap Modal class
2060  * @cfg {String} title Title of dialog
2061  * @cfg {Boolean} specificTitle (true|false) default false
2062  * @cfg {Array} buttons Array of buttons or standard button set..
2063  * @cfg {String} buttonPosition (left|right|center) default right
2064  * @cfg {Boolean} animate (true | false) default true
2065  * 
2066  * @constructor
2067  * Create a new Modal Dialog
2068  * @param {Object} config The config object
2069  */
2070
2071 Roo.bootstrap.Modal = function(config){
2072     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2073     this.addEvents({
2074         // raw events
2075         /**
2076          * @event btnclick
2077          * The raw btnclick event for the button
2078          * @param {Roo.EventObject} e
2079          */
2080         "btnclick" : true
2081     });
2082     this.buttons = this.buttons || [];
2083 };
2084
2085 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2086     
2087     title : 'test dialog',
2088    
2089     buttons : false,
2090     
2091     // set on load...
2092     body:  false,
2093     
2094     specificTitle: false,
2095     
2096     buttonPosition: 'right',
2097     
2098     animate : true,
2099     
2100     onRender : function(ct, position)
2101     {
2102         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2103      
2104         if(!this.el){
2105             var cfg = Roo.apply({},  this.getAutoCreate());
2106             cfg.id = Roo.id();
2107             //if(!cfg.name){
2108             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2109             //}
2110             //if (!cfg.name.length) {
2111             //    delete cfg.name;
2112            // }
2113             if (this.cls) {
2114                 cfg.cls += ' ' + this.cls;
2115             }
2116             if (this.style) {
2117                 cfg.style = this.style;
2118             }
2119             this.el = Roo.get(document.body).createChild(cfg, position);
2120         }
2121         //var type = this.el.dom.type;
2122         
2123         if(this.tabIndex !== undefined){
2124             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2125         }
2126         
2127         
2128         
2129         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2130         this.maskEl.enableDisplayMode("block");
2131         this.maskEl.hide();
2132         //this.el.addClass("x-dlg-modal");
2133     
2134         if (this.buttons.length) {
2135             Roo.each(this.buttons, function(bb) {
2136                 b = Roo.apply({}, bb);
2137                 b.xns = b.xns || Roo.bootstrap;
2138                 b.xtype = b.xtype || 'Button';
2139                 if (typeof(b.listeners) == 'undefined') {
2140                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2141                 }
2142                 
2143                 var btn = Roo.factory(b);
2144                 
2145                 btn.onRender(this.el.select('.modal-footer div').first());
2146                 
2147             },this);
2148         }
2149         // render the children.
2150         var nitems = [];
2151         
2152         if(typeof(this.items) != 'undefined'){
2153             var items = this.items;
2154             delete this.items;
2155
2156             for(var i =0;i < items.length;i++) {
2157                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2158             }
2159         }
2160         
2161         this.items = nitems;
2162         
2163         this.body = this.el.select('.modal-body',true).first();
2164         this.close = this.el.select('.modal-header .close', true).first();
2165         this.footer = this.el.select('.modal-footer',true).first();
2166         this.initEvents();
2167         //this.el.addClass([this.fieldClass, this.cls]);
2168         
2169     },
2170     getAutoCreate : function(){
2171         
2172         
2173         var bdy = {
2174                 cls : 'modal-body',
2175                 html : this.html || ''
2176         };
2177         
2178         var title = {
2179             tag: 'h4',
2180             cls : 'modal-title',
2181             html : this.title
2182         };
2183         
2184         if(this.specificTitle){
2185             title = this.title;
2186         };
2187         
2188         var modal = {
2189             cls: "modal",
2190             style : 'display: none',
2191             cn : [
2192                 {
2193                     cls: "modal-dialog",
2194                     cn : [
2195                         {
2196                             cls : "modal-content",
2197                             cn : [
2198                                 {
2199                                     cls : 'modal-header',
2200                                     cn : [
2201                                         {
2202                                             tag: 'button',
2203                                             cls : 'close',
2204                                             html : '&times'
2205                                         },
2206                                         title
2207                                     ]
2208                                 },
2209                                 bdy,
2210                                 {
2211                                     cls : 'modal-footer',
2212                                     cn : [
2213                                         {
2214                                             tag: 'div',
2215                                             cls: 'btn-' + this.buttonPosition
2216                                         }
2217                                     ]
2218                                     
2219                                 }
2220                                 
2221                                 
2222                             ]
2223                             
2224                         }
2225                     ]
2226                         
2227                 }
2228             ]
2229         };
2230         
2231         if(this.animate){
2232             modal.cls += ' fade';
2233         }
2234         
2235         return modal;
2236           
2237     },
2238     getChildContainer : function() {
2239          
2240          return this.el.select('.modal-body',true).first();
2241         
2242     },
2243     getButtonContainer : function() {
2244          return this.el.select('.modal-footer div',true).first();
2245         
2246     },
2247     initEvents : function()
2248     {
2249         this.el.select('.modal-header .close').on('click', this.hide, this);
2250 //        
2251 //        this.addxtype(this);
2252     },
2253     show : function() {
2254         
2255         if (!this.rendered) {
2256             this.render();
2257         }
2258         
2259         this.el.setStyle('display', 'block');
2260         
2261         if(this.animate){
2262             var _this = this;
2263             (function(){ _this.el.addClass('in'); }).defer(50);
2264         }else{
2265             this.el.addClass('in');
2266         }
2267         
2268         Roo.get(document.body).addClass("x-body-masked");
2269         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2270         this.maskEl.show();
2271         this.el.setStyle('zIndex', '10001');
2272         this.fireEvent('show', this);
2273         
2274         
2275     },
2276     hide : function()
2277     {
2278         this.maskEl.hide();
2279         Roo.get(document.body).removeClass("x-body-masked");
2280         this.el.removeClass('in');
2281         
2282         if(this.animate){
2283             var _this = this;
2284             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2285         }else{
2286             this.el.setStyle('display', 'none');
2287         }
2288         
2289         this.fireEvent('hide', this);
2290     },
2291     
2292     addButton : function(str, cb)
2293     {
2294          
2295         
2296         var b = Roo.apply({}, { html : str } );
2297         b.xns = b.xns || Roo.bootstrap;
2298         b.xtype = b.xtype || 'Button';
2299         if (typeof(b.listeners) == 'undefined') {
2300             b.listeners = { click : cb.createDelegate(this)  };
2301         }
2302         
2303         var btn = Roo.factory(b);
2304            
2305         btn.onRender(this.el.select('.modal-footer div').first());
2306         
2307         return btn;   
2308        
2309     },
2310     
2311     setDefaultButton : function(btn)
2312     {
2313         //this.el.select('.modal-footer').()
2314     },
2315     resizeTo: function(w,h)
2316     {
2317         // skip..
2318     },
2319     setContentSize  : function(w, h)
2320     {
2321         
2322     },
2323     onButtonClick: function(btn,e)
2324     {
2325         //Roo.log([a,b,c]);
2326         this.fireEvent('btnclick', btn.name, e);
2327     },
2328     setTitle: function(str) {
2329         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2330         
2331     }
2332 });
2333
2334
2335 Roo.apply(Roo.bootstrap.Modal,  {
2336     /**
2337          * Button config that displays a single OK button
2338          * @type Object
2339          */
2340         OK :  [{
2341             name : 'ok',
2342             weight : 'primary',
2343             html : 'OK'
2344         }], 
2345         /**
2346          * Button config that displays Yes and No buttons
2347          * @type Object
2348          */
2349         YESNO : [
2350             {
2351                 name  : 'no',
2352                 html : 'No'
2353             },
2354             {
2355                 name  :'yes',
2356                 weight : 'primary',
2357                 html : 'Yes'
2358             }
2359         ],
2360         
2361         /**
2362          * Button config that displays OK and Cancel buttons
2363          * @type Object
2364          */
2365         OKCANCEL : [
2366             {
2367                name : 'cancel',
2368                 html : 'Cancel'
2369             },
2370             {
2371                 name : 'ok',
2372                 weight : 'primary',
2373                 html : 'OK'
2374             }
2375         ],
2376         /**
2377          * Button config that displays Yes, No and Cancel buttons
2378          * @type Object
2379          */
2380         YESNOCANCEL : [
2381             {
2382                 name : 'yes',
2383                 weight : 'primary',
2384                 html : 'Yes'
2385             },
2386             {
2387                 name : 'no',
2388                 html : 'No'
2389             },
2390             {
2391                 name : 'cancel',
2392                 html : 'Cancel'
2393             }
2394         ]
2395 });
2396  /*
2397  * - LGPL
2398  *
2399  * messagebox - can be used as a replace
2400  * 
2401  */
2402 /**
2403  * @class Roo.MessageBox
2404  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2405  * Example usage:
2406  *<pre><code>
2407 // Basic alert:
2408 Roo.Msg.alert('Status', 'Changes saved successfully.');
2409
2410 // Prompt for user data:
2411 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2412     if (btn == 'ok'){
2413         // process text value...
2414     }
2415 });
2416
2417 // Show a dialog using config options:
2418 Roo.Msg.show({
2419    title:'Save Changes?',
2420    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2421    buttons: Roo.Msg.YESNOCANCEL,
2422    fn: processResult,
2423    animEl: 'elId'
2424 });
2425 </code></pre>
2426  * @singleton
2427  */
2428 Roo.bootstrap.MessageBox = function(){
2429     var dlg, opt, mask, waitTimer;
2430     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2431     var buttons, activeTextEl, bwidth;
2432
2433     
2434     // private
2435     var handleButton = function(button){
2436         dlg.hide();
2437         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2438     };
2439
2440     // private
2441     var handleHide = function(){
2442         if(opt && opt.cls){
2443             dlg.el.removeClass(opt.cls);
2444         }
2445         //if(waitTimer){
2446         //    Roo.TaskMgr.stop(waitTimer);
2447         //    waitTimer = null;
2448         //}
2449     };
2450
2451     // private
2452     var updateButtons = function(b){
2453         var width = 0;
2454         if(!b){
2455             buttons["ok"].hide();
2456             buttons["cancel"].hide();
2457             buttons["yes"].hide();
2458             buttons["no"].hide();
2459             //dlg.footer.dom.style.display = 'none';
2460             return width;
2461         }
2462         dlg.footer.dom.style.display = '';
2463         for(var k in buttons){
2464             if(typeof buttons[k] != "function"){
2465                 if(b[k]){
2466                     buttons[k].show();
2467                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2468                     width += buttons[k].el.getWidth()+15;
2469                 }else{
2470                     buttons[k].hide();
2471                 }
2472             }
2473         }
2474         return width;
2475     };
2476
2477     // private
2478     var handleEsc = function(d, k, e){
2479         if(opt && opt.closable !== false){
2480             dlg.hide();
2481         }
2482         if(e){
2483             e.stopEvent();
2484         }
2485     };
2486
2487     return {
2488         /**
2489          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2490          * @return {Roo.BasicDialog} The BasicDialog element
2491          */
2492         getDialog : function(){
2493            if(!dlg){
2494                 dlg = new Roo.bootstrap.Modal( {
2495                     //draggable: true,
2496                     //resizable:false,
2497                     //constraintoviewport:false,
2498                     //fixedcenter:true,
2499                     //collapsible : false,
2500                     //shim:true,
2501                     //modal: true,
2502                   //  width:400,
2503                   //  height:100,
2504                     //buttonAlign:"center",
2505                     closeClick : function(){
2506                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2507                             handleButton("no");
2508                         }else{
2509                             handleButton("cancel");
2510                         }
2511                     }
2512                 });
2513                 dlg.render();
2514                 dlg.on("hide", handleHide);
2515                 mask = dlg.mask;
2516                 //dlg.addKeyListener(27, handleEsc);
2517                 buttons = {};
2518                 this.buttons = buttons;
2519                 var bt = this.buttonText;
2520                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2521                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2522                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2523                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2524                 Roo.log(buttons)
2525                 bodyEl = dlg.body.createChild({
2526
2527                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2528                         '<textarea class="roo-mb-textarea"></textarea>' +
2529                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2530                 });
2531                 msgEl = bodyEl.dom.firstChild;
2532                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2533                 textboxEl.enableDisplayMode();
2534                 textboxEl.addKeyListener([10,13], function(){
2535                     if(dlg.isVisible() && opt && opt.buttons){
2536                         if(opt.buttons.ok){
2537                             handleButton("ok");
2538                         }else if(opt.buttons.yes){
2539                             handleButton("yes");
2540                         }
2541                     }
2542                 });
2543                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2544                 textareaEl.enableDisplayMode();
2545                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2546                 progressEl.enableDisplayMode();
2547                 var pf = progressEl.dom.firstChild;
2548                 if (pf) {
2549                     pp = Roo.get(pf.firstChild);
2550                     pp.setHeight(pf.offsetHeight);
2551                 }
2552                 
2553             }
2554             return dlg;
2555         },
2556
2557         /**
2558          * Updates the message box body text
2559          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2560          * the XHTML-compliant non-breaking space character '&amp;#160;')
2561          * @return {Roo.MessageBox} This message box
2562          */
2563         updateText : function(text){
2564             if(!dlg.isVisible() && !opt.width){
2565                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2566             }
2567             msgEl.innerHTML = text || '&#160;';
2568       
2569             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2570             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2571             var w = Math.max(
2572                     Math.min(opt.width || cw , this.maxWidth), 
2573                     Math.max(opt.minWidth || this.minWidth, bwidth)
2574             );
2575             if(opt.prompt){
2576                 activeTextEl.setWidth(w);
2577             }
2578             if(dlg.isVisible()){
2579                 dlg.fixedcenter = false;
2580             }
2581             // to big, make it scroll. = But as usual stupid IE does not support
2582             // !important..
2583             
2584             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2585                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2586                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2587             } else {
2588                 bodyEl.dom.style.height = '';
2589                 bodyEl.dom.style.overflowY = '';
2590             }
2591             if (cw > w) {
2592                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2593             } else {
2594                 bodyEl.dom.style.overflowX = '';
2595             }
2596             
2597             dlg.setContentSize(w, bodyEl.getHeight());
2598             if(dlg.isVisible()){
2599                 dlg.fixedcenter = true;
2600             }
2601             return this;
2602         },
2603
2604         /**
2605          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2606          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2607          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2608          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2609          * @return {Roo.MessageBox} This message box
2610          */
2611         updateProgress : function(value, text){
2612             if(text){
2613                 this.updateText(text);
2614             }
2615             if (pp) { // weird bug on my firefox - for some reason this is not defined
2616                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2617             }
2618             return this;
2619         },        
2620
2621         /**
2622          * Returns true if the message box is currently displayed
2623          * @return {Boolean} True if the message box is visible, else false
2624          */
2625         isVisible : function(){
2626             return dlg && dlg.isVisible();  
2627         },
2628
2629         /**
2630          * Hides the message box if it is displayed
2631          */
2632         hide : function(){
2633             if(this.isVisible()){
2634                 dlg.hide();
2635             }  
2636         },
2637
2638         /**
2639          * Displays a new message box, or reinitializes an existing message box, based on the config options
2640          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2641          * The following config object properties are supported:
2642          * <pre>
2643 Property    Type             Description
2644 ----------  ---------------  ------------------------------------------------------------------------------------
2645 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2646                                    closes (defaults to undefined)
2647 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2648                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2649 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2650                                    progress and wait dialogs will ignore this property and always hide the
2651                                    close button as they can only be closed programmatically.
2652 cls               String           A custom CSS class to apply to the message box element
2653 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2654                                    displayed (defaults to 75)
2655 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2656                                    function will be btn (the name of the button that was clicked, if applicable,
2657                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2658                                    Progress and wait dialogs will ignore this option since they do not respond to
2659                                    user actions and can only be closed programmatically, so any required function
2660                                    should be called by the same code after it closes the dialog.
2661 icon              String           A CSS class that provides a background image to be used as an icon for
2662                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2663 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2664 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2665 modal             Boolean          False to allow user interaction with the page while the message box is
2666                                    displayed (defaults to true)
2667 msg               String           A string that will replace the existing message box body text (defaults
2668                                    to the XHTML-compliant non-breaking space character '&#160;')
2669 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2670 progress          Boolean          True to display a progress bar (defaults to false)
2671 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2672 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2673 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2674 title             String           The title text
2675 value             String           The string value to set into the active textbox element if displayed
2676 wait              Boolean          True to display a progress bar (defaults to false)
2677 width             Number           The width of the dialog in pixels
2678 </pre>
2679          *
2680          * Example usage:
2681          * <pre><code>
2682 Roo.Msg.show({
2683    title: 'Address',
2684    msg: 'Please enter your address:',
2685    width: 300,
2686    buttons: Roo.MessageBox.OKCANCEL,
2687    multiline: true,
2688    fn: saveAddress,
2689    animEl: 'addAddressBtn'
2690 });
2691 </code></pre>
2692          * @param {Object} config Configuration options
2693          * @return {Roo.MessageBox} This message box
2694          */
2695         show : function(options)
2696         {
2697             
2698             // this causes nightmares if you show one dialog after another
2699             // especially on callbacks..
2700              
2701             if(this.isVisible()){
2702                 
2703                 this.hide();
2704                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2705                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2706                 Roo.log("New Dialog Message:" +  options.msg )
2707                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2708                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2709                 
2710             }
2711             var d = this.getDialog();
2712             opt = options;
2713             d.setTitle(opt.title || "&#160;");
2714             d.close.setDisplayed(opt.closable !== false);
2715             activeTextEl = textboxEl;
2716             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2717             if(opt.prompt){
2718                 if(opt.multiline){
2719                     textboxEl.hide();
2720                     textareaEl.show();
2721                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2722                         opt.multiline : this.defaultTextHeight);
2723                     activeTextEl = textareaEl;
2724                 }else{
2725                     textboxEl.show();
2726                     textareaEl.hide();
2727                 }
2728             }else{
2729                 textboxEl.hide();
2730                 textareaEl.hide();
2731             }
2732             progressEl.setDisplayed(opt.progress === true);
2733             this.updateProgress(0);
2734             activeTextEl.dom.value = opt.value || "";
2735             if(opt.prompt){
2736                 dlg.setDefaultButton(activeTextEl);
2737             }else{
2738                 var bs = opt.buttons;
2739                 var db = null;
2740                 if(bs && bs.ok){
2741                     db = buttons["ok"];
2742                 }else if(bs && bs.yes){
2743                     db = buttons["yes"];
2744                 }
2745                 dlg.setDefaultButton(db);
2746             }
2747             bwidth = updateButtons(opt.buttons);
2748             this.updateText(opt.msg);
2749             if(opt.cls){
2750                 d.el.addClass(opt.cls);
2751             }
2752             d.proxyDrag = opt.proxyDrag === true;
2753             d.modal = opt.modal !== false;
2754             d.mask = opt.modal !== false ? mask : false;
2755             if(!d.isVisible()){
2756                 // force it to the end of the z-index stack so it gets a cursor in FF
2757                 document.body.appendChild(dlg.el.dom);
2758                 d.animateTarget = null;
2759                 d.show(options.animEl);
2760             }
2761             return this;
2762         },
2763
2764         /**
2765          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2766          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2767          * and closing the message box when the process is complete.
2768          * @param {String} title The title bar text
2769          * @param {String} msg The message box body text
2770          * @return {Roo.MessageBox} This message box
2771          */
2772         progress : function(title, msg){
2773             this.show({
2774                 title : title,
2775                 msg : msg,
2776                 buttons: false,
2777                 progress:true,
2778                 closable:false,
2779                 minWidth: this.minProgressWidth,
2780                 modal : true
2781             });
2782             return this;
2783         },
2784
2785         /**
2786          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2787          * If a callback function is passed it will be called after the user clicks the button, and the
2788          * id of the button that was clicked will be passed as the only parameter to the callback
2789          * (could also be the top-right close button).
2790          * @param {String} title The title bar text
2791          * @param {String} msg The message box body text
2792          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2793          * @param {Object} scope (optional) The scope of the callback function
2794          * @return {Roo.MessageBox} This message box
2795          */
2796         alert : function(title, msg, fn, scope){
2797             this.show({
2798                 title : title,
2799                 msg : msg,
2800                 buttons: this.OK,
2801                 fn: fn,
2802                 scope : scope,
2803                 modal : true
2804             });
2805             return this;
2806         },
2807
2808         /**
2809          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2810          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2811          * You are responsible for closing the message box when the process is complete.
2812          * @param {String} msg The message box body text
2813          * @param {String} title (optional) The title bar text
2814          * @return {Roo.MessageBox} This message box
2815          */
2816         wait : function(msg, title){
2817             this.show({
2818                 title : title,
2819                 msg : msg,
2820                 buttons: false,
2821                 closable:false,
2822                 progress:true,
2823                 modal:true,
2824                 width:300,
2825                 wait:true
2826             });
2827             waitTimer = Roo.TaskMgr.start({
2828                 run: function(i){
2829                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2830                 },
2831                 interval: 1000
2832             });
2833             return this;
2834         },
2835
2836         /**
2837          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2838          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2839          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2840          * @param {String} title The title bar text
2841          * @param {String} msg The message box body text
2842          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2843          * @param {Object} scope (optional) The scope of the callback function
2844          * @return {Roo.MessageBox} This message box
2845          */
2846         confirm : function(title, msg, fn, scope){
2847             this.show({
2848                 title : title,
2849                 msg : msg,
2850                 buttons: this.YESNO,
2851                 fn: fn,
2852                 scope : scope,
2853                 modal : true
2854             });
2855             return this;
2856         },
2857
2858         /**
2859          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2860          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2861          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2862          * (could also be the top-right close button) and the text that was entered will be passed as the two
2863          * parameters to the callback.
2864          * @param {String} title The title bar text
2865          * @param {String} msg The message box body text
2866          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2867          * @param {Object} scope (optional) The scope of the callback function
2868          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2869          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2870          * @return {Roo.MessageBox} This message box
2871          */
2872         prompt : function(title, msg, fn, scope, multiline){
2873             this.show({
2874                 title : title,
2875                 msg : msg,
2876                 buttons: this.OKCANCEL,
2877                 fn: fn,
2878                 minWidth:250,
2879                 scope : scope,
2880                 prompt:true,
2881                 multiline: multiline,
2882                 modal : true
2883             });
2884             return this;
2885         },
2886
2887         /**
2888          * Button config that displays a single OK button
2889          * @type Object
2890          */
2891         OK : {ok:true},
2892         /**
2893          * Button config that displays Yes and No buttons
2894          * @type Object
2895          */
2896         YESNO : {yes:true, no:true},
2897         /**
2898          * Button config that displays OK and Cancel buttons
2899          * @type Object
2900          */
2901         OKCANCEL : {ok:true, cancel:true},
2902         /**
2903          * Button config that displays Yes, No and Cancel buttons
2904          * @type Object
2905          */
2906         YESNOCANCEL : {yes:true, no:true, cancel:true},
2907
2908         /**
2909          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2910          * @type Number
2911          */
2912         defaultTextHeight : 75,
2913         /**
2914          * The maximum width in pixels of the message box (defaults to 600)
2915          * @type Number
2916          */
2917         maxWidth : 600,
2918         /**
2919          * The minimum width in pixels of the message box (defaults to 100)
2920          * @type Number
2921          */
2922         minWidth : 100,
2923         /**
2924          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2925          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2926          * @type Number
2927          */
2928         minProgressWidth : 250,
2929         /**
2930          * An object containing the default button text strings that can be overriden for localized language support.
2931          * Supported properties are: ok, cancel, yes and no.
2932          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2933          * @type Object
2934          */
2935         buttonText : {
2936             ok : "OK",
2937             cancel : "Cancel",
2938             yes : "Yes",
2939             no : "No"
2940         }
2941     };
2942 }();
2943
2944 /**
2945  * Shorthand for {@link Roo.MessageBox}
2946  */
2947 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2948 Roo.Msg = Roo.Msg || Roo.MessageBox;
2949 /*
2950  * - LGPL
2951  *
2952  * navbar
2953  * 
2954  */
2955
2956 /**
2957  * @class Roo.bootstrap.Navbar
2958  * @extends Roo.bootstrap.Component
2959  * Bootstrap Navbar class
2960
2961  * @constructor
2962  * Create a new Navbar
2963  * @param {Object} config The config object
2964  */
2965
2966
2967 Roo.bootstrap.Navbar = function(config){
2968     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2969     
2970 };
2971
2972 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2973     
2974     
2975    
2976     // private
2977     navItems : false,
2978     loadMask : false,
2979     
2980     
2981     getAutoCreate : function(){
2982         
2983         
2984         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2985         
2986     },
2987     
2988     initEvents :function ()
2989     {
2990         //Roo.log(this.el.select('.navbar-toggle',true));
2991         this.el.select('.navbar-toggle',true).on('click', function() {
2992            // Roo.log('click');
2993             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2994         }, this);
2995         
2996         var mark = {
2997             tag: "div",
2998             cls:"x-dlg-mask"
2999         }
3000         
3001         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3002         
3003         var size = this.el.getSize();
3004         this.maskEl.setSize(size.width, size.height);
3005         this.maskEl.enableDisplayMode("block");
3006         this.maskEl.hide();
3007         
3008         if(this.loadMask){
3009             this.maskEl.show();
3010         }
3011     },
3012     
3013     
3014     getChildContainer : function()
3015     {
3016         if (this.el.select('.collapse').getCount()) {
3017             return this.el.select('.collapse',true).first();
3018         }
3019         
3020         return this.el;
3021     },
3022     
3023     mask : function()
3024     {
3025         this.maskEl.show();
3026     },
3027     
3028     unmask : function()
3029     {
3030         this.maskEl.hide();
3031     } 
3032     
3033     
3034     
3035     
3036 });
3037
3038
3039
3040  
3041
3042  /*
3043  * - LGPL
3044  *
3045  * navbar
3046  * 
3047  */
3048
3049 /**
3050  * @class Roo.bootstrap.NavSimplebar
3051  * @extends Roo.bootstrap.Navbar
3052  * Bootstrap Sidebar class
3053  *
3054  * @cfg {Boolean} inverse is inverted color
3055  * 
3056  * @cfg {String} type (nav | pills | tabs)
3057  * @cfg {Boolean} arrangement stacked | justified
3058  * @cfg {String} align (left | right) alignment
3059  * 
3060  * @cfg {Boolean} main (true|false) main nav bar? default false
3061  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3062  * 
3063  * @cfg {String} tag (header|footer|nav|div) default is nav 
3064
3065  * 
3066  * 
3067  * 
3068  * @constructor
3069  * Create a new Sidebar
3070  * @param {Object} config The config object
3071  */
3072
3073
3074 Roo.bootstrap.NavSimplebar = function(config){
3075     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3076 };
3077
3078 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3079     
3080     inverse: false,
3081     
3082     type: false,
3083     arrangement: '',
3084     align : false,
3085     
3086     
3087     
3088     main : false,
3089     
3090     
3091     tag : false,
3092     
3093     
3094     getAutoCreate : function(){
3095         
3096         
3097         var cfg = {
3098             tag : this.tag || 'div',
3099             cls : 'navbar'
3100         };
3101           
3102         
3103         cfg.cn = [
3104             {
3105                 cls: 'nav',
3106                 tag : 'ul'
3107             }
3108         ];
3109         
3110          
3111         this.type = this.type || 'nav';
3112         if (['tabs','pills'].indexOf(this.type)!==-1) {
3113             cfg.cn[0].cls += ' nav-' + this.type
3114         
3115         
3116         } else {
3117             if (this.type!=='nav') {
3118                 Roo.log('nav type must be nav/tabs/pills')
3119             }
3120             cfg.cn[0].cls += ' navbar-nav'
3121         }
3122         
3123         
3124         
3125         
3126         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3127             cfg.cn[0].cls += ' nav-' + this.arrangement;
3128         }
3129         
3130         
3131         if (this.align === 'right') {
3132             cfg.cn[0].cls += ' navbar-right';
3133         }
3134         
3135         if (this.inverse) {
3136             cfg.cls += ' navbar-inverse';
3137             
3138         }
3139         
3140         
3141         return cfg;
3142     
3143         
3144     }
3145     
3146     
3147     
3148 });
3149
3150
3151
3152  
3153
3154  
3155        /*
3156  * - LGPL
3157  *
3158  * navbar
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavHeaderbar
3164  * @extends Roo.bootstrap.NavSimplebar
3165  * Bootstrap Sidebar class
3166  *
3167  * @cfg {String} brand what is brand
3168  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3169  * @cfg {String} brand_href href of the brand
3170  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3171  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3172  * 
3173  * @constructor
3174  * Create a new Sidebar
3175  * @param {Object} config The config object
3176  */
3177
3178
3179 Roo.bootstrap.NavHeaderbar = function(config){
3180     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3181 };
3182
3183 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3184     
3185     position: '',
3186     brand: '',
3187     brand_href: false,
3188     srButton : true,
3189     autohide : false,
3190     
3191     getAutoCreate : function(){
3192         
3193         var   cfg = {
3194             tag: this.nav || 'nav',
3195             cls: 'navbar',
3196             role: 'navigation',
3197             cn: []
3198         };
3199         
3200         if(this.srButton){
3201             cfg.cn.push({
3202                 tag: 'div',
3203                 cls: 'navbar-header',
3204                 cn: [
3205                     {
3206                         tag: 'button',
3207                         type: 'button',
3208                         cls: 'navbar-toggle',
3209                         'data-toggle': 'collapse',
3210                         cn: [
3211                             {
3212                                 tag: 'span',
3213                                 cls: 'sr-only',
3214                                 html: 'Toggle navigation'
3215                             },
3216                             {
3217                                 tag: 'span',
3218                                 cls: 'icon-bar'
3219                             },
3220                             {
3221                                 tag: 'span',
3222                                 cls: 'icon-bar'
3223                             },
3224                             {
3225                                 tag: 'span',
3226                                 cls: 'icon-bar'
3227                             }
3228                         ]
3229                     }
3230                 ]
3231             });
3232         }
3233         
3234         cfg.cn.push({
3235             tag: 'div',
3236             cls: 'collapse navbar-collapse',
3237             cn : []
3238         });
3239         
3240         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3241         
3242         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3243             cfg.cls += ' navbar-' + this.position;
3244             
3245             // tag can override this..
3246             
3247             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3248         }
3249         
3250         if (this.brand !== '') {
3251             cfg.cn[0].cn.push({
3252                 tag: 'a',
3253                 href: this.brand_href ? this.brand_href : '#',
3254                 cls: 'navbar-brand',
3255                 cn: [
3256                 this.brand
3257                 ]
3258             });
3259         }
3260         
3261         if(this.main){
3262             cfg.cls += ' main-nav';
3263         }
3264         
3265         
3266         return cfg;
3267
3268         
3269     },
3270     initEvents : function()
3271     {
3272         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3273         
3274         if (this.autohide) {
3275             
3276             var prevScroll = 0;
3277             var ft = this.el;
3278             
3279             Roo.get(document).on('scroll',function(e) {
3280                 var ns = Roo.get(document).getScroll().top;
3281                 var os = prevScroll;
3282                 prevScroll = ns;
3283                 
3284                 if(ns > os){
3285                     ft.removeClass('slideDown');
3286                     ft.addClass('slideUp');
3287                     return;
3288                 }
3289                 ft.removeClass('slideUp');
3290                 ft.addClass('slideDown');
3291                  
3292               
3293           },this);
3294         }
3295     }    
3296           
3297       
3298     
3299     
3300 });
3301
3302
3303
3304  
3305
3306  /*
3307  * - LGPL
3308  *
3309  * navbar
3310  * 
3311  */
3312
3313 /**
3314  * @class Roo.bootstrap.NavSidebar
3315  * @extends Roo.bootstrap.Navbar
3316  * Bootstrap Sidebar class
3317  * 
3318  * @constructor
3319  * Create a new Sidebar
3320  * @param {Object} config The config object
3321  */
3322
3323
3324 Roo.bootstrap.NavSidebar = function(config){
3325     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3326 };
3327
3328 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3329     
3330     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3331     
3332     getAutoCreate : function(){
3333         
3334         
3335         return  {
3336             tag: 'div',
3337             cls: 'sidebar sidebar-nav'
3338         };
3339     
3340         
3341     }
3342     
3343     
3344     
3345 });
3346
3347
3348
3349  
3350
3351  /*
3352  * - LGPL
3353  *
3354  * nav group
3355  * 
3356  */
3357
3358 /**
3359  * @class Roo.bootstrap.NavGroup
3360  * @extends Roo.bootstrap.Component
3361  * Bootstrap NavGroup class
3362  * @cfg {String} align left | right
3363  * @cfg {Boolean} inverse false | true
3364  * @cfg {String} type (nav|pills|tab) default nav
3365  * @cfg {String} navId - reference Id for navbar.
3366
3367  * 
3368  * @constructor
3369  * Create a new nav group
3370  * @param {Object} config The config object
3371  */
3372
3373 Roo.bootstrap.NavGroup = function(config){
3374     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3375     this.navItems = [];
3376    
3377     Roo.bootstrap.NavGroup.register(this);
3378      this.addEvents({
3379         /**
3380              * @event changed
3381              * Fires when the active item changes
3382              * @param {Roo.bootstrap.NavGroup} this
3383              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3384              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3385          */
3386         'changed': true
3387      });
3388     
3389 };
3390
3391 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3392     
3393     align: '',
3394     inverse: false,
3395     form: false,
3396     type: 'nav',
3397     navId : '',
3398     // private
3399     
3400     navItems : false, 
3401     
3402     getAutoCreate : function()
3403     {
3404         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3405         
3406         cfg = {
3407             tag : 'ul',
3408             cls: 'nav' 
3409         }
3410         
3411         if (['tabs','pills'].indexOf(this.type)!==-1) {
3412             cfg.cls += ' nav-' + this.type
3413         } else {
3414             if (this.type!=='nav') {
3415                 Roo.log('nav type must be nav/tabs/pills')
3416             }
3417             cfg.cls += ' navbar-nav'
3418         }
3419         
3420         if (this.parent().sidebar) {
3421             cfg = {
3422                 tag: 'ul',
3423                 cls: 'dashboard-menu sidebar-menu'
3424             }
3425             
3426             return cfg;
3427         }
3428         
3429         if (this.form === true) {
3430             cfg = {
3431                 tag: 'form',
3432                 cls: 'navbar-form'
3433             }
3434             
3435             if (this.align === 'right') {
3436                 cfg.cls += ' navbar-right';
3437             } else {
3438                 cfg.cls += ' navbar-left';
3439             }
3440         }
3441         
3442         if (this.align === 'right') {
3443             cfg.cls += ' navbar-right';
3444         }
3445         
3446         if (this.inverse) {
3447             cfg.cls += ' navbar-inverse';
3448             
3449         }
3450         
3451         
3452         return cfg;
3453     },
3454     /**
3455     * sets the active Navigation item
3456     * @param {Roo.bootstrap.NavItem} the new current navitem
3457     */
3458     setActiveItem : function(item)
3459     {
3460         var prev = false;
3461         Roo.each(this.navItems, function(v){
3462             if (v == item) {
3463                 return ;
3464             }
3465             if (v.isActive()) {
3466                 v.setActive(false, true);
3467                 prev = v;
3468                 
3469             }
3470             
3471         });
3472
3473         item.setActive(true, true);
3474         this.fireEvent('changed', this, item, prev);
3475         
3476         
3477     },
3478     /**
3479     * gets the active Navigation item
3480     * @return {Roo.bootstrap.NavItem} the current navitem
3481     */
3482     getActive : function()
3483     {
3484         
3485         var prev = false;
3486         Roo.each(this.navItems, function(v){
3487             
3488             if (v.isActive()) {
3489                 prev = v;
3490                 
3491             }
3492             
3493         });
3494         return prev;
3495     },
3496     
3497     indexOfNav : function()
3498     {
3499         
3500         var prev = false;
3501         Roo.each(this.navItems, function(v,i){
3502             
3503             if (v.isActive()) {
3504                 prev = i;
3505                 
3506             }
3507             
3508         });
3509         return prev;
3510     },
3511     /**
3512     * adds a Navigation item
3513     * @param {Roo.bootstrap.NavItem} the navitem to add
3514     */
3515     addItem : function(cfg)
3516     {
3517         var cn = new Roo.bootstrap.NavItem(cfg);
3518         this.register(cn);
3519         cn.parentId = this.id;
3520         cn.onRender(this.el, null);
3521         return cn;
3522     },
3523     /**
3524     * register a Navigation item
3525     * @param {Roo.bootstrap.NavItem} the navitem to add
3526     */
3527     register : function(item)
3528     {
3529         this.navItems.push( item);
3530         item.navId = this.navId;
3531     
3532     },
3533     
3534     /**
3535     * clear all the Navigation item
3536     */
3537    
3538     clearAll : function()
3539     {
3540         this.navItems = [];
3541         this.el.dom.innerHTML = '';
3542     },
3543     
3544     getNavItem: function(tabId)
3545     {
3546         var ret = false;
3547         Roo.each(this.navItems, function(e) {
3548             if (e.tabId == tabId) {
3549                ret =  e;
3550                return false;
3551             }
3552             return true;
3553             
3554         });
3555         return ret;
3556     },
3557     
3558     setActiveNext : function()
3559     {
3560         var i = this.indexOfNav(this.getActive());
3561         if (i > this.navItems.length) {
3562             return;
3563         }
3564         this.setActiveItem(this.navItems[i+1]);
3565     },
3566     setActivePrev : function()
3567     {
3568         var i = this.indexOfNav(this.getActive());
3569         if (i  < 1) {
3570             return;
3571         }
3572         this.setActiveItem(this.navItems[i-1]);
3573     },
3574     clearWasActive : function(except) {
3575         Roo.each(this.navItems, function(e) {
3576             if (e.tabId != except.tabId && e.was_active) {
3577                e.was_active = false;
3578                return false;
3579             }
3580             return true;
3581             
3582         });
3583     },
3584     getWasActive : function ()
3585     {
3586         var r = false;
3587         Roo.each(this.navItems, function(e) {
3588             if (e.was_active) {
3589                r = e;
3590                return false;
3591             }
3592             return true;
3593             
3594         });
3595         return r;
3596     }
3597     
3598     
3599 });
3600
3601  
3602 Roo.apply(Roo.bootstrap.NavGroup, {
3603     
3604     groups: {},
3605      /**
3606     * register a Navigation Group
3607     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3608     */
3609     register : function(navgrp)
3610     {
3611         this.groups[navgrp.navId] = navgrp;
3612         
3613     },
3614     /**
3615     * fetch a Navigation Group based on the navigation ID
3616     * @param {string} the navgroup to add
3617     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3618     */
3619     get: function(navId) {
3620         if (typeof(this.groups[navId]) == 'undefined') {
3621             return false;
3622             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3623         }
3624         return this.groups[navId] ;
3625     }
3626     
3627     
3628     
3629 });
3630
3631  /*
3632  * - LGPL
3633  *
3634  * row
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavItem
3640  * @extends Roo.bootstrap.Component
3641  * Bootstrap Navbar.NavItem class
3642  * @cfg {String} href  link to
3643  * @cfg {String} html content of button
3644  * @cfg {String} badge text inside badge
3645  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3646  * @cfg {String} glyphicon name of glyphicon
3647  * @cfg {String} icon name of font awesome icon
3648  * @cfg {Boolean} active Is item active
3649  * @cfg {Boolean} disabled Is item disabled
3650  
3651  * @cfg {Boolean} preventDefault (true | false) default false
3652  * @cfg {String} tabId the tab that this item activates.
3653  * @cfg {String} tagtype (a|span) render as a href or span?
3654   
3655  * @constructor
3656  * Create a new Navbar Item
3657  * @param {Object} config The config object
3658  */
3659 Roo.bootstrap.NavItem = function(config){
3660     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3661     this.addEvents({
3662         // raw events
3663         /**
3664          * @event click
3665          * The raw click event for the entire grid.
3666          * @param {Roo.EventObject} e
3667          */
3668         "click" : true,
3669          /**
3670             * @event changed
3671             * Fires when the active item active state changes
3672             * @param {Roo.bootstrap.NavItem} this
3673             * @param {boolean} state the new state
3674              
3675          */
3676         'changed': true
3677     });
3678    
3679 };
3680
3681 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3682     
3683     href: false,
3684     html: '',
3685     badge: '',
3686     icon: false,
3687     glyphicon: false,
3688     active: false,
3689     preventDefault : false,
3690     tabId : false,
3691     tagtype : 'a',
3692     disabled : false,
3693     
3694     was_active : false,
3695     
3696     getAutoCreate : function(){
3697          
3698         var cfg = {
3699             tag: 'li',
3700             cls: 'nav-item'
3701             
3702         }
3703         if (this.active) {
3704             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3705         }
3706         if (this.disabled) {
3707             cfg.cls += ' disabled';
3708         }
3709         
3710         if (this.href || this.html || this.glyphicon || this.icon) {
3711             cfg.cn = [
3712                 {
3713                     tag: this.tagtype,
3714                     href : this.href || "#",
3715                     html: this.html || ''
3716                 }
3717             ];
3718             
3719             if (this.icon) {
3720                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3721             }
3722
3723             if(this.glyphicon) {
3724                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3725             }
3726             
3727             if (this.menu) {
3728                 
3729                 cfg.cn[0].html += " <span class='caret'></span>";
3730              
3731             }
3732             
3733             if (this.badge !== '') {
3734                  
3735                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3736             }
3737         }
3738         
3739         
3740         
3741         return cfg;
3742     },
3743     initEvents: function() 
3744     {
3745         if (typeof (this.menu) != 'undefined') {
3746             this.menu.parentType = this.xtype;
3747             this.menu.triggerEl = this.el;
3748             this.addxtype(Roo.apply({}, this.menu));
3749         }
3750         
3751         this.el.select('a',true).on('click', this.onClick, this);
3752         
3753         if(this.tagtype == 'span'){
3754             this.el.select('span',true).on('click', this.onClick, this);
3755         }
3756        
3757         // at this point parent should be available..
3758         this.parent().register(this);
3759     },
3760     
3761     onClick : function(e)
3762     {
3763          
3764         if(this.preventDefault){
3765             e.preventDefault();
3766         }
3767         if (this.disabled) {
3768             return;
3769         }
3770         
3771         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3772         if (tg && tg.transition) {
3773             Roo.log("waiting for the transitionend");
3774             return;
3775         }
3776         
3777         Roo.log("fire event clicked");
3778         if(this.fireEvent('click', this, e) === false){
3779             return;
3780         };
3781         
3782         if(this.tagtype == 'span'){
3783             return;
3784         }
3785         
3786         var p = this.parent();
3787         if (['tabs','pills'].indexOf(p.type)!==-1) {
3788             if (typeof(p.setActiveItem) !== 'undefined') {
3789                 p.setActiveItem(this);
3790             }
3791         }
3792         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3793         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3794             // remove the collapsed menu expand...
3795             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3796         }
3797         
3798     },
3799     
3800     isActive: function () {
3801         return this.active
3802     },
3803     setActive : function(state, fire, is_was_active)
3804     {
3805         if (this.active && !state & this.navId) {
3806             this.was_active = true;
3807             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3808             if (nv) {
3809                 nv.clearWasActive(this);
3810             }
3811             
3812         }
3813         this.active = state;
3814         
3815         if (!state ) {
3816             this.el.removeClass('active');
3817         } else if (!this.el.hasClass('active')) {
3818             this.el.addClass('active');
3819         }
3820         if (fire) {
3821             this.fireEvent('changed', this, state);
3822         }
3823         
3824         // show a panel if it's registered and related..
3825         
3826         if (!this.navId || !this.tabId || !state || is_was_active) {
3827             return;
3828         }
3829         
3830         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3831         if (!tg) {
3832             return;
3833         }
3834         var pan = tg.getPanelByName(this.tabId);
3835         if (!pan) {
3836             return;
3837         }
3838         // if we can not flip to new panel - go back to old nav highlight..
3839         if (false == tg.showPanel(pan)) {
3840             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3841             if (nv) {
3842                 var onav = nv.getWasActive();
3843                 if (onav) {
3844                     onav.setActive(true, false, true);
3845                 }
3846             }
3847             
3848         }
3849         
3850         
3851         
3852     },
3853      // this should not be here...
3854     setDisabled : function(state)
3855     {
3856         this.disabled = state;
3857         if (!state ) {
3858             this.el.removeClass('disabled');
3859         } else if (!this.el.hasClass('disabled')) {
3860             this.el.addClass('disabled');
3861         }
3862         
3863     }
3864 });
3865  
3866
3867  /*
3868  * - LGPL
3869  *
3870  * sidebar item
3871  *
3872  *  li
3873  *    <span> icon </span>
3874  *    <span> text </span>
3875  *    <span>badge </span>
3876  */
3877
3878 /**
3879  * @class Roo.bootstrap.NavSidebarItem
3880  * @extends Roo.bootstrap.NavItem
3881  * Bootstrap Navbar.NavSidebarItem class
3882  * @constructor
3883  * Create a new Navbar Button
3884  * @param {Object} config The config object
3885  */
3886 Roo.bootstrap.NavSidebarItem = function(config){
3887     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3888     this.addEvents({
3889         // raw events
3890         /**
3891          * @event click
3892          * The raw click event for the entire grid.
3893          * @param {Roo.EventObject} e
3894          */
3895         "click" : true,
3896          /**
3897             * @event changed
3898             * Fires when the active item active state changes
3899             * @param {Roo.bootstrap.NavSidebarItem} this
3900             * @param {boolean} state the new state
3901              
3902          */
3903         'changed': true
3904     });
3905    
3906 };
3907
3908 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3909     
3910     
3911     getAutoCreate : function(){
3912         
3913         
3914         var a = {
3915                 tag: 'a',
3916                 href : this.href || '#',
3917                 cls: '',
3918                 html : '',
3919                 cn : []
3920         };
3921         var cfg = {
3922             tag: 'li',
3923             cls: '',
3924             cn: [ a ]
3925         }
3926         var span = {
3927             tag: 'span',
3928             html : this.html || ''
3929         }
3930         
3931         
3932         if (this.active) {
3933             cfg.cls += ' active';
3934         }
3935         
3936         // left icon..
3937         if (this.glyphicon || this.icon) {
3938             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3939             a.cn.push({ tag : 'i', cls : c }) ;
3940         }
3941         // html..
3942         a.cn.push(span);
3943         // then badge..
3944         if (this.badge !== '') {
3945             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3946         }
3947         // fi
3948         if (this.menu) {
3949             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3950             a.cls += 'dropdown-toggle treeview' ;
3951             
3952         }
3953         
3954         
3955         
3956         return cfg;
3957          
3958            
3959     }
3960    
3961      
3962  
3963 });
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * row
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.Row
3975  * @extends Roo.bootstrap.Component
3976  * Bootstrap Row class (contains columns...)
3977  * 
3978  * @constructor
3979  * Create a new Row
3980  * @param {Object} config The config object
3981  */
3982
3983 Roo.bootstrap.Row = function(config){
3984     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3985 };
3986
3987 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3988     
3989     getAutoCreate : function(){
3990        return {
3991             cls: 'row clearfix'
3992        };
3993     }
3994     
3995     
3996 });
3997
3998  
3999
4000  /*
4001  * - LGPL
4002  *
4003  * element
4004  * 
4005  */
4006
4007 /**
4008  * @class Roo.bootstrap.Element
4009  * @extends Roo.bootstrap.Component
4010  * Bootstrap Element class
4011  * @cfg {String} html contents of the element
4012  * @cfg {String} tag tag of the element
4013  * @cfg {String} cls class of the element
4014  * 
4015  * @constructor
4016  * Create a new Element
4017  * @param {Object} config The config object
4018  */
4019
4020 Roo.bootstrap.Element = function(config){
4021     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4022 };
4023
4024 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4025     
4026     tag: 'div',
4027     cls: '',
4028     html: '',
4029      
4030     
4031     getAutoCreate : function(){
4032         
4033         var cfg = {
4034             tag: this.tag,
4035             cls: this.cls,
4036             html: this.html
4037         }
4038         
4039         
4040         
4041         return cfg;
4042     }
4043    
4044 });
4045
4046  
4047
4048  /*
4049  * - LGPL
4050  *
4051  * pagination
4052  * 
4053  */
4054
4055 /**
4056  * @class Roo.bootstrap.Pagination
4057  * @extends Roo.bootstrap.Component
4058  * Bootstrap Pagination class
4059  * @cfg {String} size xs | sm | md | lg
4060  * @cfg {Boolean} inverse false | true
4061  * 
4062  * @constructor
4063  * Create a new Pagination
4064  * @param {Object} config The config object
4065  */
4066
4067 Roo.bootstrap.Pagination = function(config){
4068     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4069 };
4070
4071 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4072     
4073     cls: false,
4074     size: false,
4075     inverse: false,
4076     
4077     getAutoCreate : function(){
4078         var cfg = {
4079             tag: 'ul',
4080                 cls: 'pagination'
4081         };
4082         if (this.inverse) {
4083             cfg.cls += ' inverse';
4084         }
4085         if (this.html) {
4086             cfg.html=this.html;
4087         }
4088         if (this.cls) {
4089             cfg.cls += " " + this.cls;
4090         }
4091         return cfg;
4092     }
4093    
4094 });
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * Pagination item
4102  * 
4103  */
4104
4105
4106 /**
4107  * @class Roo.bootstrap.PaginationItem
4108  * @extends Roo.bootstrap.Component
4109  * Bootstrap PaginationItem class
4110  * @cfg {String} html text
4111  * @cfg {String} href the link
4112  * @cfg {Boolean} preventDefault (true | false) default true
4113  * @cfg {Boolean} active (true | false) default false
4114  * 
4115  * 
4116  * @constructor
4117  * Create a new PaginationItem
4118  * @param {Object} config The config object
4119  */
4120
4121
4122 Roo.bootstrap.PaginationItem = function(config){
4123     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4124     this.addEvents({
4125         // raw events
4126         /**
4127          * @event click
4128          * The raw click event for the entire grid.
4129          * @param {Roo.EventObject} e
4130          */
4131         "click" : true
4132     });
4133 };
4134
4135 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4136     
4137     href : false,
4138     html : false,
4139     preventDefault: true,
4140     active : false,
4141     cls : false,
4142     
4143     getAutoCreate : function(){
4144         var cfg= {
4145             tag: 'li',
4146             cn: [
4147                 {
4148                     tag : 'a',
4149                     href : this.href ? this.href : '#',
4150                     html : this.html ? this.html : ''
4151                 }
4152             ]
4153         };
4154         
4155         if(this.cls){
4156             cfg.cls = this.cls;
4157         }
4158         
4159         if(this.active){
4160             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4161         }
4162         
4163         return cfg;
4164     },
4165     
4166     initEvents: function() {
4167         
4168         this.el.on('click', this.onClick, this);
4169         
4170     },
4171     onClick : function(e)
4172     {
4173         Roo.log('PaginationItem on click ');
4174         if(this.preventDefault){
4175             e.preventDefault();
4176         }
4177         
4178         this.fireEvent('click', this, e);
4179     }
4180    
4181 });
4182
4183  
4184
4185  /*
4186  * - LGPL
4187  *
4188  * slider
4189  * 
4190  */
4191
4192
4193 /**
4194  * @class Roo.bootstrap.Slider
4195  * @extends Roo.bootstrap.Component
4196  * Bootstrap Slider class
4197  *    
4198  * @constructor
4199  * Create a new Slider
4200  * @param {Object} config The config object
4201  */
4202
4203 Roo.bootstrap.Slider = function(config){
4204     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4205 };
4206
4207 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4208     
4209     getAutoCreate : function(){
4210         
4211         var cfg = {
4212             tag: 'div',
4213             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4214             cn: [
4215                 {
4216                     tag: 'a',
4217                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4218                 }
4219             ]
4220         }
4221         
4222         return cfg;
4223     }
4224    
4225 });
4226
4227  /*
4228  * Based on:
4229  * Ext JS Library 1.1.1
4230  * Copyright(c) 2006-2007, Ext JS, LLC.
4231  *
4232  * Originally Released Under LGPL - original licence link has changed is not relivant.
4233  *
4234  * Fork - LGPL
4235  * <script type="text/javascript">
4236  */
4237  
4238
4239 /**
4240  * @class Roo.grid.ColumnModel
4241  * @extends Roo.util.Observable
4242  * This is the default implementation of a ColumnModel used by the Grid. It defines
4243  * the columns in the grid.
4244  * <br>Usage:<br>
4245  <pre><code>
4246  var colModel = new Roo.grid.ColumnModel([
4247         {header: "Ticker", width: 60, sortable: true, locked: true},
4248         {header: "Company Name", width: 150, sortable: true},
4249         {header: "Market Cap.", width: 100, sortable: true},
4250         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4251         {header: "Employees", width: 100, sortable: true, resizable: false}
4252  ]);
4253  </code></pre>
4254  * <p>
4255  
4256  * The config options listed for this class are options which may appear in each
4257  * individual column definition.
4258  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4259  * @constructor
4260  * @param {Object} config An Array of column config objects. See this class's
4261  * config objects for details.
4262 */
4263 Roo.grid.ColumnModel = function(config){
4264         /**
4265      * The config passed into the constructor
4266      */
4267     this.config = config;
4268     this.lookup = {};
4269
4270     // if no id, create one
4271     // if the column does not have a dataIndex mapping,
4272     // map it to the order it is in the config
4273     for(var i = 0, len = config.length; i < len; i++){
4274         var c = config[i];
4275         if(typeof c.dataIndex == "undefined"){
4276             c.dataIndex = i;
4277         }
4278         if(typeof c.renderer == "string"){
4279             c.renderer = Roo.util.Format[c.renderer];
4280         }
4281         if(typeof c.id == "undefined"){
4282             c.id = Roo.id();
4283         }
4284         if(c.editor && c.editor.xtype){
4285             c.editor  = Roo.factory(c.editor, Roo.grid);
4286         }
4287         if(c.editor && c.editor.isFormField){
4288             c.editor = new Roo.grid.GridEditor(c.editor);
4289         }
4290         this.lookup[c.id] = c;
4291     }
4292
4293     /**
4294      * The width of columns which have no width specified (defaults to 100)
4295      * @type Number
4296      */
4297     this.defaultWidth = 100;
4298
4299     /**
4300      * Default sortable of columns which have no sortable specified (defaults to false)
4301      * @type Boolean
4302      */
4303     this.defaultSortable = false;
4304
4305     this.addEvents({
4306         /**
4307              * @event widthchange
4308              * Fires when the width of a column changes.
4309              * @param {ColumnModel} this
4310              * @param {Number} columnIndex The column index
4311              * @param {Number} newWidth The new width
4312              */
4313             "widthchange": true,
4314         /**
4315              * @event headerchange
4316              * Fires when the text of a header changes.
4317              * @param {ColumnModel} this
4318              * @param {Number} columnIndex The column index
4319              * @param {Number} newText The new header text
4320              */
4321             "headerchange": true,
4322         /**
4323              * @event hiddenchange
4324              * Fires when a column is hidden or "unhidden".
4325              * @param {ColumnModel} this
4326              * @param {Number} columnIndex The column index
4327              * @param {Boolean} hidden true if hidden, false otherwise
4328              */
4329             "hiddenchange": true,
4330             /**
4331          * @event columnmoved
4332          * Fires when a column is moved.
4333          * @param {ColumnModel} this
4334          * @param {Number} oldIndex
4335          * @param {Number} newIndex
4336          */
4337         "columnmoved" : true,
4338         /**
4339          * @event columlockchange
4340          * Fires when a column's locked state is changed
4341          * @param {ColumnModel} this
4342          * @param {Number} colIndex
4343          * @param {Boolean} locked true if locked
4344          */
4345         "columnlockchange" : true
4346     });
4347     Roo.grid.ColumnModel.superclass.constructor.call(this);
4348 };
4349 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4350     /**
4351      * @cfg {String} header The header text to display in the Grid view.
4352      */
4353     /**
4354      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4355      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4356      * specified, the column's index is used as an index into the Record's data Array.
4357      */
4358     /**
4359      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4360      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4361      */
4362     /**
4363      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4364      * Defaults to the value of the {@link #defaultSortable} property.
4365      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4366      */
4367     /**
4368      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4369      */
4370     /**
4371      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4372      */
4373     /**
4374      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4375      */
4376     /**
4377      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4378      */
4379     /**
4380      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4381      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4382      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4383      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4384      */
4385        /**
4386      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4387      */
4388     /**
4389      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4390      */
4391
4392     /**
4393      * Returns the id of the column at the specified index.
4394      * @param {Number} index The column index
4395      * @return {String} the id
4396      */
4397     getColumnId : function(index){
4398         return this.config[index].id;
4399     },
4400
4401     /**
4402      * Returns the column for a specified id.
4403      * @param {String} id The column id
4404      * @return {Object} the column
4405      */
4406     getColumnById : function(id){
4407         return this.lookup[id];
4408     },
4409
4410     
4411     /**
4412      * Returns the column for a specified dataIndex.
4413      * @param {String} dataIndex The column dataIndex
4414      * @return {Object|Boolean} the column or false if not found
4415      */
4416     getColumnByDataIndex: function(dataIndex){
4417         var index = this.findColumnIndex(dataIndex);
4418         return index > -1 ? this.config[index] : false;
4419     },
4420     
4421     /**
4422      * Returns the index for a specified column id.
4423      * @param {String} id The column id
4424      * @return {Number} the index, or -1 if not found
4425      */
4426     getIndexById : function(id){
4427         for(var i = 0, len = this.config.length; i < len; i++){
4428             if(this.config[i].id == id){
4429                 return i;
4430             }
4431         }
4432         return -1;
4433     },
4434     
4435     /**
4436      * Returns the index for a specified column dataIndex.
4437      * @param {String} dataIndex The column dataIndex
4438      * @return {Number} the index, or -1 if not found
4439      */
4440     
4441     findColumnIndex : function(dataIndex){
4442         for(var i = 0, len = this.config.length; i < len; i++){
4443             if(this.config[i].dataIndex == dataIndex){
4444                 return i;
4445             }
4446         }
4447         return -1;
4448     },
4449     
4450     
4451     moveColumn : function(oldIndex, newIndex){
4452         var c = this.config[oldIndex];
4453         this.config.splice(oldIndex, 1);
4454         this.config.splice(newIndex, 0, c);
4455         this.dataMap = null;
4456         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4457     },
4458
4459     isLocked : function(colIndex){
4460         return this.config[colIndex].locked === true;
4461     },
4462
4463     setLocked : function(colIndex, value, suppressEvent){
4464         if(this.isLocked(colIndex) == value){
4465             return;
4466         }
4467         this.config[colIndex].locked = value;
4468         if(!suppressEvent){
4469             this.fireEvent("columnlockchange", this, colIndex, value);
4470         }
4471     },
4472
4473     getTotalLockedWidth : function(){
4474         var totalWidth = 0;
4475         for(var i = 0; i < this.config.length; i++){
4476             if(this.isLocked(i) && !this.isHidden(i)){
4477                 this.totalWidth += this.getColumnWidth(i);
4478             }
4479         }
4480         return totalWidth;
4481     },
4482
4483     getLockedCount : function(){
4484         for(var i = 0, len = this.config.length; i < len; i++){
4485             if(!this.isLocked(i)){
4486                 return i;
4487             }
4488         }
4489     },
4490
4491     /**
4492      * Returns the number of columns.
4493      * @return {Number}
4494      */
4495     getColumnCount : function(visibleOnly){
4496         if(visibleOnly === true){
4497             var c = 0;
4498             for(var i = 0, len = this.config.length; i < len; i++){
4499                 if(!this.isHidden(i)){
4500                     c++;
4501                 }
4502             }
4503             return c;
4504         }
4505         return this.config.length;
4506     },
4507
4508     /**
4509      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4510      * @param {Function} fn
4511      * @param {Object} scope (optional)
4512      * @return {Array} result
4513      */
4514     getColumnsBy : function(fn, scope){
4515         var r = [];
4516         for(var i = 0, len = this.config.length; i < len; i++){
4517             var c = this.config[i];
4518             if(fn.call(scope||this, c, i) === true){
4519                 r[r.length] = c;
4520             }
4521         }
4522         return r;
4523     },
4524
4525     /**
4526      * Returns true if the specified column is sortable.
4527      * @param {Number} col The column index
4528      * @return {Boolean}
4529      */
4530     isSortable : function(col){
4531         if(typeof this.config[col].sortable == "undefined"){
4532             return this.defaultSortable;
4533         }
4534         return this.config[col].sortable;
4535     },
4536
4537     /**
4538      * Returns the rendering (formatting) function defined for the column.
4539      * @param {Number} col The column index.
4540      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4541      */
4542     getRenderer : function(col){
4543         if(!this.config[col].renderer){
4544             return Roo.grid.ColumnModel.defaultRenderer;
4545         }
4546         return this.config[col].renderer;
4547     },
4548
4549     /**
4550      * Sets the rendering (formatting) function for a column.
4551      * @param {Number} col The column index
4552      * @param {Function} fn The function to use to process the cell's raw data
4553      * to return HTML markup for the grid view. The render function is called with
4554      * the following parameters:<ul>
4555      * <li>Data value.</li>
4556      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4557      * <li>css A CSS style string to apply to the table cell.</li>
4558      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4559      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4560      * <li>Row index</li>
4561      * <li>Column index</li>
4562      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4563      */
4564     setRenderer : function(col, fn){
4565         this.config[col].renderer = fn;
4566     },
4567
4568     /**
4569      * Returns the width for the specified column.
4570      * @param {Number} col The column index
4571      * @return {Number}
4572      */
4573     getColumnWidth : function(col){
4574         return this.config[col].width * 1 || this.defaultWidth;
4575     },
4576
4577     /**
4578      * Sets the width for a column.
4579      * @param {Number} col The column index
4580      * @param {Number} width The new width
4581      */
4582     setColumnWidth : function(col, width, suppressEvent){
4583         this.config[col].width = width;
4584         this.totalWidth = null;
4585         if(!suppressEvent){
4586              this.fireEvent("widthchange", this, col, width);
4587         }
4588     },
4589
4590     /**
4591      * Returns the total width of all columns.
4592      * @param {Boolean} includeHidden True to include hidden column widths
4593      * @return {Number}
4594      */
4595     getTotalWidth : function(includeHidden){
4596         if(!this.totalWidth){
4597             this.totalWidth = 0;
4598             for(var i = 0, len = this.config.length; i < len; i++){
4599                 if(includeHidden || !this.isHidden(i)){
4600                     this.totalWidth += this.getColumnWidth(i);
4601                 }
4602             }
4603         }
4604         return this.totalWidth;
4605     },
4606
4607     /**
4608      * Returns the header for the specified column.
4609      * @param {Number} col The column index
4610      * @return {String}
4611      */
4612     getColumnHeader : function(col){
4613         return this.config[col].header;
4614     },
4615
4616     /**
4617      * Sets the header for a column.
4618      * @param {Number} col The column index
4619      * @param {String} header The new header
4620      */
4621     setColumnHeader : function(col, header){
4622         this.config[col].header = header;
4623         this.fireEvent("headerchange", this, col, header);
4624     },
4625
4626     /**
4627      * Returns the tooltip for the specified column.
4628      * @param {Number} col The column index
4629      * @return {String}
4630      */
4631     getColumnTooltip : function(col){
4632             return this.config[col].tooltip;
4633     },
4634     /**
4635      * Sets the tooltip for a column.
4636      * @param {Number} col The column index
4637      * @param {String} tooltip The new tooltip
4638      */
4639     setColumnTooltip : function(col, tooltip){
4640             this.config[col].tooltip = tooltip;
4641     },
4642
4643     /**
4644      * Returns the dataIndex for the specified column.
4645      * @param {Number} col The column index
4646      * @return {Number}
4647      */
4648     getDataIndex : function(col){
4649         return this.config[col].dataIndex;
4650     },
4651
4652     /**
4653      * Sets the dataIndex for a column.
4654      * @param {Number} col The column index
4655      * @param {Number} dataIndex The new dataIndex
4656      */
4657     setDataIndex : function(col, dataIndex){
4658         this.config[col].dataIndex = dataIndex;
4659     },
4660
4661     
4662     
4663     /**
4664      * Returns true if the cell is editable.
4665      * @param {Number} colIndex The column index
4666      * @param {Number} rowIndex The row index
4667      * @return {Boolean}
4668      */
4669     isCellEditable : function(colIndex, rowIndex){
4670         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4671     },
4672
4673     /**
4674      * Returns the editor defined for the cell/column.
4675      * return false or null to disable editing.
4676      * @param {Number} colIndex The column index
4677      * @param {Number} rowIndex The row index
4678      * @return {Object}
4679      */
4680     getCellEditor : function(colIndex, rowIndex){
4681         return this.config[colIndex].editor;
4682     },
4683
4684     /**
4685      * Sets if a column is editable.
4686      * @param {Number} col The column index
4687      * @param {Boolean} editable True if the column is editable
4688      */
4689     setEditable : function(col, editable){
4690         this.config[col].editable = editable;
4691     },
4692
4693
4694     /**
4695      * Returns true if the column is hidden.
4696      * @param {Number} colIndex The column index
4697      * @return {Boolean}
4698      */
4699     isHidden : function(colIndex){
4700         return this.config[colIndex].hidden;
4701     },
4702
4703
4704     /**
4705      * Returns true if the column width cannot be changed
4706      */
4707     isFixed : function(colIndex){
4708         return this.config[colIndex].fixed;
4709     },
4710
4711     /**
4712      * Returns true if the column can be resized
4713      * @return {Boolean}
4714      */
4715     isResizable : function(colIndex){
4716         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4717     },
4718     /**
4719      * Sets if a column is hidden.
4720      * @param {Number} colIndex The column index
4721      * @param {Boolean} hidden True if the column is hidden
4722      */
4723     setHidden : function(colIndex, hidden){
4724         this.config[colIndex].hidden = hidden;
4725         this.totalWidth = null;
4726         this.fireEvent("hiddenchange", this, colIndex, hidden);
4727     },
4728
4729     /**
4730      * Sets the editor for a column.
4731      * @param {Number} col The column index
4732      * @param {Object} editor The editor object
4733      */
4734     setEditor : function(col, editor){
4735         this.config[col].editor = editor;
4736     }
4737 });
4738
4739 Roo.grid.ColumnModel.defaultRenderer = function(value){
4740         if(typeof value == "string" && value.length < 1){
4741             return "&#160;";
4742         }
4743         return value;
4744 };
4745
4746 // Alias for backwards compatibility
4747 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4748 /*
4749  * Based on:
4750  * Ext JS Library 1.1.1
4751  * Copyright(c) 2006-2007, Ext JS, LLC.
4752  *
4753  * Originally Released Under LGPL - original licence link has changed is not relivant.
4754  *
4755  * Fork - LGPL
4756  * <script type="text/javascript">
4757  */
4758  
4759 /**
4760  * @class Roo.LoadMask
4761  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4762  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4763  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4764  * element's UpdateManager load indicator and will be destroyed after the initial load.
4765  * @constructor
4766  * Create a new LoadMask
4767  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4768  * @param {Object} config The config object
4769  */
4770 Roo.LoadMask = function(el, config){
4771     this.el = Roo.get(el);
4772     Roo.apply(this, config);
4773     if(this.store){
4774         this.store.on('beforeload', this.onBeforeLoad, this);
4775         this.store.on('load', this.onLoad, this);
4776         this.store.on('loadexception', this.onLoadException, this);
4777         this.removeMask = false;
4778     }else{
4779         var um = this.el.getUpdateManager();
4780         um.showLoadIndicator = false; // disable the default indicator
4781         um.on('beforeupdate', this.onBeforeLoad, this);
4782         um.on('update', this.onLoad, this);
4783         um.on('failure', this.onLoad, this);
4784         this.removeMask = true;
4785     }
4786 };
4787
4788 Roo.LoadMask.prototype = {
4789     /**
4790      * @cfg {Boolean} removeMask
4791      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4792      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4793      */
4794     /**
4795      * @cfg {String} msg
4796      * The text to display in a centered loading message box (defaults to 'Loading...')
4797      */
4798     msg : 'Loading...',
4799     /**
4800      * @cfg {String} msgCls
4801      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4802      */
4803     msgCls : 'x-mask-loading',
4804
4805     /**
4806      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4807      * @type Boolean
4808      */
4809     disabled: false,
4810
4811     /**
4812      * Disables the mask to prevent it from being displayed
4813      */
4814     disable : function(){
4815        this.disabled = true;
4816     },
4817
4818     /**
4819      * Enables the mask so that it can be displayed
4820      */
4821     enable : function(){
4822         this.disabled = false;
4823     },
4824     
4825     onLoadException : function()
4826     {
4827         Roo.log(arguments);
4828         
4829         if (typeof(arguments[3]) != 'undefined') {
4830             Roo.MessageBox.alert("Error loading",arguments[3]);
4831         } 
4832         /*
4833         try {
4834             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4835                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4836             }   
4837         } catch(e) {
4838             
4839         }
4840         */
4841     
4842         
4843         
4844         this.el.unmask(this.removeMask);
4845     },
4846     // private
4847     onLoad : function()
4848     {
4849         this.el.unmask(this.removeMask);
4850     },
4851
4852     // private
4853     onBeforeLoad : function(){
4854         if(!this.disabled){
4855             this.el.mask(this.msg, this.msgCls);
4856         }
4857     },
4858
4859     // private
4860     destroy : function(){
4861         if(this.store){
4862             this.store.un('beforeload', this.onBeforeLoad, this);
4863             this.store.un('load', this.onLoad, this);
4864             this.store.un('loadexception', this.onLoadException, this);
4865         }else{
4866             var um = this.el.getUpdateManager();
4867             um.un('beforeupdate', this.onBeforeLoad, this);
4868             um.un('update', this.onLoad, this);
4869             um.un('failure', this.onLoad, this);
4870         }
4871     }
4872 };/*
4873  * - LGPL
4874  *
4875  * table
4876  * 
4877  */
4878
4879 /**
4880  * @class Roo.bootstrap.Table
4881  * @extends Roo.bootstrap.Component
4882  * Bootstrap Table class
4883  * @cfg {String} cls table class
4884  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4885  * @cfg {String} bgcolor Specifies the background color for a table
4886  * @cfg {Number} border Specifies whether the table cells should have borders or not
4887  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4888  * @cfg {Number} cellspacing Specifies the space between cells
4889  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4890  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4891  * @cfg {String} sortable Specifies that the table should be sortable
4892  * @cfg {String} summary Specifies a summary of the content of a table
4893  * @cfg {Number} width Specifies the width of a table
4894  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4895  * 
4896  * @cfg {boolean} striped Should the rows be alternative striped
4897  * @cfg {boolean} bordered Add borders to the table
4898  * @cfg {boolean} hover Add hover highlighting
4899  * @cfg {boolean} condensed Format condensed
4900  * @cfg {boolean} responsive Format condensed
4901  * @cfg {Boolean} loadMask (true|false) default false
4902  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4903  * @cfg {Boolean} thead (true|false) generate thead, default true
4904  * @cfg {Boolean} RowSelection (true|false) default false
4905  * @cfg {Boolean} CellSelection (true|false) default false
4906  *
4907  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4908  
4909  * 
4910  * @constructor
4911  * Create a new Table
4912  * @param {Object} config The config object
4913  */
4914
4915 Roo.bootstrap.Table = function(config){
4916     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4917     
4918     if (this.sm) {
4919         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4920         this.sm = this.selModel;
4921         this.sm.xmodule = this.xmodule || false;
4922     }
4923     if (this.cm && typeof(this.cm.config) == 'undefined') {
4924         this.colModel = new Roo.grid.ColumnModel(this.cm);
4925         this.cm = this.colModel;
4926         this.cm.xmodule = this.xmodule || false;
4927     }
4928     if (this.store) {
4929         this.store= Roo.factory(this.store, Roo.data);
4930         this.ds = this.store;
4931         this.ds.xmodule = this.xmodule || false;
4932          
4933     }
4934     if (this.footer && this.store) {
4935         this.footer.dataSource = this.ds;
4936         this.footer = Roo.factory(this.footer);
4937     }
4938     
4939     /** @private */
4940     this.addEvents({
4941         /**
4942          * @event cellclick
4943          * Fires when a cell is clicked
4944          * @param {Roo.bootstrap.Table} this
4945          * @param {Roo.Element} el
4946          * @param {Number} rowIndex
4947          * @param {Number} columnIndex
4948          * @param {Roo.EventObject} e
4949          */
4950         "cellclick" : true,
4951         /**
4952          * @event celldblclick
4953          * Fires when a cell is double clicked
4954          * @param {Roo.bootstrap.Table} this
4955          * @param {Roo.Element} el
4956          * @param {Number} rowIndex
4957          * @param {Number} columnIndex
4958          * @param {Roo.EventObject} e
4959          */
4960         "celldblclick" : true,
4961         /**
4962          * @event rowclick
4963          * Fires when a row is clicked
4964          * @param {Roo.bootstrap.Table} this
4965          * @param {Roo.Element} el
4966          * @param {Number} rowIndex
4967          * @param {Roo.EventObject} e
4968          */
4969         "rowclick" : true,
4970         /**
4971          * @event rowdblclick
4972          * Fires when a row is double clicked
4973          * @param {Roo.bootstrap.Table} this
4974          * @param {Roo.Element} el
4975          * @param {Number} rowIndex
4976          * @param {Roo.EventObject} e
4977          */
4978         "rowdblclick" : true,
4979         /**
4980          * @event mouseover
4981          * Fires when a mouseover occur
4982          * @param {Roo.bootstrap.Table} this
4983          * @param {Roo.Element} el
4984          * @param {Number} rowIndex
4985          * @param {Number} columnIndex
4986          * @param {Roo.EventObject} e
4987          */
4988         "mouseover" : true,
4989         /**
4990          * @event mouseout
4991          * Fires when a mouseout occur
4992          * @param {Roo.bootstrap.Table} this
4993          * @param {Roo.Element} el
4994          * @param {Number} rowIndex
4995          * @param {Number} columnIndex
4996          * @param {Roo.EventObject} e
4997          */
4998         "mouseout" : true,
4999         /**
5000          * @event rowclass
5001          * Fires when a row is rendered, so you can change add a style to it.
5002          * @param {Roo.bootstrap.Table} this
5003          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5004          */
5005         'rowclass' : true
5006         
5007     });
5008 };
5009
5010 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5011     
5012     cls: false,
5013     align: false,
5014     bgcolor: false,
5015     border: false,
5016     cellpadding: false,
5017     cellspacing: false,
5018     frame: false,
5019     rules: false,
5020     sortable: false,
5021     summary: false,
5022     width: false,
5023     striped : false,
5024     bordered: false,
5025     hover:  false,
5026     condensed : false,
5027     responsive : false,
5028     sm : false,
5029     cm : false,
5030     store : false,
5031     loadMask : false,
5032     tfoot : true,
5033     thead : true,
5034     RowSelection : false,
5035     CellSelection : false,
5036     layout : false,
5037     
5038     // Roo.Element - the tbody
5039     mainBody: false, 
5040     
5041     getAutoCreate : function(){
5042         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5043         
5044         cfg = {
5045             tag: 'table',
5046             cls : 'table',
5047             cn : []
5048         }
5049             
5050         if (this.striped) {
5051             cfg.cls += ' table-striped';
5052         }
5053         
5054         if (this.hover) {
5055             cfg.cls += ' table-hover';
5056         }
5057         if (this.bordered) {
5058             cfg.cls += ' table-bordered';
5059         }
5060         if (this.condensed) {
5061             cfg.cls += ' table-condensed';
5062         }
5063         if (this.responsive) {
5064             cfg.cls += ' table-responsive';
5065         }
5066         
5067         if (this.cls) {
5068             cfg.cls+=  ' ' +this.cls;
5069         }
5070         
5071         // this lot should be simplifed...
5072         
5073         if (this.align) {
5074             cfg.align=this.align;
5075         }
5076         if (this.bgcolor) {
5077             cfg.bgcolor=this.bgcolor;
5078         }
5079         if (this.border) {
5080             cfg.border=this.border;
5081         }
5082         if (this.cellpadding) {
5083             cfg.cellpadding=this.cellpadding;
5084         }
5085         if (this.cellspacing) {
5086             cfg.cellspacing=this.cellspacing;
5087         }
5088         if (this.frame) {
5089             cfg.frame=this.frame;
5090         }
5091         if (this.rules) {
5092             cfg.rules=this.rules;
5093         }
5094         if (this.sortable) {
5095             cfg.sortable=this.sortable;
5096         }
5097         if (this.summary) {
5098             cfg.summary=this.summary;
5099         }
5100         if (this.width) {
5101             cfg.width=this.width;
5102         }
5103         if (this.layout) {
5104             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5105         }
5106         
5107         if(this.store || this.cm){
5108             if(this.thead){
5109                 cfg.cn.push(this.renderHeader());
5110             }
5111             
5112             cfg.cn.push(this.renderBody());
5113             
5114             if(this.tfoot){
5115                 cfg.cn.push(this.renderFooter());
5116             }
5117             
5118             cfg.cls+=  ' TableGrid';
5119         }
5120         
5121         return { cn : [ cfg ] };
5122     },
5123     
5124     initEvents : function()
5125     {   
5126         if(!this.store || !this.cm){
5127             return;
5128         }
5129         
5130         //Roo.log('initEvents with ds!!!!');
5131         
5132         this.mainBody = this.el.select('tbody', true).first();
5133         
5134         
5135         var _this = this;
5136         
5137         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5138             e.on('click', _this.sort, _this);
5139         });
5140         
5141         this.el.on("click", this.onClick, this);
5142         this.el.on("dblclick", this.onDblClick, this);
5143         
5144         this.parent().el.setStyle('position', 'relative');
5145         if (this.footer) {
5146             this.footer.parentId = this.id;
5147             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5148         }
5149         
5150         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5151         
5152         this.store.on('load', this.onLoad, this);
5153         this.store.on('beforeload', this.onBeforeLoad, this);
5154         this.store.on('update', this.onUpdate, this);
5155         
5156     },
5157     
5158     onMouseover : function(e, el)
5159     {
5160         var cell = Roo.get(el);
5161         
5162         if(!cell){
5163             return;
5164         }
5165         
5166         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5167             cell = cell.findParent('td', false, true);
5168         }
5169         
5170         var row = cell.findParent('tr', false, true);
5171         var cellIndex = cell.dom.cellIndex;
5172         var rowIndex = row.dom.rowIndex - 1; // start from 0
5173         
5174         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5175         
5176     },
5177     
5178     onMouseout : function(e, el)
5179     {
5180         var cell = Roo.get(el);
5181         
5182         if(!cell){
5183             return;
5184         }
5185         
5186         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5187             cell = cell.findParent('td', false, true);
5188         }
5189         
5190         var row = cell.findParent('tr', false, true);
5191         var cellIndex = cell.dom.cellIndex;
5192         var rowIndex = row.dom.rowIndex - 1; // start from 0
5193         
5194         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5195         
5196     },
5197     
5198     onClick : function(e, el)
5199     {
5200         var cell = Roo.get(el);
5201         
5202         if(!cell || (!this.CellSelection && !this.RowSelection)){
5203             return;
5204         }
5205         
5206         
5207         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5208             cell = cell.findParent('td', false, true);
5209         }
5210         
5211         var row = cell.findParent('tr', false, true);
5212         var cellIndex = cell.dom.cellIndex;
5213         var rowIndex = row.dom.rowIndex - 1;
5214         
5215         if(this.CellSelection){
5216             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5217         }
5218         
5219         if(this.RowSelection){
5220             this.fireEvent('rowclick', this, row, rowIndex, e);
5221         }
5222         
5223         
5224     },
5225     
5226     onDblClick : function(e,el)
5227     {
5228         var cell = Roo.get(el);
5229         
5230         if(!cell || (!this.CellSelection && !this.RowSelection)){
5231             return;
5232         }
5233         
5234         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5235             cell = cell.findParent('td', false, true);
5236         }
5237         
5238         var row = cell.findParent('tr', false, true);
5239         var cellIndex = cell.dom.cellIndex;
5240         var rowIndex = row.dom.rowIndex - 1;
5241         
5242         if(this.CellSelection){
5243             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5244         }
5245         
5246         if(this.RowSelection){
5247             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5248         }
5249     },
5250     
5251     sort : function(e,el)
5252     {
5253         var col = Roo.get(el)
5254         
5255         if(!col.hasClass('sortable')){
5256             return;
5257         }
5258         
5259         var sort = col.attr('sort');
5260         var dir = 'ASC';
5261         
5262         if(col.hasClass('glyphicon-arrow-up')){
5263             dir = 'DESC';
5264         }
5265         
5266         this.store.sortInfo = {field : sort, direction : dir};
5267         
5268         if (this.footer) {
5269             Roo.log("calling footer first");
5270             this.footer.onClick('first');
5271         } else {
5272         
5273             this.store.load({ params : { start : 0 } });
5274         }
5275     },
5276     
5277     renderHeader : function()
5278     {
5279         var header = {
5280             tag: 'thead',
5281             cn : []
5282         };
5283         
5284         var cm = this.cm;
5285         
5286         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5287             
5288             var config = cm.config[i];
5289                     
5290             var c = {
5291                 tag: 'th',
5292                 style : '',
5293                 html: cm.getColumnHeader(i)
5294             };
5295             
5296             if(typeof(config.hidden) != 'undefined' && config.hidden){
5297                 c.style += ' display:none;';
5298             }
5299             
5300             if(typeof(config.dataIndex) != 'undefined'){
5301                 c.sort = config.dataIndex;
5302             }
5303             
5304             if(typeof(config.sortable) != 'undefined' && config.sortable){
5305                 c.cls = 'sortable';
5306             }
5307             
5308             if(typeof(config.align) != 'undefined' && config.align.length){
5309                 c.style += ' text-align:' + config.align + ';';
5310             }
5311             
5312             if(typeof(config.width) != 'undefined'){
5313                 c.style += ' width:' + config.width + 'px;';
5314             }
5315             
5316             header.cn.push(c)
5317         }
5318         
5319         return header;
5320     },
5321     
5322     renderBody : function()
5323     {
5324         var body = {
5325             tag: 'tbody',
5326             cn : [
5327                 {
5328                     tag: 'tr',
5329                     cn : [
5330                         {
5331                             tag : 'td',
5332                             colspan :  this.cm.getColumnCount()
5333                         }
5334                     ]
5335                 }
5336             ]
5337         };
5338         
5339         return body;
5340     },
5341     
5342     renderFooter : function()
5343     {
5344         var footer = {
5345             tag: 'tfoot',
5346             cn : [
5347                 {
5348                     tag: 'tr',
5349                     cn : [
5350                         {
5351                             tag : 'td',
5352                             colspan :  this.cm.getColumnCount()
5353                         }
5354                     ]
5355                 }
5356             ]
5357         };
5358         
5359         return footer;
5360     },
5361     
5362     
5363     
5364     onLoad : function()
5365     {
5366         Roo.log('ds onload');
5367         this.clear();
5368         
5369         var _this = this;
5370         var cm = this.cm;
5371         var ds = this.store;
5372         
5373         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5374             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5375             
5376             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5377                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5378             }
5379             
5380             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5381                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5382             }
5383         });
5384         
5385         var tbody =  this.mainBody;
5386               
5387         if(ds.getCount() > 0){
5388             ds.data.each(function(d,rowIndex){
5389                 var row =  this.renderRow(cm, ds, rowIndex);
5390                 
5391                 tbody.createChild(row);
5392                 
5393                 var _this = this;
5394                 
5395                 if(row.cellObjects.length){
5396                     Roo.each(row.cellObjects, function(r){
5397                         _this.renderCellObject(r);
5398                     })
5399                 }
5400                 
5401             }, this);
5402         }
5403         
5404         Roo.each(this.el.select('tbody td', true).elements, function(e){
5405             e.on('mouseover', _this.onMouseover, _this);
5406         });
5407         
5408         Roo.each(this.el.select('tbody td', true).elements, function(e){
5409             e.on('mouseout', _this.onMouseout, _this);
5410         });
5411
5412         //if(this.loadMask){
5413         //    this.maskEl.hide();
5414         //}
5415     },
5416     
5417     
5418     onUpdate : function(ds,record)
5419     {
5420         this.refreshRow(record);
5421     },
5422     onRemove : function(ds, record, index, isUpdate){
5423         if(isUpdate !== true){
5424             this.fireEvent("beforerowremoved", this, index, record);
5425         }
5426         var bt = this.mainBody.dom;
5427         if(bt.rows[index]){
5428             bt.removeChild(bt.rows[index]);
5429         }
5430         
5431         if(isUpdate !== true){
5432             //this.stripeRows(index);
5433             //this.syncRowHeights(index, index);
5434             //this.layout();
5435             this.fireEvent("rowremoved", this, index, record);
5436         }
5437     },
5438     
5439     
5440     refreshRow : function(record){
5441         var ds = this.store, index;
5442         if(typeof record == 'number'){
5443             index = record;
5444             record = ds.getAt(index);
5445         }else{
5446             index = ds.indexOf(record);
5447         }
5448         this.insertRow(ds, index, true);
5449         this.onRemove(ds, record, index+1, true);
5450         //this.syncRowHeights(index, index);
5451         //this.layout();
5452         this.fireEvent("rowupdated", this, index, record);
5453     },
5454     
5455     insertRow : function(dm, rowIndex, isUpdate){
5456         
5457         if(!isUpdate){
5458             this.fireEvent("beforerowsinserted", this, rowIndex);
5459         }
5460             //var s = this.getScrollState();
5461         var row = this.renderRow(this.cm, this.store, rowIndex);
5462         // insert before rowIndex..
5463         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5464         
5465         var _this = this;
5466                 
5467         if(row.cellObjects.length){
5468             Roo.each(row.cellObjects, function(r){
5469                 _this.renderCellObject(r);
5470             })
5471         }
5472             
5473         if(!isUpdate){
5474             this.fireEvent("rowsinserted", this, rowIndex);
5475             //this.syncRowHeights(firstRow, lastRow);
5476             //this.stripeRows(firstRow);
5477             //this.layout();
5478         }
5479         
5480     },
5481     
5482     
5483     getRowDom : function(rowIndex)
5484     {
5485         // not sure if I need to check this.. but let's do it anyway..
5486         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5487                 this.mainBody.dom.rows[rowIndex] : false
5488     },
5489     // returns the object tree for a tr..
5490   
5491     
5492     renderRow : function(cm, ds, rowIndex) {
5493         
5494         var d = ds.getAt(rowIndex);
5495         
5496         var row = {
5497             tag : 'tr',
5498             cn : []
5499         };
5500             
5501         var cellObjects = [];
5502         
5503         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5504             var config = cm.config[i];
5505             
5506             var renderer = cm.getRenderer(i);
5507             var value = '';
5508             var id = false;
5509             
5510             if(typeof(renderer) !== 'undefined'){
5511                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5512             }
5513             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5514             // and are rendered into the cells after the row is rendered - using the id for the element.
5515             
5516             if(typeof(value) === 'object'){
5517                 id = Roo.id();
5518                 cellObjects.push({
5519                     container : id,
5520                     cfg : value 
5521                 })
5522             }
5523             
5524             var rowcfg = {
5525                 record: d,
5526                 rowIndex : rowIndex,
5527                 colIndex : i,
5528                 rowClass : ''
5529             }
5530
5531             this.fireEvent('rowclass', this, rowcfg);
5532             
5533             var td = {
5534                 tag: 'td',
5535                 cls : rowcfg.rowClass,
5536                 style: '',
5537                 html: (typeof(value) === 'object') ? '' : value
5538             };
5539             
5540             if (id) {
5541                 td.id = id;
5542             }
5543             
5544             if(typeof(config.hidden) != 'undefined' && config.hidden){
5545                 td.style += ' display:none;';
5546             }
5547             
5548             if(typeof(config.align) != 'undefined' && config.align.length){
5549                 td.style += ' text-align:' + config.align + ';';
5550             }
5551             
5552             if(typeof(config.width) != 'undefined'){
5553                 td.style += ' width:' +  config.width + 'px;';
5554             }
5555              
5556             row.cn.push(td);
5557            
5558         }
5559         
5560         row.cellObjects = cellObjects;
5561         
5562         return row;
5563           
5564     },
5565     
5566     
5567     
5568     onBeforeLoad : function()
5569     {
5570         //Roo.log('ds onBeforeLoad');
5571         
5572         //this.clear();
5573         
5574         //if(this.loadMask){
5575         //    this.maskEl.show();
5576         //}
5577     },
5578     
5579     clear : function()
5580     {
5581         this.el.select('tbody', true).first().dom.innerHTML = '';
5582     },
5583     
5584     getSelectionModel : function(){
5585         if(!this.selModel){
5586             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5587         }
5588         return this.selModel;
5589     },
5590     /*
5591      * Render the Roo.bootstrap object from renderder
5592      */
5593     renderCellObject : function(r)
5594     {
5595         var _this = this;
5596         
5597         var t = r.cfg.render(r.container);
5598         
5599         if(r.cfg.cn){
5600             Roo.each(r.cfg.cn, function(c){
5601                 var child = {
5602                     container: t.getChildContainer(),
5603                     cfg: c
5604                 }
5605                 _this.renderCellObject(child);
5606             })
5607         }
5608     }
5609    
5610 });
5611
5612  
5613
5614  /*
5615  * - LGPL
5616  *
5617  * table cell
5618  * 
5619  */
5620
5621 /**
5622  * @class Roo.bootstrap.TableCell
5623  * @extends Roo.bootstrap.Component
5624  * Bootstrap TableCell class
5625  * @cfg {String} html cell contain text
5626  * @cfg {String} cls cell class
5627  * @cfg {String} tag cell tag (td|th) default td
5628  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5629  * @cfg {String} align Aligns the content in a cell
5630  * @cfg {String} axis Categorizes cells
5631  * @cfg {String} bgcolor Specifies the background color of a cell
5632  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5633  * @cfg {Number} colspan Specifies the number of columns a cell should span
5634  * @cfg {String} headers Specifies one or more header cells a cell is related to
5635  * @cfg {Number} height Sets the height of a cell
5636  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5637  * @cfg {Number} rowspan Sets the number of rows a cell should span
5638  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5639  * @cfg {String} valign Vertical aligns the content in a cell
5640  * @cfg {Number} width Specifies the width of a cell
5641  * 
5642  * @constructor
5643  * Create a new TableCell
5644  * @param {Object} config The config object
5645  */
5646
5647 Roo.bootstrap.TableCell = function(config){
5648     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5649 };
5650
5651 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5652     
5653     html: false,
5654     cls: false,
5655     tag: false,
5656     abbr: false,
5657     align: false,
5658     axis: false,
5659     bgcolor: false,
5660     charoff: false,
5661     colspan: false,
5662     headers: false,
5663     height: false,
5664     nowrap: false,
5665     rowspan: false,
5666     scope: false,
5667     valign: false,
5668     width: false,
5669     
5670     
5671     getAutoCreate : function(){
5672         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5673         
5674         cfg = {
5675             tag: 'td'
5676         }
5677         
5678         if(this.tag){
5679             cfg.tag = this.tag;
5680         }
5681         
5682         if (this.html) {
5683             cfg.html=this.html
5684         }
5685         if (this.cls) {
5686             cfg.cls=this.cls
5687         }
5688         if (this.abbr) {
5689             cfg.abbr=this.abbr
5690         }
5691         if (this.align) {
5692             cfg.align=this.align
5693         }
5694         if (this.axis) {
5695             cfg.axis=this.axis
5696         }
5697         if (this.bgcolor) {
5698             cfg.bgcolor=this.bgcolor
5699         }
5700         if (this.charoff) {
5701             cfg.charoff=this.charoff
5702         }
5703         if (this.colspan) {
5704             cfg.colspan=this.colspan
5705         }
5706         if (this.headers) {
5707             cfg.headers=this.headers
5708         }
5709         if (this.height) {
5710             cfg.height=this.height
5711         }
5712         if (this.nowrap) {
5713             cfg.nowrap=this.nowrap
5714         }
5715         if (this.rowspan) {
5716             cfg.rowspan=this.rowspan
5717         }
5718         if (this.scope) {
5719             cfg.scope=this.scope
5720         }
5721         if (this.valign) {
5722             cfg.valign=this.valign
5723         }
5724         if (this.width) {
5725             cfg.width=this.width
5726         }
5727         
5728         
5729         return cfg;
5730     }
5731    
5732 });
5733
5734  
5735
5736  /*
5737  * - LGPL
5738  *
5739  * table row
5740  * 
5741  */
5742
5743 /**
5744  * @class Roo.bootstrap.TableRow
5745  * @extends Roo.bootstrap.Component
5746  * Bootstrap TableRow class
5747  * @cfg {String} cls row class
5748  * @cfg {String} align Aligns the content in a table row
5749  * @cfg {String} bgcolor Specifies a background color for a table row
5750  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5751  * @cfg {String} valign Vertical aligns the content in a table row
5752  * 
5753  * @constructor
5754  * Create a new TableRow
5755  * @param {Object} config The config object
5756  */
5757
5758 Roo.bootstrap.TableRow = function(config){
5759     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5760 };
5761
5762 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5763     
5764     cls: false,
5765     align: false,
5766     bgcolor: false,
5767     charoff: false,
5768     valign: false,
5769     
5770     getAutoCreate : function(){
5771         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5772         
5773         cfg = {
5774             tag: 'tr'
5775         }
5776             
5777         if(this.cls){
5778             cfg.cls = this.cls;
5779         }
5780         if(this.align){
5781             cfg.align = this.align;
5782         }
5783         if(this.bgcolor){
5784             cfg.bgcolor = this.bgcolor;
5785         }
5786         if(this.charoff){
5787             cfg.charoff = this.charoff;
5788         }
5789         if(this.valign){
5790             cfg.valign = this.valign;
5791         }
5792         
5793         return cfg;
5794     }
5795    
5796 });
5797
5798  
5799
5800  /*
5801  * - LGPL
5802  *
5803  * table body
5804  * 
5805  */
5806
5807 /**
5808  * @class Roo.bootstrap.TableBody
5809  * @extends Roo.bootstrap.Component
5810  * Bootstrap TableBody class
5811  * @cfg {String} cls element class
5812  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5813  * @cfg {String} align Aligns the content inside the element
5814  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5815  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5816  * 
5817  * @constructor
5818  * Create a new TableBody
5819  * @param {Object} config The config object
5820  */
5821
5822 Roo.bootstrap.TableBody = function(config){
5823     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5824 };
5825
5826 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5827     
5828     cls: false,
5829     tag: false,
5830     align: false,
5831     charoff: false,
5832     valign: false,
5833     
5834     getAutoCreate : function(){
5835         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5836         
5837         cfg = {
5838             tag: 'tbody'
5839         }
5840             
5841         if (this.cls) {
5842             cfg.cls=this.cls
5843         }
5844         if(this.tag){
5845             cfg.tag = this.tag;
5846         }
5847         
5848         if(this.align){
5849             cfg.align = this.align;
5850         }
5851         if(this.charoff){
5852             cfg.charoff = this.charoff;
5853         }
5854         if(this.valign){
5855             cfg.valign = this.valign;
5856         }
5857         
5858         return cfg;
5859     }
5860     
5861     
5862 //    initEvents : function()
5863 //    {
5864 //        
5865 //        if(!this.store){
5866 //            return;
5867 //        }
5868 //        
5869 //        this.store = Roo.factory(this.store, Roo.data);
5870 //        this.store.on('load', this.onLoad, this);
5871 //        
5872 //        this.store.load();
5873 //        
5874 //    },
5875 //    
5876 //    onLoad: function () 
5877 //    {   
5878 //        this.fireEvent('load', this);
5879 //    }
5880 //    
5881 //   
5882 });
5883
5884  
5885
5886  /*
5887  * Based on:
5888  * Ext JS Library 1.1.1
5889  * Copyright(c) 2006-2007, Ext JS, LLC.
5890  *
5891  * Originally Released Under LGPL - original licence link has changed is not relivant.
5892  *
5893  * Fork - LGPL
5894  * <script type="text/javascript">
5895  */
5896
5897 // as we use this in bootstrap.
5898 Roo.namespace('Roo.form');
5899  /**
5900  * @class Roo.form.Action
5901  * Internal Class used to handle form actions
5902  * @constructor
5903  * @param {Roo.form.BasicForm} el The form element or its id
5904  * @param {Object} config Configuration options
5905  */
5906
5907  
5908  
5909 // define the action interface
5910 Roo.form.Action = function(form, options){
5911     this.form = form;
5912     this.options = options || {};
5913 };
5914 /**
5915  * Client Validation Failed
5916  * @const 
5917  */
5918 Roo.form.Action.CLIENT_INVALID = 'client';
5919 /**
5920  * Server Validation Failed
5921  * @const 
5922  */
5923 Roo.form.Action.SERVER_INVALID = 'server';
5924  /**
5925  * Connect to Server Failed
5926  * @const 
5927  */
5928 Roo.form.Action.CONNECT_FAILURE = 'connect';
5929 /**
5930  * Reading Data from Server Failed
5931  * @const 
5932  */
5933 Roo.form.Action.LOAD_FAILURE = 'load';
5934
5935 Roo.form.Action.prototype = {
5936     type : 'default',
5937     failureType : undefined,
5938     response : undefined,
5939     result : undefined,
5940
5941     // interface method
5942     run : function(options){
5943
5944     },
5945
5946     // interface method
5947     success : function(response){
5948
5949     },
5950
5951     // interface method
5952     handleResponse : function(response){
5953
5954     },
5955
5956     // default connection failure
5957     failure : function(response){
5958         
5959         this.response = response;
5960         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5961         this.form.afterAction(this, false);
5962     },
5963
5964     processResponse : function(response){
5965         this.response = response;
5966         if(!response.responseText){
5967             return true;
5968         }
5969         this.result = this.handleResponse(response);
5970         return this.result;
5971     },
5972
5973     // utility functions used internally
5974     getUrl : function(appendParams){
5975         var url = this.options.url || this.form.url || this.form.el.dom.action;
5976         if(appendParams){
5977             var p = this.getParams();
5978             if(p){
5979                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5980             }
5981         }
5982         return url;
5983     },
5984
5985     getMethod : function(){
5986         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5987     },
5988
5989     getParams : function(){
5990         var bp = this.form.baseParams;
5991         var p = this.options.params;
5992         if(p){
5993             if(typeof p == "object"){
5994                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5995             }else if(typeof p == 'string' && bp){
5996                 p += '&' + Roo.urlEncode(bp);
5997             }
5998         }else if(bp){
5999             p = Roo.urlEncode(bp);
6000         }
6001         return p;
6002     },
6003
6004     createCallback : function(){
6005         return {
6006             success: this.success,
6007             failure: this.failure,
6008             scope: this,
6009             timeout: (this.form.timeout*1000),
6010             upload: this.form.fileUpload ? this.success : undefined
6011         };
6012     }
6013 };
6014
6015 Roo.form.Action.Submit = function(form, options){
6016     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6017 };
6018
6019 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6020     type : 'submit',
6021
6022     haveProgress : false,
6023     uploadComplete : false,
6024     
6025     // uploadProgress indicator.
6026     uploadProgress : function()
6027     {
6028         if (!this.form.progressUrl) {
6029             return;
6030         }
6031         
6032         if (!this.haveProgress) {
6033             Roo.MessageBox.progress("Uploading", "Uploading");
6034         }
6035         if (this.uploadComplete) {
6036            Roo.MessageBox.hide();
6037            return;
6038         }
6039         
6040         this.haveProgress = true;
6041    
6042         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6043         
6044         var c = new Roo.data.Connection();
6045         c.request({
6046             url : this.form.progressUrl,
6047             params: {
6048                 id : uid
6049             },
6050             method: 'GET',
6051             success : function(req){
6052                //console.log(data);
6053                 var rdata = false;
6054                 var edata;
6055                 try  {
6056                    rdata = Roo.decode(req.responseText)
6057                 } catch (e) {
6058                     Roo.log("Invalid data from server..");
6059                     Roo.log(edata);
6060                     return;
6061                 }
6062                 if (!rdata || !rdata.success) {
6063                     Roo.log(rdata);
6064                     Roo.MessageBox.alert(Roo.encode(rdata));
6065                     return;
6066                 }
6067                 var data = rdata.data;
6068                 
6069                 if (this.uploadComplete) {
6070                    Roo.MessageBox.hide();
6071                    return;
6072                 }
6073                    
6074                 if (data){
6075                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6076                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6077                     );
6078                 }
6079                 this.uploadProgress.defer(2000,this);
6080             },
6081        
6082             failure: function(data) {
6083                 Roo.log('progress url failed ');
6084                 Roo.log(data);
6085             },
6086             scope : this
6087         });
6088            
6089     },
6090     
6091     
6092     run : function()
6093     {
6094         // run get Values on the form, so it syncs any secondary forms.
6095         this.form.getValues();
6096         
6097         var o = this.options;
6098         var method = this.getMethod();
6099         var isPost = method == 'POST';
6100         if(o.clientValidation === false || this.form.isValid()){
6101             
6102             if (this.form.progressUrl) {
6103                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6104                     (new Date() * 1) + '' + Math.random());
6105                     
6106             } 
6107             
6108             
6109             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6110                 form:this.form.el.dom,
6111                 url:this.getUrl(!isPost),
6112                 method: method,
6113                 params:isPost ? this.getParams() : null,
6114                 isUpload: this.form.fileUpload
6115             }));
6116             
6117             this.uploadProgress();
6118
6119         }else if (o.clientValidation !== false){ // client validation failed
6120             this.failureType = Roo.form.Action.CLIENT_INVALID;
6121             this.form.afterAction(this, false);
6122         }
6123     },
6124
6125     success : function(response)
6126     {
6127         this.uploadComplete= true;
6128         if (this.haveProgress) {
6129             Roo.MessageBox.hide();
6130         }
6131         
6132         
6133         var result = this.processResponse(response);
6134         if(result === true || result.success){
6135             this.form.afterAction(this, true);
6136             return;
6137         }
6138         if(result.errors){
6139             this.form.markInvalid(result.errors);
6140             this.failureType = Roo.form.Action.SERVER_INVALID;
6141         }
6142         this.form.afterAction(this, false);
6143     },
6144     failure : function(response)
6145     {
6146         this.uploadComplete= true;
6147         if (this.haveProgress) {
6148             Roo.MessageBox.hide();
6149         }
6150         
6151         this.response = response;
6152         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6153         this.form.afterAction(this, false);
6154     },
6155     
6156     handleResponse : function(response){
6157         if(this.form.errorReader){
6158             var rs = this.form.errorReader.read(response);
6159             var errors = [];
6160             if(rs.records){
6161                 for(var i = 0, len = rs.records.length; i < len; i++) {
6162                     var r = rs.records[i];
6163                     errors[i] = r.data;
6164                 }
6165             }
6166             if(errors.length < 1){
6167                 errors = null;
6168             }
6169             return {
6170                 success : rs.success,
6171                 errors : errors
6172             };
6173         }
6174         var ret = false;
6175         try {
6176             ret = Roo.decode(response.responseText);
6177         } catch (e) {
6178             ret = {
6179                 success: false,
6180                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6181                 errors : []
6182             };
6183         }
6184         return ret;
6185         
6186     }
6187 });
6188
6189
6190 Roo.form.Action.Load = function(form, options){
6191     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6192     this.reader = this.form.reader;
6193 };
6194
6195 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6196     type : 'load',
6197
6198     run : function(){
6199         
6200         Roo.Ajax.request(Roo.apply(
6201                 this.createCallback(), {
6202                     method:this.getMethod(),
6203                     url:this.getUrl(false),
6204                     params:this.getParams()
6205         }));
6206     },
6207
6208     success : function(response){
6209         
6210         var result = this.processResponse(response);
6211         if(result === true || !result.success || !result.data){
6212             this.failureType = Roo.form.Action.LOAD_FAILURE;
6213             this.form.afterAction(this, false);
6214             return;
6215         }
6216         this.form.clearInvalid();
6217         this.form.setValues(result.data);
6218         this.form.afterAction(this, true);
6219     },
6220
6221     handleResponse : function(response){
6222         if(this.form.reader){
6223             var rs = this.form.reader.read(response);
6224             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6225             return {
6226                 success : rs.success,
6227                 data : data
6228             };
6229         }
6230         return Roo.decode(response.responseText);
6231     }
6232 });
6233
6234 Roo.form.Action.ACTION_TYPES = {
6235     'load' : Roo.form.Action.Load,
6236     'submit' : Roo.form.Action.Submit
6237 };/*
6238  * - LGPL
6239  *
6240  * form
6241  * 
6242  */
6243
6244 /**
6245  * @class Roo.bootstrap.Form
6246  * @extends Roo.bootstrap.Component
6247  * Bootstrap Form class
6248  * @cfg {String} method  GET | POST (default POST)
6249  * @cfg {String} labelAlign top | left (default top)
6250  * @cfg {String} align left  | right - for navbars
6251  * @cfg {Boolean} loadMask load mask when submit (default true)
6252
6253  * 
6254  * @constructor
6255  * Create a new Form
6256  * @param {Object} config The config object
6257  */
6258
6259
6260 Roo.bootstrap.Form = function(config){
6261     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6262     this.addEvents({
6263         /**
6264          * @event clientvalidation
6265          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6266          * @param {Form} this
6267          * @param {Boolean} valid true if the form has passed client-side validation
6268          */
6269         clientvalidation: true,
6270         /**
6271          * @event beforeaction
6272          * Fires before any action is performed. Return false to cancel the action.
6273          * @param {Form} this
6274          * @param {Action} action The action to be performed
6275          */
6276         beforeaction: true,
6277         /**
6278          * @event actionfailed
6279          * Fires when an action fails.
6280          * @param {Form} this
6281          * @param {Action} action The action that failed
6282          */
6283         actionfailed : true,
6284         /**
6285          * @event actioncomplete
6286          * Fires when an action is completed.
6287          * @param {Form} this
6288          * @param {Action} action The action that completed
6289          */
6290         actioncomplete : true
6291     });
6292     
6293 };
6294
6295 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6296       
6297      /**
6298      * @cfg {String} method
6299      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6300      */
6301     method : 'POST',
6302     /**
6303      * @cfg {String} url
6304      * The URL to use for form actions if one isn't supplied in the action options.
6305      */
6306     /**
6307      * @cfg {Boolean} fileUpload
6308      * Set to true if this form is a file upload.
6309      */
6310      
6311     /**
6312      * @cfg {Object} baseParams
6313      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6314      */
6315       
6316     /**
6317      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6318      */
6319     timeout: 30,
6320     /**
6321      * @cfg {Sting} align (left|right) for navbar forms
6322      */
6323     align : 'left',
6324
6325     // private
6326     activeAction : null,
6327  
6328     /**
6329      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6330      * element by passing it or its id or mask the form itself by passing in true.
6331      * @type Mixed
6332      */
6333     waitMsgTarget : false,
6334     
6335     loadMask : true,
6336     
6337     getAutoCreate : function(){
6338         
6339         var cfg = {
6340             tag: 'form',
6341             method : this.method || 'POST',
6342             id : this.id || Roo.id(),
6343             cls : ''
6344         }
6345         if (this.parent().xtype.match(/^Nav/)) {
6346             cfg.cls = 'navbar-form navbar-' + this.align;
6347             
6348         }
6349         
6350         if (this.labelAlign == 'left' ) {
6351             cfg.cls += ' form-horizontal';
6352         }
6353         
6354         
6355         return cfg;
6356     },
6357     initEvents : function()
6358     {
6359         this.el.on('submit', this.onSubmit, this);
6360         // this was added as random key presses on the form where triggering form submit.
6361         this.el.on('keypress', function(e) {
6362             if (e.getCharCode() != 13) {
6363                 return true;
6364             }
6365             // we might need to allow it for textareas.. and some other items.
6366             // check e.getTarget().
6367             
6368             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6369                 return true;
6370             }
6371         
6372             Roo.log("keypress blocked");
6373             
6374             e.preventDefault();
6375             return false;
6376         });
6377         
6378     },
6379     // private
6380     onSubmit : function(e){
6381         e.stopEvent();
6382     },
6383     
6384      /**
6385      * Returns true if client-side validation on the form is successful.
6386      * @return Boolean
6387      */
6388     isValid : function(){
6389         var items = this.getItems();
6390         var valid = true;
6391         items.each(function(f){
6392            if(!f.validate()){
6393                valid = false;
6394                
6395            }
6396         });
6397         return valid;
6398     },
6399     /**
6400      * Returns true if any fields in this form have changed since their original load.
6401      * @return Boolean
6402      */
6403     isDirty : function(){
6404         var dirty = false;
6405         var items = this.getItems();
6406         items.each(function(f){
6407            if(f.isDirty()){
6408                dirty = true;
6409                return false;
6410            }
6411            return true;
6412         });
6413         return dirty;
6414     },
6415      /**
6416      * Performs a predefined action (submit or load) or custom actions you define on this form.
6417      * @param {String} actionName The name of the action type
6418      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6419      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6420      * accept other config options):
6421      * <pre>
6422 Property          Type             Description
6423 ----------------  ---------------  ----------------------------------------------------------------------------------
6424 url               String           The url for the action (defaults to the form's url)
6425 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6426 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6427 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6428                                    validate the form on the client (defaults to false)
6429      * </pre>
6430      * @return {BasicForm} this
6431      */
6432     doAction : function(action, options){
6433         if(typeof action == 'string'){
6434             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6435         }
6436         if(this.fireEvent('beforeaction', this, action) !== false){
6437             this.beforeAction(action);
6438             action.run.defer(100, action);
6439         }
6440         return this;
6441     },
6442     
6443     // private
6444     beforeAction : function(action){
6445         var o = action.options;
6446         
6447         if(this.loadMask){
6448             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6449         }
6450         // not really supported yet.. ??
6451         
6452         //if(this.waitMsgTarget === true){
6453         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6454         //}else if(this.waitMsgTarget){
6455         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6456         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6457         //}else {
6458         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6459        // }
6460          
6461     },
6462
6463     // private
6464     afterAction : function(action, success){
6465         this.activeAction = null;
6466         var o = action.options;
6467         
6468         //if(this.waitMsgTarget === true){
6469             this.el.unmask();
6470         //}else if(this.waitMsgTarget){
6471         //    this.waitMsgTarget.unmask();
6472         //}else{
6473         //    Roo.MessageBox.updateProgress(1);
6474         //    Roo.MessageBox.hide();
6475        // }
6476         // 
6477         if(success){
6478             if(o.reset){
6479                 this.reset();
6480             }
6481             Roo.callback(o.success, o.scope, [this, action]);
6482             this.fireEvent('actioncomplete', this, action);
6483             
6484         }else{
6485             
6486             // failure condition..
6487             // we have a scenario where updates need confirming.
6488             // eg. if a locking scenario exists..
6489             // we look for { errors : { needs_confirm : true }} in the response.
6490             if (
6491                 (typeof(action.result) != 'undefined')  &&
6492                 (typeof(action.result.errors) != 'undefined')  &&
6493                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6494            ){
6495                 var _t = this;
6496                 Roo.log("not supported yet");
6497                  /*
6498                 
6499                 Roo.MessageBox.confirm(
6500                     "Change requires confirmation",
6501                     action.result.errorMsg,
6502                     function(r) {
6503                         if (r != 'yes') {
6504                             return;
6505                         }
6506                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6507                     }
6508                     
6509                 );
6510                 */
6511                 
6512                 
6513                 return;
6514             }
6515             
6516             Roo.callback(o.failure, o.scope, [this, action]);
6517             // show an error message if no failed handler is set..
6518             if (!this.hasListener('actionfailed')) {
6519                 Roo.log("need to add dialog support");
6520                 /*
6521                 Roo.MessageBox.alert("Error",
6522                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6523                         action.result.errorMsg :
6524                         "Saving Failed, please check your entries or try again"
6525                 );
6526                 */
6527             }
6528             
6529             this.fireEvent('actionfailed', this, action);
6530         }
6531         
6532     },
6533     /**
6534      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6535      * @param {String} id The value to search for
6536      * @return Field
6537      */
6538     findField : function(id){
6539         var items = this.getItems();
6540         var field = items.get(id);
6541         if(!field){
6542              items.each(function(f){
6543                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6544                     field = f;
6545                     return false;
6546                 }
6547                 return true;
6548             });
6549         }
6550         return field || null;
6551     },
6552      /**
6553      * Mark fields in this form invalid in bulk.
6554      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6555      * @return {BasicForm} this
6556      */
6557     markInvalid : function(errors){
6558         if(errors instanceof Array){
6559             for(var i = 0, len = errors.length; i < len; i++){
6560                 var fieldError = errors[i];
6561                 var f = this.findField(fieldError.id);
6562                 if(f){
6563                     f.markInvalid(fieldError.msg);
6564                 }
6565             }
6566         }else{
6567             var field, id;
6568             for(id in errors){
6569                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6570                     field.markInvalid(errors[id]);
6571                 }
6572             }
6573         }
6574         //Roo.each(this.childForms || [], function (f) {
6575         //    f.markInvalid(errors);
6576         //});
6577         
6578         return this;
6579     },
6580
6581     /**
6582      * Set values for fields in this form in bulk.
6583      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6584      * @return {BasicForm} this
6585      */
6586     setValues : function(values){
6587         if(values instanceof Array){ // array of objects
6588             for(var i = 0, len = values.length; i < len; i++){
6589                 var v = values[i];
6590                 var f = this.findField(v.id);
6591                 if(f){
6592                     f.setValue(v.value);
6593                     if(this.trackResetOnLoad){
6594                         f.originalValue = f.getValue();
6595                     }
6596                 }
6597             }
6598         }else{ // object hash
6599             var field, id;
6600             for(id in values){
6601                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6602                     
6603                     if (field.setFromData && 
6604                         field.valueField && 
6605                         field.displayField &&
6606                         // combos' with local stores can 
6607                         // be queried via setValue()
6608                         // to set their value..
6609                         (field.store && !field.store.isLocal)
6610                         ) {
6611                         // it's a combo
6612                         var sd = { };
6613                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6614                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6615                         field.setFromData(sd);
6616                         
6617                     } else {
6618                         field.setValue(values[id]);
6619                     }
6620                     
6621                     
6622                     if(this.trackResetOnLoad){
6623                         field.originalValue = field.getValue();
6624                     }
6625                 }
6626             }
6627         }
6628          
6629         //Roo.each(this.childForms || [], function (f) {
6630         //    f.setValues(values);
6631         //});
6632                 
6633         return this;
6634     },
6635
6636     /**
6637      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6638      * they are returned as an array.
6639      * @param {Boolean} asString
6640      * @return {Object}
6641      */
6642     getValues : function(asString){
6643         //if (this.childForms) {
6644             // copy values from the child forms
6645         //    Roo.each(this.childForms, function (f) {
6646         //        this.setValues(f.getValues());
6647         //    }, this);
6648         //}
6649         
6650         
6651         
6652         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6653         if(asString === true){
6654             return fs;
6655         }
6656         return Roo.urlDecode(fs);
6657     },
6658     
6659     /**
6660      * Returns the fields in this form as an object with key/value pairs. 
6661      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6662      * @return {Object}
6663      */
6664     getFieldValues : function(with_hidden)
6665     {
6666         var items = this.getItems();
6667         var ret = {};
6668         items.each(function(f){
6669             if (!f.getName()) {
6670                 return;
6671             }
6672             var v = f.getValue();
6673             if (f.inputType =='radio') {
6674                 if (typeof(ret[f.getName()]) == 'undefined') {
6675                     ret[f.getName()] = ''; // empty..
6676                 }
6677                 
6678                 if (!f.el.dom.checked) {
6679                     return;
6680                     
6681                 }
6682                 v = f.el.dom.value;
6683                 
6684             }
6685             
6686             // not sure if this supported any more..
6687             if ((typeof(v) == 'object') && f.getRawValue) {
6688                 v = f.getRawValue() ; // dates..
6689             }
6690             // combo boxes where name != hiddenName...
6691             if (f.name != f.getName()) {
6692                 ret[f.name] = f.getRawValue();
6693             }
6694             ret[f.getName()] = v;
6695         });
6696         
6697         return ret;
6698     },
6699
6700     /**
6701      * Clears all invalid messages in this form.
6702      * @return {BasicForm} this
6703      */
6704     clearInvalid : function(){
6705         var items = this.getItems();
6706         
6707         items.each(function(f){
6708            f.clearInvalid();
6709         });
6710         
6711         
6712         
6713         return this;
6714     },
6715
6716     /**
6717      * Resets this form.
6718      * @return {BasicForm} this
6719      */
6720     reset : function(){
6721         var items = this.getItems();
6722         items.each(function(f){
6723             f.reset();
6724         });
6725         
6726         Roo.each(this.childForms || [], function (f) {
6727             f.reset();
6728         });
6729        
6730         
6731         return this;
6732     },
6733     getItems : function()
6734     {
6735         var r=new Roo.util.MixedCollection(false, function(o){
6736             return o.id || (o.id = Roo.id());
6737         });
6738         var iter = function(el) {
6739             if (el.inputEl) {
6740                 r.add(el);
6741             }
6742             if (!el.items) {
6743                 return;
6744             }
6745             Roo.each(el.items,function(e) {
6746                 iter(e);
6747             });
6748             
6749             
6750         };
6751         iter(this);
6752         return r;
6753         
6754         
6755         
6756         
6757     }
6758     
6759 });
6760
6761  
6762 /*
6763  * Based on:
6764  * Ext JS Library 1.1.1
6765  * Copyright(c) 2006-2007, Ext JS, LLC.
6766  *
6767  * Originally Released Under LGPL - original licence link has changed is not relivant.
6768  *
6769  * Fork - LGPL
6770  * <script type="text/javascript">
6771  */
6772 /**
6773  * @class Roo.form.VTypes
6774  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6775  * @singleton
6776  */
6777 Roo.form.VTypes = function(){
6778     // closure these in so they are only created once.
6779     var alpha = /^[a-zA-Z_]+$/;
6780     var alphanum = /^[a-zA-Z0-9_]+$/;
6781     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6782     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6783
6784     // All these messages and functions are configurable
6785     return {
6786         /**
6787          * The function used to validate email addresses
6788          * @param {String} value The email address
6789          */
6790         'email' : function(v){
6791             return email.test(v);
6792         },
6793         /**
6794          * The error text to display when the email validation function returns false
6795          * @type String
6796          */
6797         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6798         /**
6799          * The keystroke filter mask to be applied on email input
6800          * @type RegExp
6801          */
6802         'emailMask' : /[a-z0-9_\.\-@]/i,
6803
6804         /**
6805          * The function used to validate URLs
6806          * @param {String} value The URL
6807          */
6808         'url' : function(v){
6809             return url.test(v);
6810         },
6811         /**
6812          * The error text to display when the url validation function returns false
6813          * @type String
6814          */
6815         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6816         
6817         /**
6818          * The function used to validate alpha values
6819          * @param {String} value The value
6820          */
6821         'alpha' : function(v){
6822             return alpha.test(v);
6823         },
6824         /**
6825          * The error text to display when the alpha validation function returns false
6826          * @type String
6827          */
6828         'alphaText' : 'This field should only contain letters and _',
6829         /**
6830          * The keystroke filter mask to be applied on alpha input
6831          * @type RegExp
6832          */
6833         'alphaMask' : /[a-z_]/i,
6834
6835         /**
6836          * The function used to validate alphanumeric values
6837          * @param {String} value The value
6838          */
6839         'alphanum' : function(v){
6840             return alphanum.test(v);
6841         },
6842         /**
6843          * The error text to display when the alphanumeric validation function returns false
6844          * @type String
6845          */
6846         'alphanumText' : 'This field should only contain letters, numbers and _',
6847         /**
6848          * The keystroke filter mask to be applied on alphanumeric input
6849          * @type RegExp
6850          */
6851         'alphanumMask' : /[a-z0-9_]/i
6852     };
6853 }();/*
6854  * - LGPL
6855  *
6856  * Input
6857  * 
6858  */
6859
6860 /**
6861  * @class Roo.bootstrap.Input
6862  * @extends Roo.bootstrap.Component
6863  * Bootstrap Input class
6864  * @cfg {Boolean} disabled is it disabled
6865  * @cfg {String} fieldLabel - the label associated
6866  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6867  * @cfg {String} name name of the input
6868  * @cfg {string} fieldLabel - the label associated
6869  * @cfg {string}  inputType - input / file submit ...
6870  * @cfg {string} placeholder - placeholder to put in text.
6871  * @cfg {string}  before - input group add on before
6872  * @cfg {string} after - input group add on after
6873  * @cfg {string} size - (lg|sm) or leave empty..
6874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6876  * @cfg {Number} md colspan out of 12 for computer-sized screens
6877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6878  * @cfg {string} value default value of the input
6879  * @cfg {Number} labelWidth set the width of label (0-12)
6880  * @cfg {String} labelAlign (top|left)
6881  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6882  * @cfg {String} align (left|center|right) Default left
6883  * 
6884  * 
6885  * @constructor
6886  * Create a new Input
6887  * @param {Object} config The config object
6888  */
6889
6890 Roo.bootstrap.Input = function(config){
6891     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6892    
6893         this.addEvents({
6894             /**
6895              * @event focus
6896              * Fires when this field receives input focus.
6897              * @param {Roo.form.Field} this
6898              */
6899             focus : true,
6900             /**
6901              * @event blur
6902              * Fires when this field loses input focus.
6903              * @param {Roo.form.Field} this
6904              */
6905             blur : true,
6906             /**
6907              * @event specialkey
6908              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6909              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6910              * @param {Roo.form.Field} this
6911              * @param {Roo.EventObject} e The event object
6912              */
6913             specialkey : true,
6914             /**
6915              * @event change
6916              * Fires just before the field blurs if the field value has changed.
6917              * @param {Roo.form.Field} this
6918              * @param {Mixed} newValue The new value
6919              * @param {Mixed} oldValue The original value
6920              */
6921             change : true,
6922             /**
6923              * @event invalid
6924              * Fires after the field has been marked as invalid.
6925              * @param {Roo.form.Field} this
6926              * @param {String} msg The validation message
6927              */
6928             invalid : true,
6929             /**
6930              * @event valid
6931              * Fires after the field has been validated with no errors.
6932              * @param {Roo.form.Field} this
6933              */
6934             valid : true,
6935              /**
6936              * @event keyup
6937              * Fires after the key up
6938              * @param {Roo.form.Field} this
6939              * @param {Roo.EventObject}  e The event Object
6940              */
6941             keyup : true
6942         });
6943 };
6944
6945 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6946      /**
6947      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6948       automatic validation (defaults to "keyup").
6949      */
6950     validationEvent : "keyup",
6951      /**
6952      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6953      */
6954     validateOnBlur : true,
6955     /**
6956      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6957      */
6958     validationDelay : 250,
6959      /**
6960      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6961      */
6962     focusClass : "x-form-focus",  // not needed???
6963     
6964        
6965     /**
6966      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6967      */
6968     invalidClass : "has-error",
6969     
6970     /**
6971      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6972      */
6973     selectOnFocus : false,
6974     
6975      /**
6976      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6977      */
6978     maskRe : null,
6979        /**
6980      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6981      */
6982     vtype : null,
6983     
6984       /**
6985      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6986      */
6987     disableKeyFilter : false,
6988     
6989        /**
6990      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6991      */
6992     disabled : false,
6993      /**
6994      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6995      */
6996     allowBlank : true,
6997     /**
6998      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6999      */
7000     blankText : "This field is required",
7001     
7002      /**
7003      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7004      */
7005     minLength : 0,
7006     /**
7007      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7008      */
7009     maxLength : Number.MAX_VALUE,
7010     /**
7011      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7012      */
7013     minLengthText : "The minimum length for this field is {0}",
7014     /**
7015      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7016      */
7017     maxLengthText : "The maximum length for this field is {0}",
7018   
7019     
7020     /**
7021      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7022      * If available, this function will be called only after the basic validators all return true, and will be passed the
7023      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7024      */
7025     validator : null,
7026     /**
7027      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7028      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7029      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7030      */
7031     regex : null,
7032     /**
7033      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7034      */
7035     regexText : "",
7036     
7037     
7038     
7039     fieldLabel : '',
7040     inputType : 'text',
7041     
7042     name : false,
7043     placeholder: false,
7044     before : false,
7045     after : false,
7046     size : false,
7047     // private
7048     hasFocus : false,
7049     preventMark: false,
7050     isFormField : true,
7051     value : '',
7052     labelWidth : 2,
7053     labelAlign : false,
7054     readOnly : false,
7055     align : false,
7056     formatedValue : false,
7057     
7058     parentLabelAlign : function()
7059     {
7060         var parent = this;
7061         while (parent.parent()) {
7062             parent = parent.parent();
7063             if (typeof(parent.labelAlign) !='undefined') {
7064                 return parent.labelAlign;
7065             }
7066         }
7067         return 'left';
7068         
7069     },
7070     
7071     getAutoCreate : function(){
7072         
7073         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7074         
7075         var id = Roo.id();
7076         
7077         var cfg = {};
7078         
7079         if(this.inputType != 'hidden'){
7080             cfg.cls = 'form-group' //input-group
7081         }
7082         
7083         var input =  {
7084             tag: 'input',
7085             id : id,
7086             type : this.inputType,
7087             value : this.value,
7088             cls : 'form-control',
7089             placeholder : this.placeholder || ''
7090             
7091         };
7092         
7093         if(this.align){
7094             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7095         }
7096         
7097         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7098             input.maxLength = this.maxLength;
7099         }
7100         
7101         if (this.disabled) {
7102             input.disabled=true;
7103         }
7104         
7105         if (this.readOnly) {
7106             input.readonly=true;
7107         }
7108         
7109         if (this.name) {
7110             input.name = this.name;
7111         }
7112         if (this.size) {
7113             input.cls += ' input-' + this.size;
7114         }
7115         var settings=this;
7116         ['xs','sm','md','lg'].map(function(size){
7117             if (settings[size]) {
7118                 cfg.cls += ' col-' + size + '-' + settings[size];
7119             }
7120         });
7121         
7122         var inputblock = input;
7123         
7124         if (this.before || this.after) {
7125             
7126             inputblock = {
7127                 cls : 'input-group',
7128                 cn :  [] 
7129             };
7130             if (this.before && typeof(this.before) == 'string') {
7131                 
7132                 inputblock.cn.push({
7133                     tag :'span',
7134                     cls : 'roo-input-before input-group-addon',
7135                     html : this.before
7136                 });
7137             }
7138             if (this.before && typeof(this.before) == 'object') {
7139                 this.before = Roo.factory(this.before);
7140                 Roo.log(this.before);
7141                 inputblock.cn.push({
7142                     tag :'span',
7143                     cls : 'roo-input-before input-group-' +
7144                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7145                 });
7146             }
7147             
7148             inputblock.cn.push(input);
7149             
7150             if (this.after && typeof(this.after) == 'string') {
7151                 inputblock.cn.push({
7152                     tag :'span',
7153                     cls : 'roo-input-after input-group-addon',
7154                     html : this.after
7155                 });
7156             }
7157             if (this.after && typeof(this.after) == 'object') {
7158                 this.after = Roo.factory(this.after);
7159                 Roo.log(this.after);
7160                 inputblock.cn.push({
7161                     tag :'span',
7162                     cls : 'roo-input-after input-group-' +
7163                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7164                 });
7165             }
7166         };
7167         
7168         if (align ==='left' && this.fieldLabel.length) {
7169                 Roo.log("left and has label");
7170                 cfg.cn = [
7171                     
7172                     {
7173                         tag: 'label',
7174                         'for' :  id,
7175                         cls : 'control-label col-sm-' + this.labelWidth,
7176                         html : this.fieldLabel
7177                         
7178                     },
7179                     {
7180                         cls : "col-sm-" + (12 - this.labelWidth), 
7181                         cn: [
7182                             inputblock
7183                         ]
7184                     }
7185                     
7186                 ];
7187         } else if ( this.fieldLabel.length) {
7188                 Roo.log(" label");
7189                  cfg.cn = [
7190                    
7191                     {
7192                         tag: 'label',
7193                         //cls : 'input-group-addon',
7194                         html : this.fieldLabel
7195                         
7196                     },
7197                     
7198                     inputblock
7199                     
7200                 ];
7201
7202         } else {
7203             
7204                 Roo.log(" no label && no align");
7205                 cfg.cn = [
7206                     
7207                         inputblock
7208                     
7209                 ];
7210                 
7211                 
7212         };
7213         Roo.log('input-parentType: ' + this.parentType);
7214         
7215         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7216            cfg.cls += ' navbar-form';
7217            Roo.log(cfg);
7218         }
7219         
7220         return cfg;
7221         
7222     },
7223     /**
7224      * return the real input element.
7225      */
7226     inputEl: function ()
7227     {
7228         return this.el.select('input.form-control',true).first();
7229     },
7230     
7231     tooltipEl : function()
7232     {
7233         return this.inputEl();
7234     },
7235     
7236     setDisabled : function(v)
7237     {
7238         var i  = this.inputEl().dom;
7239         if (!v) {
7240             i.removeAttribute('disabled');
7241             return;
7242             
7243         }
7244         i.setAttribute('disabled','true');
7245     },
7246     initEvents : function()
7247     {
7248           
7249         this.inputEl().on("keydown" , this.fireKey,  this);
7250         this.inputEl().on("focus", this.onFocus,  this);
7251         this.inputEl().on("blur", this.onBlur,  this);
7252         
7253         this.inputEl().relayEvent('keyup', this);
7254
7255         // reference to original value for reset
7256         this.originalValue = this.getValue();
7257         //Roo.form.TextField.superclass.initEvents.call(this);
7258         if(this.validationEvent == 'keyup'){
7259             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7260             this.inputEl().on('keyup', this.filterValidation, this);
7261         }
7262         else if(this.validationEvent !== false){
7263             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7264         }
7265         
7266         if(this.selectOnFocus){
7267             this.on("focus", this.preFocus, this);
7268             
7269         }
7270         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7271             this.inputEl().on("keypress", this.filterKeys, this);
7272         }
7273        /* if(this.grow){
7274             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7275             this.el.on("click", this.autoSize,  this);
7276         }
7277         */
7278         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7279             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7280         }
7281         
7282         if (typeof(this.before) == 'object') {
7283             this.before.render(this.el.select('.roo-input-before',true).first());
7284         }
7285         if (typeof(this.after) == 'object') {
7286             this.after.render(this.el.select('.roo-input-after',true).first());
7287         }
7288         
7289         
7290     },
7291     filterValidation : function(e){
7292         if(!e.isNavKeyPress()){
7293             this.validationTask.delay(this.validationDelay);
7294         }
7295     },
7296      /**
7297      * Validates the field value
7298      * @return {Boolean} True if the value is valid, else false
7299      */
7300     validate : function(){
7301         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7302         if(this.disabled || this.validateValue(this.getRawValue())){
7303             this.clearInvalid();
7304             return true;
7305         }
7306         return false;
7307     },
7308     
7309     
7310     /**
7311      * Validates a value according to the field's validation rules and marks the field as invalid
7312      * if the validation fails
7313      * @param {Mixed} value The value to validate
7314      * @return {Boolean} True if the value is valid, else false
7315      */
7316     validateValue : function(value){
7317         if(value.length < 1)  { // if it's blank
7318              if(this.allowBlank){
7319                 this.clearInvalid();
7320                 return true;
7321              }else{
7322                 this.markInvalid(this.blankText);
7323                 return false;
7324              }
7325         }
7326         if(value.length < this.minLength){
7327             this.markInvalid(String.format(this.minLengthText, this.minLength));
7328             return false;
7329         }
7330         if(value.length > this.maxLength){
7331             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7332             return false;
7333         }
7334         if(this.vtype){
7335             var vt = Roo.form.VTypes;
7336             if(!vt[this.vtype](value, this)){
7337                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7338                 return false;
7339             }
7340         }
7341         if(typeof this.validator == "function"){
7342             var msg = this.validator(value);
7343             if(msg !== true){
7344                 this.markInvalid(msg);
7345                 return false;
7346             }
7347         }
7348         if(this.regex && !this.regex.test(value)){
7349             this.markInvalid(this.regexText);
7350             return false;
7351         }
7352         return true;
7353     },
7354
7355     
7356     
7357      // private
7358     fireKey : function(e){
7359         //Roo.log('field ' + e.getKey());
7360         if(e.isNavKeyPress()){
7361             this.fireEvent("specialkey", this, e);
7362         }
7363     },
7364     focus : function (selectText){
7365         if(this.rendered){
7366             this.inputEl().focus();
7367             if(selectText === true){
7368                 this.inputEl().dom.select();
7369             }
7370         }
7371         return this;
7372     } ,
7373     
7374     onFocus : function(){
7375         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7376            // this.el.addClass(this.focusClass);
7377         }
7378         if(!this.hasFocus){
7379             this.hasFocus = true;
7380             this.startValue = this.getValue();
7381             this.fireEvent("focus", this);
7382         }
7383     },
7384     
7385     beforeBlur : Roo.emptyFn,
7386
7387     
7388     // private
7389     onBlur : function(){
7390         this.beforeBlur();
7391         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7392             //this.el.removeClass(this.focusClass);
7393         }
7394         this.hasFocus = false;
7395         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7396             this.validate();
7397         }
7398         var v = this.getValue();
7399         if(String(v) !== String(this.startValue)){
7400             this.fireEvent('change', this, v, this.startValue);
7401         }
7402         this.fireEvent("blur", this);
7403     },
7404     
7405     /**
7406      * Resets the current field value to the originally loaded value and clears any validation messages
7407      */
7408     reset : function(){
7409         this.setValue(this.originalValue);
7410         this.clearInvalid();
7411     },
7412      /**
7413      * Returns the name of the field
7414      * @return {Mixed} name The name field
7415      */
7416     getName: function(){
7417         return this.name;
7418     },
7419      /**
7420      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7421      * @return {Mixed} value The field value
7422      */
7423     getValue : function(){
7424         
7425         var v = this.inputEl().getValue();
7426         
7427         return v;
7428     },
7429     /**
7430      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7431      * @return {Mixed} value The field value
7432      */
7433     getRawValue : function(){
7434         var v = this.inputEl().getValue();
7435         
7436         return v;
7437     },
7438     
7439     /**
7440      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7441      * @param {Mixed} value The value to set
7442      */
7443     setRawValue : function(v){
7444         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7445     },
7446     
7447     selectText : function(start, end){
7448         var v = this.getRawValue();
7449         if(v.length > 0){
7450             start = start === undefined ? 0 : start;
7451             end = end === undefined ? v.length : end;
7452             var d = this.inputEl().dom;
7453             if(d.setSelectionRange){
7454                 d.setSelectionRange(start, end);
7455             }else if(d.createTextRange){
7456                 var range = d.createTextRange();
7457                 range.moveStart("character", start);
7458                 range.moveEnd("character", v.length-end);
7459                 range.select();
7460             }
7461         }
7462     },
7463     
7464     /**
7465      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7466      * @param {Mixed} value The value to set
7467      */
7468     setValue : function(v){
7469         this.value = v;
7470         if(this.rendered){
7471             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7472             this.validate();
7473         }
7474     },
7475     
7476     /*
7477     processValue : function(value){
7478         if(this.stripCharsRe){
7479             var newValue = value.replace(this.stripCharsRe, '');
7480             if(newValue !== value){
7481                 this.setRawValue(newValue);
7482                 return newValue;
7483             }
7484         }
7485         return value;
7486     },
7487   */
7488     preFocus : function(){
7489         
7490         if(this.selectOnFocus){
7491             this.inputEl().dom.select();
7492         }
7493     },
7494     filterKeys : function(e){
7495         var k = e.getKey();
7496         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7497             return;
7498         }
7499         var c = e.getCharCode(), cc = String.fromCharCode(c);
7500         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7501             return;
7502         }
7503         if(!this.maskRe.test(cc)){
7504             e.stopEvent();
7505         }
7506     },
7507      /**
7508      * Clear any invalid styles/messages for this field
7509      */
7510     clearInvalid : function(){
7511         
7512         if(!this.el || this.preventMark){ // not rendered
7513             return;
7514         }
7515         this.el.removeClass(this.invalidClass);
7516         /*
7517         switch(this.msgTarget){
7518             case 'qtip':
7519                 this.el.dom.qtip = '';
7520                 break;
7521             case 'title':
7522                 this.el.dom.title = '';
7523                 break;
7524             case 'under':
7525                 if(this.errorEl){
7526                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7527                 }
7528                 break;
7529             case 'side':
7530                 if(this.errorIcon){
7531                     this.errorIcon.dom.qtip = '';
7532                     this.errorIcon.hide();
7533                     this.un('resize', this.alignErrorIcon, this);
7534                 }
7535                 break;
7536             default:
7537                 var t = Roo.getDom(this.msgTarget);
7538                 t.innerHTML = '';
7539                 t.style.display = 'none';
7540                 break;
7541         }
7542         */
7543         this.fireEvent('valid', this);
7544     },
7545      /**
7546      * Mark this field as invalid
7547      * @param {String} msg The validation message
7548      */
7549     markInvalid : function(msg){
7550         if(!this.el  || this.preventMark){ // not rendered
7551             return;
7552         }
7553         this.el.addClass(this.invalidClass);
7554         /*
7555         msg = msg || this.invalidText;
7556         switch(this.msgTarget){
7557             case 'qtip':
7558                 this.el.dom.qtip = msg;
7559                 this.el.dom.qclass = 'x-form-invalid-tip';
7560                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7561                     Roo.QuickTips.enable();
7562                 }
7563                 break;
7564             case 'title':
7565                 this.el.dom.title = msg;
7566                 break;
7567             case 'under':
7568                 if(!this.errorEl){
7569                     var elp = this.el.findParent('.x-form-element', 5, true);
7570                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7571                     this.errorEl.setWidth(elp.getWidth(true)-20);
7572                 }
7573                 this.errorEl.update(msg);
7574                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7575                 break;
7576             case 'side':
7577                 if(!this.errorIcon){
7578                     var elp = this.el.findParent('.x-form-element', 5, true);
7579                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7580                 }
7581                 this.alignErrorIcon();
7582                 this.errorIcon.dom.qtip = msg;
7583                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7584                 this.errorIcon.show();
7585                 this.on('resize', this.alignErrorIcon, this);
7586                 break;
7587             default:
7588                 var t = Roo.getDom(this.msgTarget);
7589                 t.innerHTML = msg;
7590                 t.style.display = this.msgDisplay;
7591                 break;
7592         }
7593         */
7594         this.fireEvent('invalid', this, msg);
7595     },
7596     // private
7597     SafariOnKeyDown : function(event)
7598     {
7599         // this is a workaround for a password hang bug on chrome/ webkit.
7600         
7601         var isSelectAll = false;
7602         
7603         if(this.inputEl().dom.selectionEnd > 0){
7604             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7605         }
7606         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7607             event.preventDefault();
7608             this.setValue('');
7609             return;
7610         }
7611         
7612         if(isSelectAll){ // backspace and delete key
7613             
7614             event.preventDefault();
7615             // this is very hacky as keydown always get's upper case.
7616             //
7617             var cc = String.fromCharCode(event.getCharCode());
7618             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7619             
7620         }
7621     },
7622     adjustWidth : function(tag, w){
7623         tag = tag.toLowerCase();
7624         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7625             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7626                 if(tag == 'input'){
7627                     return w + 2;
7628                 }
7629                 if(tag == 'textarea'){
7630                     return w-2;
7631                 }
7632             }else if(Roo.isOpera){
7633                 if(tag == 'input'){
7634                     return w + 2;
7635                 }
7636                 if(tag == 'textarea'){
7637                     return w-2;
7638                 }
7639             }
7640         }
7641         return w;
7642     }
7643     
7644 });
7645
7646  
7647 /*
7648  * - LGPL
7649  *
7650  * Input
7651  * 
7652  */
7653
7654 /**
7655  * @class Roo.bootstrap.TextArea
7656  * @extends Roo.bootstrap.Input
7657  * Bootstrap TextArea class
7658  * @cfg {Number} cols Specifies the visible width of a text area
7659  * @cfg {Number} rows Specifies the visible number of lines in a text area
7660  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7661  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7662  * @cfg {string} html text
7663  * 
7664  * @constructor
7665  * Create a new TextArea
7666  * @param {Object} config The config object
7667  */
7668
7669 Roo.bootstrap.TextArea = function(config){
7670     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7671    
7672 };
7673
7674 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7675      
7676     cols : false,
7677     rows : 5,
7678     readOnly : false,
7679     warp : 'soft',
7680     resize : false,
7681     value: false,
7682     html: false,
7683     
7684     getAutoCreate : function(){
7685         
7686         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7687         
7688         var id = Roo.id();
7689         
7690         var cfg = {};
7691         
7692         var input =  {
7693             tag: 'textarea',
7694             id : id,
7695             warp : this.warp,
7696             rows : this.rows,
7697             value : this.value || '',
7698             html: this.html || '',
7699             cls : 'form-control',
7700             placeholder : this.placeholder || '' 
7701             
7702         };
7703         
7704         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7705             input.maxLength = this.maxLength;
7706         }
7707         
7708         if(this.resize){
7709             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7710         }
7711         
7712         if(this.cols){
7713             input.cols = this.cols;
7714         }
7715         
7716         if (this.readOnly) {
7717             input.readonly = true;
7718         }
7719         
7720         if (this.name) {
7721             input.name = this.name;
7722         }
7723         
7724         if (this.size) {
7725             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7726         }
7727         
7728         var settings=this;
7729         ['xs','sm','md','lg'].map(function(size){
7730             if (settings[size]) {
7731                 cfg.cls += ' col-' + size + '-' + settings[size];
7732             }
7733         });
7734         
7735         var inputblock = input;
7736         
7737         if (this.before || this.after) {
7738             
7739             inputblock = {
7740                 cls : 'input-group',
7741                 cn :  [] 
7742             };
7743             if (this.before) {
7744                 inputblock.cn.push({
7745                     tag :'span',
7746                     cls : 'input-group-addon',
7747                     html : this.before
7748                 });
7749             }
7750             inputblock.cn.push(input);
7751             if (this.after) {
7752                 inputblock.cn.push({
7753                     tag :'span',
7754                     cls : 'input-group-addon',
7755                     html : this.after
7756                 });
7757             }
7758             
7759         }
7760         
7761         if (align ==='left' && this.fieldLabel.length) {
7762                 Roo.log("left and has label");
7763                 cfg.cn = [
7764                     
7765                     {
7766                         tag: 'label',
7767                         'for' :  id,
7768                         cls : 'control-label col-sm-' + this.labelWidth,
7769                         html : this.fieldLabel
7770                         
7771                     },
7772                     {
7773                         cls : "col-sm-" + (12 - this.labelWidth), 
7774                         cn: [
7775                             inputblock
7776                         ]
7777                     }
7778                     
7779                 ];
7780         } else if ( this.fieldLabel.length) {
7781                 Roo.log(" label");
7782                  cfg.cn = [
7783                    
7784                     {
7785                         tag: 'label',
7786                         //cls : 'input-group-addon',
7787                         html : this.fieldLabel
7788                         
7789                     },
7790                     
7791                     inputblock
7792                     
7793                 ];
7794
7795         } else {
7796             
7797                    Roo.log(" no label && no align");
7798                 cfg.cn = [
7799                     
7800                         inputblock
7801                     
7802                 ];
7803                 
7804                 
7805         }
7806         
7807         if (this.disabled) {
7808             input.disabled=true;
7809         }
7810         
7811         return cfg;
7812         
7813     },
7814     /**
7815      * return the real textarea element.
7816      */
7817     inputEl: function ()
7818     {
7819         return this.el.select('textarea.form-control',true).first();
7820     }
7821 });
7822
7823  
7824 /*
7825  * - LGPL
7826  *
7827  * trigger field - base class for combo..
7828  * 
7829  */
7830  
7831 /**
7832  * @class Roo.bootstrap.TriggerField
7833  * @extends Roo.bootstrap.Input
7834  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7835  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7836  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7837  * for which you can provide a custom implementation.  For example:
7838  * <pre><code>
7839 var trigger = new Roo.bootstrap.TriggerField();
7840 trigger.onTriggerClick = myTriggerFn;
7841 trigger.applyTo('my-field');
7842 </code></pre>
7843  *
7844  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7845  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7846  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7847  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7848  * @constructor
7849  * Create a new TriggerField.
7850  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7851  * to the base TextField)
7852  */
7853 Roo.bootstrap.TriggerField = function(config){
7854     this.mimicing = false;
7855     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7856 };
7857
7858 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7859     /**
7860      * @cfg {String} triggerClass A CSS class to apply to the trigger
7861      */
7862      /**
7863      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7864      */
7865     hideTrigger:false,
7866
7867     /** @cfg {Boolean} grow @hide */
7868     /** @cfg {Number} growMin @hide */
7869     /** @cfg {Number} growMax @hide */
7870
7871     /**
7872      * @hide 
7873      * @method
7874      */
7875     autoSize: Roo.emptyFn,
7876     // private
7877     monitorTab : true,
7878     // private
7879     deferHeight : true,
7880
7881     
7882     actionMode : 'wrap',
7883     
7884     
7885     
7886     getAutoCreate : function(){
7887        
7888         var align = this.labelAlign || this.parentLabelAlign();
7889         
7890         var id = Roo.id();
7891         
7892         var cfg = {
7893             cls: 'form-group' //input-group
7894         };
7895         
7896         
7897         var input =  {
7898             tag: 'input',
7899             id : id,
7900             type : this.inputType,
7901             cls : 'form-control',
7902             autocomplete: 'off',
7903             placeholder : this.placeholder || '' 
7904             
7905         };
7906         if (this.name) {
7907             input.name = this.name;
7908         }
7909         if (this.size) {
7910             input.cls += ' input-' + this.size;
7911         }
7912         
7913         if (this.disabled) {
7914             input.disabled=true;
7915         }
7916         
7917         var inputblock = input;
7918         
7919         if (this.before || this.after) {
7920             
7921             inputblock = {
7922                 cls : 'input-group',
7923                 cn :  [] 
7924             };
7925             if (this.before) {
7926                 inputblock.cn.push({
7927                     tag :'span',
7928                     cls : 'input-group-addon',
7929                     html : this.before
7930                 });
7931             }
7932             inputblock.cn.push(input);
7933             if (this.after) {
7934                 inputblock.cn.push({
7935                     tag :'span',
7936                     cls : 'input-group-addon',
7937                     html : this.after
7938                 });
7939             }
7940             
7941         };
7942         
7943         var box = {
7944             tag: 'div',
7945             cn: [
7946                 {
7947                     tag: 'input',
7948                     type : 'hidden',
7949                     cls: 'form-hidden-field'
7950                 },
7951                 inputblock
7952             ]
7953             
7954         };
7955         
7956         if(this.multiple){
7957             Roo.log('multiple');
7958             
7959             box = {
7960                 tag: 'div',
7961                 cn: [
7962                     {
7963                         tag: 'input',
7964                         type : 'hidden',
7965                         cls: 'form-hidden-field'
7966                     },
7967                     {
7968                         tag: 'ul',
7969                         cls: 'select2-choices',
7970                         cn:[
7971                             {
7972                                 tag: 'li',
7973                                 cls: 'select2-search-field',
7974                                 cn: [
7975
7976                                     inputblock
7977                                 ]
7978                             }
7979                         ]
7980                     }
7981                 ]
7982             }
7983         };
7984         
7985         var combobox = {
7986             cls: 'select2-container input-group',
7987             cn: [
7988                 box
7989 //                {
7990 //                    tag: 'ul',
7991 //                    cls: 'typeahead typeahead-long dropdown-menu',
7992 //                    style: 'display:none'
7993 //                }
7994             ]
7995         };
7996         
7997         if(!this.multiple && this.showToggleBtn){
7998             combobox.cn.push({
7999                 tag :'span',
8000                 cls : 'input-group-addon btn dropdown-toggle',
8001                 cn : [
8002                     {
8003                         tag: 'span',
8004                         cls: 'caret'
8005                     },
8006                     {
8007                         tag: 'span',
8008                         cls: 'combobox-clear',
8009                         cn  : [
8010                             {
8011                                 tag : 'i',
8012                                 cls: 'icon-remove'
8013                             }
8014                         ]
8015                     }
8016                 ]
8017
8018             })
8019         }
8020         
8021         if(this.multiple){
8022             combobox.cls += ' select2-container-multi';
8023         }
8024         
8025         if (align ==='left' && this.fieldLabel.length) {
8026             
8027                 Roo.log("left and has label");
8028                 cfg.cn = [
8029                     
8030                     {
8031                         tag: 'label',
8032                         'for' :  id,
8033                         cls : 'control-label col-sm-' + this.labelWidth,
8034                         html : this.fieldLabel
8035                         
8036                     },
8037                     {
8038                         cls : "col-sm-" + (12 - this.labelWidth), 
8039                         cn: [
8040                             combobox
8041                         ]
8042                     }
8043                     
8044                 ];
8045         } else if ( this.fieldLabel.length) {
8046                 Roo.log(" label");
8047                  cfg.cn = [
8048                    
8049                     {
8050                         tag: 'label',
8051                         //cls : 'input-group-addon',
8052                         html : this.fieldLabel
8053                         
8054                     },
8055                     
8056                     combobox
8057                     
8058                 ];
8059
8060         } else {
8061             
8062                 Roo.log(" no label && no align");
8063                 cfg = combobox
8064                      
8065                 
8066         }
8067          
8068         var settings=this;
8069         ['xs','sm','md','lg'].map(function(size){
8070             if (settings[size]) {
8071                 cfg.cls += ' col-' + size + '-' + settings[size];
8072             }
8073         });
8074         
8075         return cfg;
8076         
8077     },
8078     
8079     
8080     
8081     // private
8082     onResize : function(w, h){
8083 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8084 //        if(typeof w == 'number'){
8085 //            var x = w - this.trigger.getWidth();
8086 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8087 //            this.trigger.setStyle('left', x+'px');
8088 //        }
8089     },
8090
8091     // private
8092     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8093
8094     // private
8095     getResizeEl : function(){
8096         return this.inputEl();
8097     },
8098
8099     // private
8100     getPositionEl : function(){
8101         return this.inputEl();
8102     },
8103
8104     // private
8105     alignErrorIcon : function(){
8106         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8107     },
8108
8109     // private
8110     initEvents : function(){
8111         
8112         this.createList();
8113         
8114         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8115         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8116         if(!this.multiple && this.showToggleBtn){
8117             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8118             if(this.hideTrigger){
8119                 this.trigger.setDisplayed(false);
8120             }
8121             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8122         }
8123         
8124         if(this.multiple){
8125             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8126         }
8127         
8128         //this.trigger.addClassOnOver('x-form-trigger-over');
8129         //this.trigger.addClassOnClick('x-form-trigger-click');
8130         
8131         //if(!this.width){
8132         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8133         //}
8134     },
8135     
8136     createList : function()
8137     {
8138         this.list = Roo.get(document.body).createChild({
8139             tag: 'ul',
8140             cls: 'typeahead typeahead-long dropdown-menu',
8141             style: 'display:none'
8142         });
8143         
8144         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8145         
8146     },
8147
8148     // private
8149     initTrigger : function(){
8150        
8151     },
8152
8153     // private
8154     onDestroy : function(){
8155         if(this.trigger){
8156             this.trigger.removeAllListeners();
8157           //  this.trigger.remove();
8158         }
8159         //if(this.wrap){
8160         //    this.wrap.remove();
8161         //}
8162         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8163     },
8164
8165     // private
8166     onFocus : function(){
8167         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8168         /*
8169         if(!this.mimicing){
8170             this.wrap.addClass('x-trigger-wrap-focus');
8171             this.mimicing = true;
8172             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8173             if(this.monitorTab){
8174                 this.el.on("keydown", this.checkTab, this);
8175             }
8176         }
8177         */
8178     },
8179
8180     // private
8181     checkTab : function(e){
8182         if(e.getKey() == e.TAB){
8183             this.triggerBlur();
8184         }
8185     },
8186
8187     // private
8188     onBlur : function(){
8189         // do nothing
8190     },
8191
8192     // private
8193     mimicBlur : function(e, t){
8194         /*
8195         if(!this.wrap.contains(t) && this.validateBlur()){
8196             this.triggerBlur();
8197         }
8198         */
8199     },
8200
8201     // private
8202     triggerBlur : function(){
8203         this.mimicing = false;
8204         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8205         if(this.monitorTab){
8206             this.el.un("keydown", this.checkTab, this);
8207         }
8208         //this.wrap.removeClass('x-trigger-wrap-focus');
8209         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8210     },
8211
8212     // private
8213     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8214     validateBlur : function(e, t){
8215         return true;
8216     },
8217
8218     // private
8219     onDisable : function(){
8220         this.inputEl().dom.disabled = true;
8221         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8222         //if(this.wrap){
8223         //    this.wrap.addClass('x-item-disabled');
8224         //}
8225     },
8226
8227     // private
8228     onEnable : function(){
8229         this.inputEl().dom.disabled = false;
8230         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8231         //if(this.wrap){
8232         //    this.el.removeClass('x-item-disabled');
8233         //}
8234     },
8235
8236     // private
8237     onShow : function(){
8238         var ae = this.getActionEl();
8239         
8240         if(ae){
8241             ae.dom.style.display = '';
8242             ae.dom.style.visibility = 'visible';
8243         }
8244     },
8245
8246     // private
8247     
8248     onHide : function(){
8249         var ae = this.getActionEl();
8250         ae.dom.style.display = 'none';
8251     },
8252
8253     /**
8254      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8255      * by an implementing function.
8256      * @method
8257      * @param {EventObject} e
8258      */
8259     onTriggerClick : Roo.emptyFn
8260 });
8261  /*
8262  * Based on:
8263  * Ext JS Library 1.1.1
8264  * Copyright(c) 2006-2007, Ext JS, LLC.
8265  *
8266  * Originally Released Under LGPL - original licence link has changed is not relivant.
8267  *
8268  * Fork - LGPL
8269  * <script type="text/javascript">
8270  */
8271
8272
8273 /**
8274  * @class Roo.data.SortTypes
8275  * @singleton
8276  * Defines the default sorting (casting?) comparison functions used when sorting data.
8277  */
8278 Roo.data.SortTypes = {
8279     /**
8280      * Default sort that does nothing
8281      * @param {Mixed} s The value being converted
8282      * @return {Mixed} The comparison value
8283      */
8284     none : function(s){
8285         return s;
8286     },
8287     
8288     /**
8289      * The regular expression used to strip tags
8290      * @type {RegExp}
8291      * @property
8292      */
8293     stripTagsRE : /<\/?[^>]+>/gi,
8294     
8295     /**
8296      * Strips all HTML tags to sort on text only
8297      * @param {Mixed} s The value being converted
8298      * @return {String} The comparison value
8299      */
8300     asText : function(s){
8301         return String(s).replace(this.stripTagsRE, "");
8302     },
8303     
8304     /**
8305      * Strips all HTML tags to sort on text only - Case insensitive
8306      * @param {Mixed} s The value being converted
8307      * @return {String} The comparison value
8308      */
8309     asUCText : function(s){
8310         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8311     },
8312     
8313     /**
8314      * Case insensitive string
8315      * @param {Mixed} s The value being converted
8316      * @return {String} The comparison value
8317      */
8318     asUCString : function(s) {
8319         return String(s).toUpperCase();
8320     },
8321     
8322     /**
8323      * Date sorting
8324      * @param {Mixed} s The value being converted
8325      * @return {Number} The comparison value
8326      */
8327     asDate : function(s) {
8328         if(!s){
8329             return 0;
8330         }
8331         if(s instanceof Date){
8332             return s.getTime();
8333         }
8334         return Date.parse(String(s));
8335     },
8336     
8337     /**
8338      * Float sorting
8339      * @param {Mixed} s The value being converted
8340      * @return {Float} The comparison value
8341      */
8342     asFloat : function(s) {
8343         var val = parseFloat(String(s).replace(/,/g, ""));
8344         if(isNaN(val)) val = 0;
8345         return val;
8346     },
8347     
8348     /**
8349      * Integer sorting
8350      * @param {Mixed} s The value being converted
8351      * @return {Number} The comparison value
8352      */
8353     asInt : function(s) {
8354         var val = parseInt(String(s).replace(/,/g, ""));
8355         if(isNaN(val)) val = 0;
8356         return val;
8357     }
8358 };/*
8359  * Based on:
8360  * Ext JS Library 1.1.1
8361  * Copyright(c) 2006-2007, Ext JS, LLC.
8362  *
8363  * Originally Released Under LGPL - original licence link has changed is not relivant.
8364  *
8365  * Fork - LGPL
8366  * <script type="text/javascript">
8367  */
8368
8369 /**
8370 * @class Roo.data.Record
8371  * Instances of this class encapsulate both record <em>definition</em> information, and record
8372  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8373  * to access Records cached in an {@link Roo.data.Store} object.<br>
8374  * <p>
8375  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8376  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8377  * objects.<br>
8378  * <p>
8379  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8380  * @constructor
8381  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8382  * {@link #create}. The parameters are the same.
8383  * @param {Array} data An associative Array of data values keyed by the field name.
8384  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8385  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8386  * not specified an integer id is generated.
8387  */
8388 Roo.data.Record = function(data, id){
8389     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8390     this.data = data;
8391 };
8392
8393 /**
8394  * Generate a constructor for a specific record layout.
8395  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8396  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8397  * Each field definition object may contain the following properties: <ul>
8398  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
8399  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8400  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8401  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8402  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8403  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8404  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8405  * this may be omitted.</p></li>
8406  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8407  * <ul><li>auto (Default, implies no conversion)</li>
8408  * <li>string</li>
8409  * <li>int</li>
8410  * <li>float</li>
8411  * <li>boolean</li>
8412  * <li>date</li></ul></p></li>
8413  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8414  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8415  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8416  * by the Reader into an object that will be stored in the Record. It is passed the
8417  * following parameters:<ul>
8418  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8419  * </ul></p></li>
8420  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8421  * </ul>
8422  * <br>usage:<br><pre><code>
8423 var TopicRecord = Roo.data.Record.create(
8424     {name: 'title', mapping: 'topic_title'},
8425     {name: 'author', mapping: 'username'},
8426     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8427     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8428     {name: 'lastPoster', mapping: 'user2'},
8429     {name: 'excerpt', mapping: 'post_text'}
8430 );
8431
8432 var myNewRecord = new TopicRecord({
8433     title: 'Do my job please',
8434     author: 'noobie',
8435     totalPosts: 1,
8436     lastPost: new Date(),
8437     lastPoster: 'Animal',
8438     excerpt: 'No way dude!'
8439 });
8440 myStore.add(myNewRecord);
8441 </code></pre>
8442  * @method create
8443  * @static
8444  */
8445 Roo.data.Record.create = function(o){
8446     var f = function(){
8447         f.superclass.constructor.apply(this, arguments);
8448     };
8449     Roo.extend(f, Roo.data.Record);
8450     var p = f.prototype;
8451     p.fields = new Roo.util.MixedCollection(false, function(field){
8452         return field.name;
8453     });
8454     for(var i = 0, len = o.length; i < len; i++){
8455         p.fields.add(new Roo.data.Field(o[i]));
8456     }
8457     f.getField = function(name){
8458         return p.fields.get(name);  
8459     };
8460     return f;
8461 };
8462
8463 Roo.data.Record.AUTO_ID = 1000;
8464 Roo.data.Record.EDIT = 'edit';
8465 Roo.data.Record.REJECT = 'reject';
8466 Roo.data.Record.COMMIT = 'commit';
8467
8468 Roo.data.Record.prototype = {
8469     /**
8470      * Readonly flag - true if this record has been modified.
8471      * @type Boolean
8472      */
8473     dirty : false,
8474     editing : false,
8475     error: null,
8476     modified: null,
8477
8478     // private
8479     join : function(store){
8480         this.store = store;
8481     },
8482
8483     /**
8484      * Set the named field to the specified value.
8485      * @param {String} name The name of the field to set.
8486      * @param {Object} value The value to set the field to.
8487      */
8488     set : function(name, value){
8489         if(this.data[name] == value){
8490             return;
8491         }
8492         this.dirty = true;
8493         if(!this.modified){
8494             this.modified = {};
8495         }
8496         if(typeof this.modified[name] == 'undefined'){
8497             this.modified[name] = this.data[name];
8498         }
8499         this.data[name] = value;
8500         if(!this.editing && this.store){
8501             this.store.afterEdit(this);
8502         }       
8503     },
8504
8505     /**
8506      * Get the value of the named field.
8507      * @param {String} name The name of the field to get the value of.
8508      * @return {Object} The value of the field.
8509      */
8510     get : function(name){
8511         return this.data[name]; 
8512     },
8513
8514     // private
8515     beginEdit : function(){
8516         this.editing = true;
8517         this.modified = {}; 
8518     },
8519
8520     // private
8521     cancelEdit : function(){
8522         this.editing = false;
8523         delete this.modified;
8524     },
8525
8526     // private
8527     endEdit : function(){
8528         this.editing = false;
8529         if(this.dirty && this.store){
8530             this.store.afterEdit(this);
8531         }
8532     },
8533
8534     /**
8535      * Usually called by the {@link Roo.data.Store} which owns the Record.
8536      * Rejects all changes made to the Record since either creation, or the last commit operation.
8537      * Modified fields are reverted to their original values.
8538      * <p>
8539      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8540      * of reject operations.
8541      */
8542     reject : function(){
8543         var m = this.modified;
8544         for(var n in m){
8545             if(typeof m[n] != "function"){
8546                 this.data[n] = m[n];
8547             }
8548         }
8549         this.dirty = false;
8550         delete this.modified;
8551         this.editing = false;
8552         if(this.store){
8553             this.store.afterReject(this);
8554         }
8555     },
8556
8557     /**
8558      * Usually called by the {@link Roo.data.Store} which owns the Record.
8559      * Commits all changes made to the Record since either creation, or the last commit operation.
8560      * <p>
8561      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8562      * of commit operations.
8563      */
8564     commit : function(){
8565         this.dirty = false;
8566         delete this.modified;
8567         this.editing = false;
8568         if(this.store){
8569             this.store.afterCommit(this);
8570         }
8571     },
8572
8573     // private
8574     hasError : function(){
8575         return this.error != null;
8576     },
8577
8578     // private
8579     clearError : function(){
8580         this.error = null;
8581     },
8582
8583     /**
8584      * Creates a copy of this record.
8585      * @param {String} id (optional) A new record id if you don't want to use this record's id
8586      * @return {Record}
8587      */
8588     copy : function(newId) {
8589         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8590     }
8591 };/*
8592  * Based on:
8593  * Ext JS Library 1.1.1
8594  * Copyright(c) 2006-2007, Ext JS, LLC.
8595  *
8596  * Originally Released Under LGPL - original licence link has changed is not relivant.
8597  *
8598  * Fork - LGPL
8599  * <script type="text/javascript">
8600  */
8601
8602
8603
8604 /**
8605  * @class Roo.data.Store
8606  * @extends Roo.util.Observable
8607  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8608  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8609  * <p>
8610  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
8611  * has no knowledge of the format of the data returned by the Proxy.<br>
8612  * <p>
8613  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8614  * instances from the data object. These records are cached and made available through accessor functions.
8615  * @constructor
8616  * Creates a new Store.
8617  * @param {Object} config A config object containing the objects needed for the Store to access data,
8618  * and read the data into Records.
8619  */
8620 Roo.data.Store = function(config){
8621     this.data = new Roo.util.MixedCollection(false);
8622     this.data.getKey = function(o){
8623         return o.id;
8624     };
8625     this.baseParams = {};
8626     // private
8627     this.paramNames = {
8628         "start" : "start",
8629         "limit" : "limit",
8630         "sort" : "sort",
8631         "dir" : "dir",
8632         "multisort" : "_multisort"
8633     };
8634
8635     if(config && config.data){
8636         this.inlineData = config.data;
8637         delete config.data;
8638     }
8639
8640     Roo.apply(this, config);
8641     
8642     if(this.reader){ // reader passed
8643         this.reader = Roo.factory(this.reader, Roo.data);
8644         this.reader.xmodule = this.xmodule || false;
8645         if(!this.recordType){
8646             this.recordType = this.reader.recordType;
8647         }
8648         if(this.reader.onMetaChange){
8649             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8650         }
8651     }
8652
8653     if(this.recordType){
8654         this.fields = this.recordType.prototype.fields;
8655     }
8656     this.modified = [];
8657
8658     this.addEvents({
8659         /**
8660          * @event datachanged
8661          * Fires when the data cache has changed, and a widget which is using this Store
8662          * as a Record cache should refresh its view.
8663          * @param {Store} this
8664          */
8665         datachanged : true,
8666         /**
8667          * @event metachange
8668          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8669          * @param {Store} this
8670          * @param {Object} meta The JSON metadata
8671          */
8672         metachange : true,
8673         /**
8674          * @event add
8675          * Fires when Records have been added to the Store
8676          * @param {Store} this
8677          * @param {Roo.data.Record[]} records The array of Records added
8678          * @param {Number} index The index at which the record(s) were added
8679          */
8680         add : true,
8681         /**
8682          * @event remove
8683          * Fires when a Record has been removed from the Store
8684          * @param {Store} this
8685          * @param {Roo.data.Record} record The Record that was removed
8686          * @param {Number} index The index at which the record was removed
8687          */
8688         remove : true,
8689         /**
8690          * @event update
8691          * Fires when a Record has been updated
8692          * @param {Store} this
8693          * @param {Roo.data.Record} record The Record that was updated
8694          * @param {String} operation The update operation being performed.  Value may be one of:
8695          * <pre><code>
8696  Roo.data.Record.EDIT
8697  Roo.data.Record.REJECT
8698  Roo.data.Record.COMMIT
8699          * </code></pre>
8700          */
8701         update : true,
8702         /**
8703          * @event clear
8704          * Fires when the data cache has been cleared.
8705          * @param {Store} this
8706          */
8707         clear : true,
8708         /**
8709          * @event beforeload
8710          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8711          * the load action will be canceled.
8712          * @param {Store} this
8713          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8714          */
8715         beforeload : true,
8716         /**
8717          * @event beforeloadadd
8718          * Fires after a new set of Records has been loaded.
8719          * @param {Store} this
8720          * @param {Roo.data.Record[]} records The Records that were loaded
8721          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8722          */
8723         beforeloadadd : true,
8724         /**
8725          * @event load
8726          * Fires after a new set of Records has been loaded, before they are added to the store.
8727          * @param {Store} this
8728          * @param {Roo.data.Record[]} records The Records that were loaded
8729          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8730          * @params {Object} return from reader
8731          */
8732         load : true,
8733         /**
8734          * @event loadexception
8735          * Fires if an exception occurs in the Proxy during loading.
8736          * Called with the signature of the Proxy's "loadexception" event.
8737          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8738          * 
8739          * @param {Proxy} 
8740          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8741          * @param {Object} load options 
8742          * @param {Object} jsonData from your request (normally this contains the Exception)
8743          */
8744         loadexception : true
8745     });
8746     
8747     if(this.proxy){
8748         this.proxy = Roo.factory(this.proxy, Roo.data);
8749         this.proxy.xmodule = this.xmodule || false;
8750         this.relayEvents(this.proxy,  ["loadexception"]);
8751     }
8752     this.sortToggle = {};
8753     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8754
8755     Roo.data.Store.superclass.constructor.call(this);
8756
8757     if(this.inlineData){
8758         this.loadData(this.inlineData);
8759         delete this.inlineData;
8760     }
8761 };
8762
8763 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8764      /**
8765     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8766     * without a remote query - used by combo/forms at present.
8767     */
8768     
8769     /**
8770     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8771     */
8772     /**
8773     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8774     */
8775     /**
8776     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8777     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8778     */
8779     /**
8780     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8781     * on any HTTP request
8782     */
8783     /**
8784     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8785     */
8786     /**
8787     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8788     */
8789     multiSort: false,
8790     /**
8791     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8792     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8793     */
8794     remoteSort : false,
8795
8796     /**
8797     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8798      * loaded or when a record is removed. (defaults to false).
8799     */
8800     pruneModifiedRecords : false,
8801
8802     // private
8803     lastOptions : null,
8804
8805     /**
8806      * Add Records to the Store and fires the add event.
8807      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8808      */
8809     add : function(records){
8810         records = [].concat(records);
8811         for(var i = 0, len = records.length; i < len; i++){
8812             records[i].join(this);
8813         }
8814         var index = this.data.length;
8815         this.data.addAll(records);
8816         this.fireEvent("add", this, records, index);
8817     },
8818
8819     /**
8820      * Remove a Record from the Store and fires the remove event.
8821      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8822      */
8823     remove : function(record){
8824         var index = this.data.indexOf(record);
8825         this.data.removeAt(index);
8826         if(this.pruneModifiedRecords){
8827             this.modified.remove(record);
8828         }
8829         this.fireEvent("remove", this, record, index);
8830     },
8831
8832     /**
8833      * Remove all Records from the Store and fires the clear event.
8834      */
8835     removeAll : function(){
8836         this.data.clear();
8837         if(this.pruneModifiedRecords){
8838             this.modified = [];
8839         }
8840         this.fireEvent("clear", this);
8841     },
8842
8843     /**
8844      * Inserts Records to the Store at the given index and fires the add event.
8845      * @param {Number} index The start index at which to insert the passed Records.
8846      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8847      */
8848     insert : function(index, records){
8849         records = [].concat(records);
8850         for(var i = 0, len = records.length; i < len; i++){
8851             this.data.insert(index, records[i]);
8852             records[i].join(this);
8853         }
8854         this.fireEvent("add", this, records, index);
8855     },
8856
8857     /**
8858      * Get the index within the cache of the passed Record.
8859      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8860      * @return {Number} The index of the passed Record. Returns -1 if not found.
8861      */
8862     indexOf : function(record){
8863         return this.data.indexOf(record);
8864     },
8865
8866     /**
8867      * Get the index within the cache of the Record with the passed id.
8868      * @param {String} id The id of the Record to find.
8869      * @return {Number} The index of the Record. Returns -1 if not found.
8870      */
8871     indexOfId : function(id){
8872         return this.data.indexOfKey(id);
8873     },
8874
8875     /**
8876      * Get the Record with the specified id.
8877      * @param {String} id The id of the Record to find.
8878      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8879      */
8880     getById : function(id){
8881         return this.data.key(id);
8882     },
8883
8884     /**
8885      * Get the Record at the specified index.
8886      * @param {Number} index The index of the Record to find.
8887      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8888      */
8889     getAt : function(index){
8890         return this.data.itemAt(index);
8891     },
8892
8893     /**
8894      * Returns a range of Records between specified indices.
8895      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8896      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8897      * @return {Roo.data.Record[]} An array of Records
8898      */
8899     getRange : function(start, end){
8900         return this.data.getRange(start, end);
8901     },
8902
8903     // private
8904     storeOptions : function(o){
8905         o = Roo.apply({}, o);
8906         delete o.callback;
8907         delete o.scope;
8908         this.lastOptions = o;
8909     },
8910
8911     /**
8912      * Loads the Record cache from the configured Proxy using the configured Reader.
8913      * <p>
8914      * If using remote paging, then the first load call must specify the <em>start</em>
8915      * and <em>limit</em> properties in the options.params property to establish the initial
8916      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8917      * <p>
8918      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8919      * and this call will return before the new data has been loaded. Perform any post-processing
8920      * in a callback function, or in a "load" event handler.</strong>
8921      * <p>
8922      * @param {Object} options An object containing properties which control loading options:<ul>
8923      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8924      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8925      * passed the following arguments:<ul>
8926      * <li>r : Roo.data.Record[]</li>
8927      * <li>options: Options object from the load call</li>
8928      * <li>success: Boolean success indicator</li></ul></li>
8929      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8930      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8931      * </ul>
8932      */
8933     load : function(options){
8934         options = options || {};
8935         if(this.fireEvent("beforeload", this, options) !== false){
8936             this.storeOptions(options);
8937             var p = Roo.apply(options.params || {}, this.baseParams);
8938             // if meta was not loaded from remote source.. try requesting it.
8939             if (!this.reader.metaFromRemote) {
8940                 p._requestMeta = 1;
8941             }
8942             if(this.sortInfo && this.remoteSort){
8943                 var pn = this.paramNames;
8944                 p[pn["sort"]] = this.sortInfo.field;
8945                 p[pn["dir"]] = this.sortInfo.direction;
8946             }
8947             if (this.multiSort) {
8948                 var pn = this.paramNames;
8949                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8950             }
8951             
8952             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8953         }
8954     },
8955
8956     /**
8957      * Reloads the Record cache from the configured Proxy using the configured Reader and
8958      * the options from the last load operation performed.
8959      * @param {Object} options (optional) An object containing properties which may override the options
8960      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8961      * the most recently used options are reused).
8962      */
8963     reload : function(options){
8964         this.load(Roo.applyIf(options||{}, this.lastOptions));
8965     },
8966
8967     // private
8968     // Called as a callback by the Reader during a load operation.
8969     loadRecords : function(o, options, success){
8970         if(!o || success === false){
8971             if(success !== false){
8972                 this.fireEvent("load", this, [], options, o);
8973             }
8974             if(options.callback){
8975                 options.callback.call(options.scope || this, [], options, false);
8976             }
8977             return;
8978         }
8979         // if data returned failure - throw an exception.
8980         if (o.success === false) {
8981             // show a message if no listener is registered.
8982             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8983                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8984             }
8985             // loadmask wil be hooked into this..
8986             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8987             return;
8988         }
8989         var r = o.records, t = o.totalRecords || r.length;
8990         
8991         this.fireEvent("beforeloadadd", this, r, options, o);
8992         
8993         if(!options || options.add !== true){
8994             if(this.pruneModifiedRecords){
8995                 this.modified = [];
8996             }
8997             for(var i = 0, len = r.length; i < len; i++){
8998                 r[i].join(this);
8999             }
9000             if(this.snapshot){
9001                 this.data = this.snapshot;
9002                 delete this.snapshot;
9003             }
9004             this.data.clear();
9005             this.data.addAll(r);
9006             this.totalLength = t;
9007             this.applySort();
9008             this.fireEvent("datachanged", this);
9009         }else{
9010             this.totalLength = Math.max(t, this.data.length+r.length);
9011             this.add(r);
9012         }
9013         this.fireEvent("load", this, r, options, o);
9014         if(options.callback){
9015             options.callback.call(options.scope || this, r, options, true);
9016         }
9017     },
9018
9019
9020     /**
9021      * Loads data from a passed data block. A Reader which understands the format of the data
9022      * must have been configured in the constructor.
9023      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9024      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9025      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9026      */
9027     loadData : function(o, append){
9028         var r = this.reader.readRecords(o);
9029         this.loadRecords(r, {add: append}, true);
9030     },
9031
9032     /**
9033      * Gets the number of cached records.
9034      * <p>
9035      * <em>If using paging, this may not be the total size of the dataset. If the data object
9036      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9037      * the data set size</em>
9038      */
9039     getCount : function(){
9040         return this.data.length || 0;
9041     },
9042
9043     /**
9044      * Gets the total number of records in the dataset as returned by the server.
9045      * <p>
9046      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9047      * the dataset size</em>
9048      */
9049     getTotalCount : function(){
9050         return this.totalLength || 0;
9051     },
9052
9053     /**
9054      * Returns the sort state of the Store as an object with two properties:
9055      * <pre><code>
9056  field {String} The name of the field by which the Records are sorted
9057  direction {String} The sort order, "ASC" or "DESC"
9058      * </code></pre>
9059      */
9060     getSortState : function(){
9061         return this.sortInfo;
9062     },
9063
9064     // private
9065     applySort : function(){
9066         if(this.sortInfo && !this.remoteSort){
9067             var s = this.sortInfo, f = s.field;
9068             var st = this.fields.get(f).sortType;
9069             var fn = function(r1, r2){
9070                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9071                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9072             };
9073             this.data.sort(s.direction, fn);
9074             if(this.snapshot && this.snapshot != this.data){
9075                 this.snapshot.sort(s.direction, fn);
9076             }
9077         }
9078     },
9079
9080     /**
9081      * Sets the default sort column and order to be used by the next load operation.
9082      * @param {String} fieldName The name of the field to sort by.
9083      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9084      */
9085     setDefaultSort : function(field, dir){
9086         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9087     },
9088
9089     /**
9090      * Sort the Records.
9091      * If remote sorting is used, the sort is performed on the server, and the cache is
9092      * reloaded. If local sorting is used, the cache is sorted internally.
9093      * @param {String} fieldName The name of the field to sort by.
9094      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9095      */
9096     sort : function(fieldName, dir){
9097         var f = this.fields.get(fieldName);
9098         if(!dir){
9099             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9100             
9101             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9102                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9103             }else{
9104                 dir = f.sortDir;
9105             }
9106         }
9107         this.sortToggle[f.name] = dir;
9108         this.sortInfo = {field: f.name, direction: dir};
9109         if(!this.remoteSort){
9110             this.applySort();
9111             this.fireEvent("datachanged", this);
9112         }else{
9113             this.load(this.lastOptions);
9114         }
9115     },
9116
9117     /**
9118      * Calls the specified function for each of the Records in the cache.
9119      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9120      * Returning <em>false</em> aborts and exits the iteration.
9121      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9122      */
9123     each : function(fn, scope){
9124         this.data.each(fn, scope);
9125     },
9126
9127     /**
9128      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9129      * (e.g., during paging).
9130      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9131      */
9132     getModifiedRecords : function(){
9133         return this.modified;
9134     },
9135
9136     // private
9137     createFilterFn : function(property, value, anyMatch){
9138         if(!value.exec){ // not a regex
9139             value = String(value);
9140             if(value.length == 0){
9141                 return false;
9142             }
9143             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9144         }
9145         return function(r){
9146             return value.test(r.data[property]);
9147         };
9148     },
9149
9150     /**
9151      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9152      * @param {String} property A field on your records
9153      * @param {Number} start The record index to start at (defaults to 0)
9154      * @param {Number} end The last record index to include (defaults to length - 1)
9155      * @return {Number} The sum
9156      */
9157     sum : function(property, start, end){
9158         var rs = this.data.items, v = 0;
9159         start = start || 0;
9160         end = (end || end === 0) ? end : rs.length-1;
9161
9162         for(var i = start; i <= end; i++){
9163             v += (rs[i].data[property] || 0);
9164         }
9165         return v;
9166     },
9167
9168     /**
9169      * Filter the records by a specified property.
9170      * @param {String} field A field on your records
9171      * @param {String/RegExp} value Either a string that the field
9172      * should start with or a RegExp to test against the field
9173      * @param {Boolean} anyMatch True to match any part not just the beginning
9174      */
9175     filter : function(property, value, anyMatch){
9176         var fn = this.createFilterFn(property, value, anyMatch);
9177         return fn ? this.filterBy(fn) : this.clearFilter();
9178     },
9179
9180     /**
9181      * Filter by a function. The specified function will be called with each
9182      * record in this data source. If the function returns true the record is included,
9183      * otherwise it is filtered.
9184      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9185      * @param {Object} scope (optional) The scope of the function (defaults to this)
9186      */
9187     filterBy : function(fn, scope){
9188         this.snapshot = this.snapshot || this.data;
9189         this.data = this.queryBy(fn, scope||this);
9190         this.fireEvent("datachanged", this);
9191     },
9192
9193     /**
9194      * Query the records by a specified property.
9195      * @param {String} field A field on your records
9196      * @param {String/RegExp} value Either a string that the field
9197      * should start with or a RegExp to test against the field
9198      * @param {Boolean} anyMatch True to match any part not just the beginning
9199      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9200      */
9201     query : function(property, value, anyMatch){
9202         var fn = this.createFilterFn(property, value, anyMatch);
9203         return fn ? this.queryBy(fn) : this.data.clone();
9204     },
9205
9206     /**
9207      * Query by a function. The specified function will be called with each
9208      * record in this data source. If the function returns true the record is included
9209      * in the results.
9210      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9211      * @param {Object} scope (optional) The scope of the function (defaults to this)
9212       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9213      **/
9214     queryBy : function(fn, scope){
9215         var data = this.snapshot || this.data;
9216         return data.filterBy(fn, scope||this);
9217     },
9218
9219     /**
9220      * Collects unique values for a particular dataIndex from this store.
9221      * @param {String} dataIndex The property to collect
9222      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9223      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9224      * @return {Array} An array of the unique values
9225      **/
9226     collect : function(dataIndex, allowNull, bypassFilter){
9227         var d = (bypassFilter === true && this.snapshot) ?
9228                 this.snapshot.items : this.data.items;
9229         var v, sv, r = [], l = {};
9230         for(var i = 0, len = d.length; i < len; i++){
9231             v = d[i].data[dataIndex];
9232             sv = String(v);
9233             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9234                 l[sv] = true;
9235                 r[r.length] = v;
9236             }
9237         }
9238         return r;
9239     },
9240
9241     /**
9242      * Revert to a view of the Record cache with no filtering applied.
9243      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9244      */
9245     clearFilter : function(suppressEvent){
9246         if(this.snapshot && this.snapshot != this.data){
9247             this.data = this.snapshot;
9248             delete this.snapshot;
9249             if(suppressEvent !== true){
9250                 this.fireEvent("datachanged", this);
9251             }
9252         }
9253     },
9254
9255     // private
9256     afterEdit : function(record){
9257         if(this.modified.indexOf(record) == -1){
9258             this.modified.push(record);
9259         }
9260         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9261     },
9262     
9263     // private
9264     afterReject : function(record){
9265         this.modified.remove(record);
9266         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9267     },
9268
9269     // private
9270     afterCommit : function(record){
9271         this.modified.remove(record);
9272         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9273     },
9274
9275     /**
9276      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9277      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9278      */
9279     commitChanges : function(){
9280         var m = this.modified.slice(0);
9281         this.modified = [];
9282         for(var i = 0, len = m.length; i < len; i++){
9283             m[i].commit();
9284         }
9285     },
9286
9287     /**
9288      * Cancel outstanding changes on all changed records.
9289      */
9290     rejectChanges : function(){
9291         var m = this.modified.slice(0);
9292         this.modified = [];
9293         for(var i = 0, len = m.length; i < len; i++){
9294             m[i].reject();
9295         }
9296     },
9297
9298     onMetaChange : function(meta, rtype, o){
9299         this.recordType = rtype;
9300         this.fields = rtype.prototype.fields;
9301         delete this.snapshot;
9302         this.sortInfo = meta.sortInfo || this.sortInfo;
9303         this.modified = [];
9304         this.fireEvent('metachange', this, this.reader.meta);
9305     },
9306     
9307     moveIndex : function(data, type)
9308     {
9309         var index = this.indexOf(data);
9310         
9311         var newIndex = index + type;
9312         
9313         this.remove(data);
9314         
9315         this.insert(newIndex, data);
9316         
9317     }
9318 });/*
9319  * Based on:
9320  * Ext JS Library 1.1.1
9321  * Copyright(c) 2006-2007, Ext JS, LLC.
9322  *
9323  * Originally Released Under LGPL - original licence link has changed is not relivant.
9324  *
9325  * Fork - LGPL
9326  * <script type="text/javascript">
9327  */
9328
9329 /**
9330  * @class Roo.data.SimpleStore
9331  * @extends Roo.data.Store
9332  * Small helper class to make creating Stores from Array data easier.
9333  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9334  * @cfg {Array} fields An array of field definition objects, or field name strings.
9335  * @cfg {Array} data The multi-dimensional array of data
9336  * @constructor
9337  * @param {Object} config
9338  */
9339 Roo.data.SimpleStore = function(config){
9340     Roo.data.SimpleStore.superclass.constructor.call(this, {
9341         isLocal : true,
9342         reader: new Roo.data.ArrayReader({
9343                 id: config.id
9344             },
9345             Roo.data.Record.create(config.fields)
9346         ),
9347         proxy : new Roo.data.MemoryProxy(config.data)
9348     });
9349     this.load();
9350 };
9351 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9352  * Based on:
9353  * Ext JS Library 1.1.1
9354  * Copyright(c) 2006-2007, Ext JS, LLC.
9355  *
9356  * Originally Released Under LGPL - original licence link has changed is not relivant.
9357  *
9358  * Fork - LGPL
9359  * <script type="text/javascript">
9360  */
9361
9362 /**
9363 /**
9364  * @extends Roo.data.Store
9365  * @class Roo.data.JsonStore
9366  * Small helper class to make creating Stores for JSON data easier. <br/>
9367 <pre><code>
9368 var store = new Roo.data.JsonStore({
9369     url: 'get-images.php',
9370     root: 'images',
9371     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9372 });
9373 </code></pre>
9374  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9375  * JsonReader and HttpProxy (unless inline data is provided).</b>
9376  * @cfg {Array} fields An array of field definition objects, or field name strings.
9377  * @constructor
9378  * @param {Object} config
9379  */
9380 Roo.data.JsonStore = function(c){
9381     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9382         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9383         reader: new Roo.data.JsonReader(c, c.fields)
9384     }));
9385 };
9386 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9387  * Based on:
9388  * Ext JS Library 1.1.1
9389  * Copyright(c) 2006-2007, Ext JS, LLC.
9390  *
9391  * Originally Released Under LGPL - original licence link has changed is not relivant.
9392  *
9393  * Fork - LGPL
9394  * <script type="text/javascript">
9395  */
9396
9397  
9398 Roo.data.Field = function(config){
9399     if(typeof config == "string"){
9400         config = {name: config};
9401     }
9402     Roo.apply(this, config);
9403     
9404     if(!this.type){
9405         this.type = "auto";
9406     }
9407     
9408     var st = Roo.data.SortTypes;
9409     // named sortTypes are supported, here we look them up
9410     if(typeof this.sortType == "string"){
9411         this.sortType = st[this.sortType];
9412     }
9413     
9414     // set default sortType for strings and dates
9415     if(!this.sortType){
9416         switch(this.type){
9417             case "string":
9418                 this.sortType = st.asUCString;
9419                 break;
9420             case "date":
9421                 this.sortType = st.asDate;
9422                 break;
9423             default:
9424                 this.sortType = st.none;
9425         }
9426     }
9427
9428     // define once
9429     var stripRe = /[\$,%]/g;
9430
9431     // prebuilt conversion function for this field, instead of
9432     // switching every time we're reading a value
9433     if(!this.convert){
9434         var cv, dateFormat = this.dateFormat;
9435         switch(this.type){
9436             case "":
9437             case "auto":
9438             case undefined:
9439                 cv = function(v){ return v; };
9440                 break;
9441             case "string":
9442                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9443                 break;
9444             case "int":
9445                 cv = function(v){
9446                     return v !== undefined && v !== null && v !== '' ?
9447                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9448                     };
9449                 break;
9450             case "float":
9451                 cv = function(v){
9452                     return v !== undefined && v !== null && v !== '' ?
9453                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9454                     };
9455                 break;
9456             case "bool":
9457             case "boolean":
9458                 cv = function(v){ return v === true || v === "true" || v == 1; };
9459                 break;
9460             case "date":
9461                 cv = function(v){
9462                     if(!v){
9463                         return '';
9464                     }
9465                     if(v instanceof Date){
9466                         return v;
9467                     }
9468                     if(dateFormat){
9469                         if(dateFormat == "timestamp"){
9470                             return new Date(v*1000);
9471                         }
9472                         return Date.parseDate(v, dateFormat);
9473                     }
9474                     var parsed = Date.parse(v);
9475                     return parsed ? new Date(parsed) : null;
9476                 };
9477              break;
9478             
9479         }
9480         this.convert = cv;
9481     }
9482 };
9483
9484 Roo.data.Field.prototype = {
9485     dateFormat: null,
9486     defaultValue: "",
9487     mapping: null,
9488     sortType : null,
9489     sortDir : "ASC"
9490 };/*
9491  * Based on:
9492  * Ext JS Library 1.1.1
9493  * Copyright(c) 2006-2007, Ext JS, LLC.
9494  *
9495  * Originally Released Under LGPL - original licence link has changed is not relivant.
9496  *
9497  * Fork - LGPL
9498  * <script type="text/javascript">
9499  */
9500  
9501 // Base class for reading structured data from a data source.  This class is intended to be
9502 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9503
9504 /**
9505  * @class Roo.data.DataReader
9506  * Base class for reading structured data from a data source.  This class is intended to be
9507  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9508  */
9509
9510 Roo.data.DataReader = function(meta, recordType){
9511     
9512     this.meta = meta;
9513     
9514     this.recordType = recordType instanceof Array ? 
9515         Roo.data.Record.create(recordType) : recordType;
9516 };
9517
9518 Roo.data.DataReader.prototype = {
9519      /**
9520      * Create an empty record
9521      * @param {Object} data (optional) - overlay some values
9522      * @return {Roo.data.Record} record created.
9523      */
9524     newRow :  function(d) {
9525         var da =  {};
9526         this.recordType.prototype.fields.each(function(c) {
9527             switch( c.type) {
9528                 case 'int' : da[c.name] = 0; break;
9529                 case 'date' : da[c.name] = new Date(); break;
9530                 case 'float' : da[c.name] = 0.0; break;
9531                 case 'boolean' : da[c.name] = false; break;
9532                 default : da[c.name] = ""; break;
9533             }
9534             
9535         });
9536         return new this.recordType(Roo.apply(da, d));
9537     }
9538     
9539 };/*
9540  * Based on:
9541  * Ext JS Library 1.1.1
9542  * Copyright(c) 2006-2007, Ext JS, LLC.
9543  *
9544  * Originally Released Under LGPL - original licence link has changed is not relivant.
9545  *
9546  * Fork - LGPL
9547  * <script type="text/javascript">
9548  */
9549
9550 /**
9551  * @class Roo.data.DataProxy
9552  * @extends Roo.data.Observable
9553  * This class is an abstract base class for implementations which provide retrieval of
9554  * unformatted data objects.<br>
9555  * <p>
9556  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9557  * (of the appropriate type which knows how to parse the data object) to provide a block of
9558  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9559  * <p>
9560  * Custom implementations must implement the load method as described in
9561  * {@link Roo.data.HttpProxy#load}.
9562  */
9563 Roo.data.DataProxy = function(){
9564     this.addEvents({
9565         /**
9566          * @event beforeload
9567          * Fires before a network request is made to retrieve a data object.
9568          * @param {Object} This DataProxy object.
9569          * @param {Object} params The params parameter to the load function.
9570          */
9571         beforeload : true,
9572         /**
9573          * @event load
9574          * Fires before the load method's callback is called.
9575          * @param {Object} This DataProxy object.
9576          * @param {Object} o The data object.
9577          * @param {Object} arg The callback argument object passed to the load function.
9578          */
9579         load : true,
9580         /**
9581          * @event loadexception
9582          * Fires if an Exception occurs during data retrieval.
9583          * @param {Object} This DataProxy object.
9584          * @param {Object} o The data object.
9585          * @param {Object} arg The callback argument object passed to the load function.
9586          * @param {Object} e The Exception.
9587          */
9588         loadexception : true
9589     });
9590     Roo.data.DataProxy.superclass.constructor.call(this);
9591 };
9592
9593 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9594
9595     /**
9596      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9597      */
9598 /*
9599  * Based on:
9600  * Ext JS Library 1.1.1
9601  * Copyright(c) 2006-2007, Ext JS, LLC.
9602  *
9603  * Originally Released Under LGPL - original licence link has changed is not relivant.
9604  *
9605  * Fork - LGPL
9606  * <script type="text/javascript">
9607  */
9608 /**
9609  * @class Roo.data.MemoryProxy
9610  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9611  * to the Reader when its load method is called.
9612  * @constructor
9613  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9614  */
9615 Roo.data.MemoryProxy = function(data){
9616     if (data.data) {
9617         data = data.data;
9618     }
9619     Roo.data.MemoryProxy.superclass.constructor.call(this);
9620     this.data = data;
9621 };
9622
9623 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9624     /**
9625      * Load data from the requested source (in this case an in-memory
9626      * data object passed to the constructor), read the data object into
9627      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9628      * process that block using the passed callback.
9629      * @param {Object} params This parameter is not used by the MemoryProxy class.
9630      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9631      * object into a block of Roo.data.Records.
9632      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9633      * The function must be passed <ul>
9634      * <li>The Record block object</li>
9635      * <li>The "arg" argument from the load function</li>
9636      * <li>A boolean success indicator</li>
9637      * </ul>
9638      * @param {Object} scope The scope in which to call the callback
9639      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9640      */
9641     load : function(params, reader, callback, scope, arg){
9642         params = params || {};
9643         var result;
9644         try {
9645             result = reader.readRecords(this.data);
9646         }catch(e){
9647             this.fireEvent("loadexception", this, arg, null, e);
9648             callback.call(scope, null, arg, false);
9649             return;
9650         }
9651         callback.call(scope, result, arg, true);
9652     },
9653     
9654     // private
9655     update : function(params, records){
9656         
9657     }
9658 });/*
9659  * Based on:
9660  * Ext JS Library 1.1.1
9661  * Copyright(c) 2006-2007, Ext JS, LLC.
9662  *
9663  * Originally Released Under LGPL - original licence link has changed is not relivant.
9664  *
9665  * Fork - LGPL
9666  * <script type="text/javascript">
9667  */
9668 /**
9669  * @class Roo.data.HttpProxy
9670  * @extends Roo.data.DataProxy
9671  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9672  * configured to reference a certain URL.<br><br>
9673  * <p>
9674  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9675  * from which the running page was served.<br><br>
9676  * <p>
9677  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9678  * <p>
9679  * Be aware that to enable the browser to parse an XML document, the server must set
9680  * the Content-Type header in the HTTP response to "text/xml".
9681  * @constructor
9682  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9683  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9684  * will be used to make the request.
9685  */
9686 Roo.data.HttpProxy = function(conn){
9687     Roo.data.HttpProxy.superclass.constructor.call(this);
9688     // is conn a conn config or a real conn?
9689     this.conn = conn;
9690     this.useAjax = !conn || !conn.events;
9691   
9692 };
9693
9694 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9695     // thse are take from connection...
9696     
9697     /**
9698      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9699      */
9700     /**
9701      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9702      * extra parameters to each request made by this object. (defaults to undefined)
9703      */
9704     /**
9705      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9706      *  to each request made by this object. (defaults to undefined)
9707      */
9708     /**
9709      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
9710      */
9711     /**
9712      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9713      */
9714      /**
9715      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9716      * @type Boolean
9717      */
9718   
9719
9720     /**
9721      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9722      * @type Boolean
9723      */
9724     /**
9725      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9726      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9727      * a finer-grained basis than the DataProxy events.
9728      */
9729     getConnection : function(){
9730         return this.useAjax ? Roo.Ajax : this.conn;
9731     },
9732
9733     /**
9734      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9735      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9736      * process that block using the passed callback.
9737      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9738      * for the request to the remote server.
9739      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9740      * object into a block of Roo.data.Records.
9741      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9742      * The function must be passed <ul>
9743      * <li>The Record block object</li>
9744      * <li>The "arg" argument from the load function</li>
9745      * <li>A boolean success indicator</li>
9746      * </ul>
9747      * @param {Object} scope The scope in which to call the callback
9748      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9749      */
9750     load : function(params, reader, callback, scope, arg){
9751         if(this.fireEvent("beforeload", this, params) !== false){
9752             var  o = {
9753                 params : params || {},
9754                 request: {
9755                     callback : callback,
9756                     scope : scope,
9757                     arg : arg
9758                 },
9759                 reader: reader,
9760                 callback : this.loadResponse,
9761                 scope: this
9762             };
9763             if(this.useAjax){
9764                 Roo.applyIf(o, this.conn);
9765                 if(this.activeRequest){
9766                     Roo.Ajax.abort(this.activeRequest);
9767                 }
9768                 this.activeRequest = Roo.Ajax.request(o);
9769             }else{
9770                 this.conn.request(o);
9771             }
9772         }else{
9773             callback.call(scope||this, null, arg, false);
9774         }
9775     },
9776
9777     // private
9778     loadResponse : function(o, success, response){
9779         delete this.activeRequest;
9780         if(!success){
9781             this.fireEvent("loadexception", this, o, response);
9782             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9783             return;
9784         }
9785         var result;
9786         try {
9787             result = o.reader.read(response);
9788         }catch(e){
9789             this.fireEvent("loadexception", this, o, response, e);
9790             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9791             return;
9792         }
9793         
9794         this.fireEvent("load", this, o, o.request.arg);
9795         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9796     },
9797
9798     // private
9799     update : function(dataSet){
9800
9801     },
9802
9803     // private
9804     updateResponse : function(dataSet){
9805
9806     }
9807 });/*
9808  * Based on:
9809  * Ext JS Library 1.1.1
9810  * Copyright(c) 2006-2007, Ext JS, LLC.
9811  *
9812  * Originally Released Under LGPL - original licence link has changed is not relivant.
9813  *
9814  * Fork - LGPL
9815  * <script type="text/javascript">
9816  */
9817
9818 /**
9819  * @class Roo.data.ScriptTagProxy
9820  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9821  * other than the originating domain of the running page.<br><br>
9822  * <p>
9823  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
9824  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9825  * <p>
9826  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9827  * source code that is used as the source inside a &lt;script> tag.<br><br>
9828  * <p>
9829  * In order for the browser to process the returned data, the server must wrap the data object
9830  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9831  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9832  * depending on whether the callback name was passed:
9833  * <p>
9834  * <pre><code>
9835 boolean scriptTag = false;
9836 String cb = request.getParameter("callback");
9837 if (cb != null) {
9838     scriptTag = true;
9839     response.setContentType("text/javascript");
9840 } else {
9841     response.setContentType("application/x-json");
9842 }
9843 Writer out = response.getWriter();
9844 if (scriptTag) {
9845     out.write(cb + "(");
9846 }
9847 out.print(dataBlock.toJsonString());
9848 if (scriptTag) {
9849     out.write(");");
9850 }
9851 </pre></code>
9852  *
9853  * @constructor
9854  * @param {Object} config A configuration object.
9855  */
9856 Roo.data.ScriptTagProxy = function(config){
9857     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9858     Roo.apply(this, config);
9859     this.head = document.getElementsByTagName("head")[0];
9860 };
9861
9862 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9863
9864 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9865     /**
9866      * @cfg {String} url The URL from which to request the data object.
9867      */
9868     /**
9869      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9870      */
9871     timeout : 30000,
9872     /**
9873      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9874      * the server the name of the callback function set up by the load call to process the returned data object.
9875      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9876      * javascript output which calls this named function passing the data object as its only parameter.
9877      */
9878     callbackParam : "callback",
9879     /**
9880      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9881      * name to the request.
9882      */
9883     nocache : true,
9884
9885     /**
9886      * Load data from the configured URL, read the data object into
9887      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9888      * process that block using the passed callback.
9889      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9890      * for the request to the remote server.
9891      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9892      * object into a block of Roo.data.Records.
9893      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9894      * The function must be passed <ul>
9895      * <li>The Record block object</li>
9896      * <li>The "arg" argument from the load function</li>
9897      * <li>A boolean success indicator</li>
9898      * </ul>
9899      * @param {Object} scope The scope in which to call the callback
9900      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9901      */
9902     load : function(params, reader, callback, scope, arg){
9903         if(this.fireEvent("beforeload", this, params) !== false){
9904
9905             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9906
9907             var url = this.url;
9908             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9909             if(this.nocache){
9910                 url += "&_dc=" + (new Date().getTime());
9911             }
9912             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9913             var trans = {
9914                 id : transId,
9915                 cb : "stcCallback"+transId,
9916                 scriptId : "stcScript"+transId,
9917                 params : params,
9918                 arg : arg,
9919                 url : url,
9920                 callback : callback,
9921                 scope : scope,
9922                 reader : reader
9923             };
9924             var conn = this;
9925
9926             window[trans.cb] = function(o){
9927                 conn.handleResponse(o, trans);
9928             };
9929
9930             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9931
9932             if(this.autoAbort !== false){
9933                 this.abort();
9934             }
9935
9936             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9937
9938             var script = document.createElement("script");
9939             script.setAttribute("src", url);
9940             script.setAttribute("type", "text/javascript");
9941             script.setAttribute("id", trans.scriptId);
9942             this.head.appendChild(script);
9943
9944             this.trans = trans;
9945         }else{
9946             callback.call(scope||this, null, arg, false);
9947         }
9948     },
9949
9950     // private
9951     isLoading : function(){
9952         return this.trans ? true : false;
9953     },
9954
9955     /**
9956      * Abort the current server request.
9957      */
9958     abort : function(){
9959         if(this.isLoading()){
9960             this.destroyTrans(this.trans);
9961         }
9962     },
9963
9964     // private
9965     destroyTrans : function(trans, isLoaded){
9966         this.head.removeChild(document.getElementById(trans.scriptId));
9967         clearTimeout(trans.timeoutId);
9968         if(isLoaded){
9969             window[trans.cb] = undefined;
9970             try{
9971                 delete window[trans.cb];
9972             }catch(e){}
9973         }else{
9974             // if hasn't been loaded, wait for load to remove it to prevent script error
9975             window[trans.cb] = function(){
9976                 window[trans.cb] = undefined;
9977                 try{
9978                     delete window[trans.cb];
9979                 }catch(e){}
9980             };
9981         }
9982     },
9983
9984     // private
9985     handleResponse : function(o, trans){
9986         this.trans = false;
9987         this.destroyTrans(trans, true);
9988         var result;
9989         try {
9990             result = trans.reader.readRecords(o);
9991         }catch(e){
9992             this.fireEvent("loadexception", this, o, trans.arg, e);
9993             trans.callback.call(trans.scope||window, null, trans.arg, false);
9994             return;
9995         }
9996         this.fireEvent("load", this, o, trans.arg);
9997         trans.callback.call(trans.scope||window, result, trans.arg, true);
9998     },
9999
10000     // private
10001     handleFailure : function(trans){
10002         this.trans = false;
10003         this.destroyTrans(trans, false);
10004         this.fireEvent("loadexception", this, null, trans.arg);
10005         trans.callback.call(trans.scope||window, null, trans.arg, false);
10006     }
10007 });/*
10008  * Based on:
10009  * Ext JS Library 1.1.1
10010  * Copyright(c) 2006-2007, Ext JS, LLC.
10011  *
10012  * Originally Released Under LGPL - original licence link has changed is not relivant.
10013  *
10014  * Fork - LGPL
10015  * <script type="text/javascript">
10016  */
10017
10018 /**
10019  * @class Roo.data.JsonReader
10020  * @extends Roo.data.DataReader
10021  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10022  * based on mappings in a provided Roo.data.Record constructor.
10023  * 
10024  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10025  * in the reply previously. 
10026  * 
10027  * <p>
10028  * Example code:
10029  * <pre><code>
10030 var RecordDef = Roo.data.Record.create([
10031     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10032     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10033 ]);
10034 var myReader = new Roo.data.JsonReader({
10035     totalProperty: "results",    // The property which contains the total dataset size (optional)
10036     root: "rows",                // The property which contains an Array of row objects
10037     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10038 }, RecordDef);
10039 </code></pre>
10040  * <p>
10041  * This would consume a JSON file like this:
10042  * <pre><code>
10043 { 'results': 2, 'rows': [
10044     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10045     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10046 }
10047 </code></pre>
10048  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10049  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10050  * paged from the remote server.
10051  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10052  * @cfg {String} root name of the property which contains the Array of row objects.
10053  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10054  * @constructor
10055  * Create a new JsonReader
10056  * @param {Object} meta Metadata configuration options
10057  * @param {Object} recordType Either an Array of field definition objects,
10058  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10059  */
10060 Roo.data.JsonReader = function(meta, recordType){
10061     
10062     meta = meta || {};
10063     // set some defaults:
10064     Roo.applyIf(meta, {
10065         totalProperty: 'total',
10066         successProperty : 'success',
10067         root : 'data',
10068         id : 'id'
10069     });
10070     
10071     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10072 };
10073 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10074     
10075     /**
10076      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10077      * Used by Store query builder to append _requestMeta to params.
10078      * 
10079      */
10080     metaFromRemote : false,
10081     /**
10082      * This method is only used by a DataProxy which has retrieved data from a remote server.
10083      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10084      * @return {Object} data A data block which is used by an Roo.data.Store object as
10085      * a cache of Roo.data.Records.
10086      */
10087     read : function(response){
10088         var json = response.responseText;
10089        
10090         var o = /* eval:var:o */ eval("("+json+")");
10091         if(!o) {
10092             throw {message: "JsonReader.read: Json object not found"};
10093         }
10094         
10095         if(o.metaData){
10096             
10097             delete this.ef;
10098             this.metaFromRemote = true;
10099             this.meta = o.metaData;
10100             this.recordType = Roo.data.Record.create(o.metaData.fields);
10101             this.onMetaChange(this.meta, this.recordType, o);
10102         }
10103         return this.readRecords(o);
10104     },
10105
10106     // private function a store will implement
10107     onMetaChange : function(meta, recordType, o){
10108
10109     },
10110
10111     /**
10112          * @ignore
10113          */
10114     simpleAccess: function(obj, subsc) {
10115         return obj[subsc];
10116     },
10117
10118         /**
10119          * @ignore
10120          */
10121     getJsonAccessor: function(){
10122         var re = /[\[\.]/;
10123         return function(expr) {
10124             try {
10125                 return(re.test(expr))
10126                     ? new Function("obj", "return obj." + expr)
10127                     : function(obj){
10128                         return obj[expr];
10129                     };
10130             } catch(e){}
10131             return Roo.emptyFn;
10132         };
10133     }(),
10134
10135     /**
10136      * Create a data block containing Roo.data.Records from an XML document.
10137      * @param {Object} o An object which contains an Array of row objects in the property specified
10138      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10139      * which contains the total size of the dataset.
10140      * @return {Object} data A data block which is used by an Roo.data.Store object as
10141      * a cache of Roo.data.Records.
10142      */
10143     readRecords : function(o){
10144         /**
10145          * After any data loads, the raw JSON data is available for further custom processing.
10146          * @type Object
10147          */
10148         this.o = o;
10149         var s = this.meta, Record = this.recordType,
10150             f = Record.prototype.fields, fi = f.items, fl = f.length;
10151
10152 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10153         if (!this.ef) {
10154             if(s.totalProperty) {
10155                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10156                 }
10157                 if(s.successProperty) {
10158                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10159                 }
10160                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10161                 if (s.id) {
10162                         var g = this.getJsonAccessor(s.id);
10163                         this.getId = function(rec) {
10164                                 var r = g(rec);  
10165                                 return (r === undefined || r === "") ? null : r;
10166                         };
10167                 } else {
10168                         this.getId = function(){return null;};
10169                 }
10170             this.ef = [];
10171             for(var jj = 0; jj < fl; jj++){
10172                 f = fi[jj];
10173                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10174                 this.ef[jj] = this.getJsonAccessor(map);
10175             }
10176         }
10177
10178         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10179         if(s.totalProperty){
10180             var vt = parseInt(this.getTotal(o), 10);
10181             if(!isNaN(vt)){
10182                 totalRecords = vt;
10183             }
10184         }
10185         if(s.successProperty){
10186             var vs = this.getSuccess(o);
10187             if(vs === false || vs === 'false'){
10188                 success = false;
10189             }
10190         }
10191         var records = [];
10192             for(var i = 0; i < c; i++){
10193                     var n = root[i];
10194                 var values = {};
10195                 var id = this.getId(n);
10196                 for(var j = 0; j < fl; j++){
10197                     f = fi[j];
10198                 var v = this.ef[j](n);
10199                 if (!f.convert) {
10200                     Roo.log('missing convert for ' + f.name);
10201                     Roo.log(f);
10202                     continue;
10203                 }
10204                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10205                 }
10206                 var record = new Record(values, id);
10207                 record.json = n;
10208                 records[i] = record;
10209             }
10210             return {
10211             raw : o,
10212                 success : success,
10213                 records : records,
10214                 totalRecords : totalRecords
10215             };
10216     }
10217 });/*
10218  * Based on:
10219  * Ext JS Library 1.1.1
10220  * Copyright(c) 2006-2007, Ext JS, LLC.
10221  *
10222  * Originally Released Under LGPL - original licence link has changed is not relivant.
10223  *
10224  * Fork - LGPL
10225  * <script type="text/javascript">
10226  */
10227
10228 /**
10229  * @class Roo.data.ArrayReader
10230  * @extends Roo.data.DataReader
10231  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10232  * Each element of that Array represents a row of data fields. The
10233  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10234  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10235  * <p>
10236  * Example code:.
10237  * <pre><code>
10238 var RecordDef = Roo.data.Record.create([
10239     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10240     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10241 ]);
10242 var myReader = new Roo.data.ArrayReader({
10243     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10244 }, RecordDef);
10245 </code></pre>
10246  * <p>
10247  * This would consume an Array like this:
10248  * <pre><code>
10249 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10250   </code></pre>
10251  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10252  * @constructor
10253  * Create a new JsonReader
10254  * @param {Object} meta Metadata configuration options.
10255  * @param {Object} recordType Either an Array of field definition objects
10256  * as specified to {@link Roo.data.Record#create},
10257  * or an {@link Roo.data.Record} object
10258  * created using {@link Roo.data.Record#create}.
10259  */
10260 Roo.data.ArrayReader = function(meta, recordType){
10261     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10262 };
10263
10264 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10265     /**
10266      * Create a data block containing Roo.data.Records from an XML document.
10267      * @param {Object} o An Array of row objects which represents the dataset.
10268      * @return {Object} data A data block which is used by an Roo.data.Store object as
10269      * a cache of Roo.data.Records.
10270      */
10271     readRecords : function(o){
10272         var sid = this.meta ? this.meta.id : null;
10273         var recordType = this.recordType, fields = recordType.prototype.fields;
10274         var records = [];
10275         var root = o;
10276             for(var i = 0; i < root.length; i++){
10277                     var n = root[i];
10278                 var values = {};
10279                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10280                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10281                 var f = fields.items[j];
10282                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10283                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10284                 v = f.convert(v);
10285                 values[f.name] = v;
10286             }
10287                 var record = new recordType(values, id);
10288                 record.json = n;
10289                 records[records.length] = record;
10290             }
10291             return {
10292                 records : records,
10293                 totalRecords : records.length
10294             };
10295     }
10296 });/*
10297  * - LGPL
10298  * * 
10299  */
10300
10301 /**
10302  * @class Roo.bootstrap.ComboBox
10303  * @extends Roo.bootstrap.TriggerField
10304  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10305  * @cfg {Boolean} append (true|false) default false
10306  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10307  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10308  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10309  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10310  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10311  * @constructor
10312  * Create a new ComboBox.
10313  * @param {Object} config Configuration options
10314  */
10315 Roo.bootstrap.ComboBox = function(config){
10316     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10317     this.addEvents({
10318         /**
10319          * @event expand
10320          * Fires when the dropdown list is expanded
10321              * @param {Roo.bootstrap.ComboBox} combo This combo box
10322              */
10323         'expand' : true,
10324         /**
10325          * @event collapse
10326          * Fires when the dropdown list is collapsed
10327              * @param {Roo.bootstrap.ComboBox} combo This combo box
10328              */
10329         'collapse' : true,
10330         /**
10331          * @event beforeselect
10332          * Fires before a list item is selected. Return false to cancel the selection.
10333              * @param {Roo.bootstrap.ComboBox} combo This combo box
10334              * @param {Roo.data.Record} record The data record returned from the underlying store
10335              * @param {Number} index The index of the selected item in the dropdown list
10336              */
10337         'beforeselect' : true,
10338         /**
10339          * @event select
10340          * Fires when a list item is selected
10341              * @param {Roo.bootstrap.ComboBox} combo This combo box
10342              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10343              * @param {Number} index The index of the selected item in the dropdown list
10344              */
10345         'select' : true,
10346         /**
10347          * @event beforequery
10348          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10349          * The event object passed has these properties:
10350              * @param {Roo.bootstrap.ComboBox} combo This combo box
10351              * @param {String} query The query
10352              * @param {Boolean} forceAll true to force "all" query
10353              * @param {Boolean} cancel true to cancel the query
10354              * @param {Object} e The query event object
10355              */
10356         'beforequery': true,
10357          /**
10358          * @event add
10359          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10360              * @param {Roo.bootstrap.ComboBox} combo This combo box
10361              */
10362         'add' : true,
10363         /**
10364          * @event edit
10365          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10366              * @param {Roo.bootstrap.ComboBox} combo This combo box
10367              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10368              */
10369         'edit' : true,
10370         /**
10371          * @event remove
10372          * Fires when the remove value from the combobox array
10373              * @param {Roo.bootstrap.ComboBox} combo This combo box
10374              */
10375         'remove' : true
10376         
10377     });
10378     
10379     this.item = [];
10380     this.tickItems = [];
10381     
10382     this.selectedIndex = -1;
10383     if(this.mode == 'local'){
10384         if(config.queryDelay === undefined){
10385             this.queryDelay = 10;
10386         }
10387         if(config.minChars === undefined){
10388             this.minChars = 0;
10389         }
10390     }
10391 };
10392
10393 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10394      
10395     /**
10396      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10397      * rendering into an Roo.Editor, defaults to false)
10398      */
10399     /**
10400      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10401      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10402      */
10403     /**
10404      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10405      */
10406     /**
10407      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10408      * the dropdown list (defaults to undefined, with no header element)
10409      */
10410
10411      /**
10412      * @cfg {String/Roo.Template} tpl The template to use to render the output
10413      */
10414      
10415      /**
10416      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10417      */
10418     listWidth: undefined,
10419     /**
10420      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10421      * mode = 'remote' or 'text' if mode = 'local')
10422      */
10423     displayField: undefined,
10424     /**
10425      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10426      * mode = 'remote' or 'value' if mode = 'local'). 
10427      * Note: use of a valueField requires the user make a selection
10428      * in order for a value to be mapped.
10429      */
10430     valueField: undefined,
10431     
10432     
10433     /**
10434      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10435      * field's data value (defaults to the underlying DOM element's name)
10436      */
10437     hiddenName: undefined,
10438     /**
10439      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10440      */
10441     listClass: '',
10442     /**
10443      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10444      */
10445     selectedClass: 'active',
10446     
10447     /**
10448      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10449      */
10450     shadow:'sides',
10451     /**
10452      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10453      * anchor positions (defaults to 'tl-bl')
10454      */
10455     listAlign: 'tl-bl?',
10456     /**
10457      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10458      */
10459     maxHeight: 300,
10460     /**
10461      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10462      * query specified by the allQuery config option (defaults to 'query')
10463      */
10464     triggerAction: 'query',
10465     /**
10466      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10467      * (defaults to 4, does not apply if editable = false)
10468      */
10469     minChars : 4,
10470     /**
10471      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10472      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10473      */
10474     typeAhead: false,
10475     /**
10476      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10477      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10478      */
10479     queryDelay: 500,
10480     /**
10481      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10482      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10483      */
10484     pageSize: 0,
10485     /**
10486      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10487      * when editable = true (defaults to false)
10488      */
10489     selectOnFocus:false,
10490     /**
10491      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10492      */
10493     queryParam: 'query',
10494     /**
10495      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10496      * when mode = 'remote' (defaults to 'Loading...')
10497      */
10498     loadingText: 'Loading...',
10499     /**
10500      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10501      */
10502     resizable: false,
10503     /**
10504      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10505      */
10506     handleHeight : 8,
10507     /**
10508      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10509      * traditional select (defaults to true)
10510      */
10511     editable: true,
10512     /**
10513      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10514      */
10515     allQuery: '',
10516     /**
10517      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10518      */
10519     mode: 'remote',
10520     /**
10521      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10522      * listWidth has a higher value)
10523      */
10524     minListWidth : 70,
10525     /**
10526      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10527      * allow the user to set arbitrary text into the field (defaults to false)
10528      */
10529     forceSelection:false,
10530     /**
10531      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10532      * if typeAhead = true (defaults to 250)
10533      */
10534     typeAheadDelay : 250,
10535     /**
10536      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10537      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10538      */
10539     valueNotFoundText : undefined,
10540     /**
10541      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10542      */
10543     blockFocus : false,
10544     
10545     /**
10546      * @cfg {Boolean} disableClear Disable showing of clear button.
10547      */
10548     disableClear : false,
10549     /**
10550      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10551      */
10552     alwaysQuery : false,
10553     
10554     /**
10555      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10556      */
10557     multiple : false,
10558     
10559     //private
10560     addicon : false,
10561     editicon: false,
10562     
10563     page: 0,
10564     hasQuery: false,
10565     append: false,
10566     loadNext: false,
10567     autoFocus : true,
10568     tickable : false,
10569     btnPosition : 'right',
10570     triggerList : true,
10571     showToggleBtn : true,
10572     // element that contains real text value.. (when hidden is used..)
10573     
10574     getAutoCreate : function()
10575     {
10576         var cfg = false;
10577         
10578         /*
10579          *  Normal ComboBox
10580          */
10581         if(!this.tickable){
10582             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10583             return cfg;
10584         }
10585         
10586         /*
10587          *  ComboBox with tickable selections
10588          */
10589              
10590         var align = this.labelAlign || this.parentLabelAlign();
10591         
10592         cfg = {
10593             cls : 'form-group roo-combobox-tickable' //input-group
10594         };
10595         
10596         
10597         var buttons = {
10598             tag : 'div',
10599             cls : 'tickable-buttons',
10600             cn : [
10601                 {
10602                     tag : 'button',
10603                     type : 'button',
10604                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10605                     html : 'Edit'
10606                 },
10607                 {
10608                     tag : 'button',
10609                     type : 'button',
10610                     name : 'ok',
10611                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10612                     html : 'Done'
10613                 },
10614                 {
10615                     tag : 'button',
10616                     type : 'button',
10617                     name : 'cancel',
10618                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10619                     html : 'Cancel'
10620                 }
10621             ]
10622         };
10623         
10624         var _this = this;
10625         Roo.each(buttons.cn, function(c){
10626             if (_this.size) {
10627                 c.cls += ' btn-' + _this.size;
10628             }
10629
10630             if (_this.disabled) {
10631                 c.disabled = true;
10632             }
10633         });
10634         
10635         var box = {
10636             tag: 'div',
10637             cn: [
10638                 {
10639                     tag: 'input',
10640                     type : 'hidden',
10641                     cls: 'form-hidden-field'
10642                 },
10643                 {
10644                     tag: 'ul',
10645                     cls: 'select2-choices',
10646                     cn:[
10647                         {
10648                             tag: 'li',
10649                             cls: 'select2-search-field',
10650                             cn: [
10651
10652                                 buttons
10653                             ]
10654                         }
10655                     ]
10656                 }
10657             ]
10658         }
10659         
10660         var combobox = {
10661             cls: 'select2-container input-group select2-container-multi',
10662             cn: [
10663                 box
10664 //                {
10665 //                    tag: 'ul',
10666 //                    cls: 'typeahead typeahead-long dropdown-menu',
10667 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10668 //                }
10669             ]
10670         };
10671         
10672         if (align ==='left' && this.fieldLabel.length) {
10673             
10674                 Roo.log("left and has label");
10675                 cfg.cn = [
10676                     
10677                     {
10678                         tag: 'label',
10679                         'for' :  id,
10680                         cls : 'control-label col-sm-' + this.labelWidth,
10681                         html : this.fieldLabel
10682                         
10683                     },
10684                     {
10685                         cls : "col-sm-" + (12 - this.labelWidth), 
10686                         cn: [
10687                             combobox
10688                         ]
10689                     }
10690                     
10691                 ];
10692         } else if ( this.fieldLabel.length) {
10693                 Roo.log(" label");
10694                  cfg.cn = [
10695                    
10696                     {
10697                         tag: 'label',
10698                         //cls : 'input-group-addon',
10699                         html : this.fieldLabel
10700                         
10701                     },
10702                     
10703                     combobox
10704                     
10705                 ];
10706
10707         } else {
10708             
10709                 Roo.log(" no label && no align");
10710                 cfg = combobox
10711                      
10712                 
10713         }
10714          
10715         var settings=this;
10716         ['xs','sm','md','lg'].map(function(size){
10717             if (settings[size]) {
10718                 cfg.cls += ' col-' + size + '-' + settings[size];
10719             }
10720         });
10721         
10722         return cfg;
10723         
10724     },
10725     
10726     // private
10727     initEvents: function()
10728     {
10729         
10730         if (!this.store) {
10731             throw "can not find store for combo";
10732         }
10733         this.store = Roo.factory(this.store, Roo.data);
10734         
10735         if(this.tickable){
10736             this.initTickableEvents();
10737             return;
10738         }
10739         
10740         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10741         
10742         if(this.hiddenName){
10743             
10744             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10745             
10746             this.hiddenField.dom.value =
10747                 this.hiddenValue !== undefined ? this.hiddenValue :
10748                 this.value !== undefined ? this.value : '';
10749
10750             // prevent input submission
10751             this.el.dom.removeAttribute('name');
10752             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10753              
10754              
10755         }
10756         //if(Roo.isGecko){
10757         //    this.el.dom.setAttribute('autocomplete', 'off');
10758         //}
10759         
10760         var cls = 'x-combo-list';
10761         
10762         //this.list = new Roo.Layer({
10763         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10764         //});
10765         
10766         var _this = this;
10767         
10768         (function(){
10769             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10770             _this.list.setWidth(lw);
10771         }).defer(100);
10772         
10773         this.list.on('mouseover', this.onViewOver, this);
10774         this.list.on('mousemove', this.onViewMove, this);
10775         
10776         this.list.on('scroll', this.onViewScroll, this);
10777         
10778         /*
10779         this.list.swallowEvent('mousewheel');
10780         this.assetHeight = 0;
10781
10782         if(this.title){
10783             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10784             this.assetHeight += this.header.getHeight();
10785         }
10786
10787         this.innerList = this.list.createChild({cls:cls+'-inner'});
10788         this.innerList.on('mouseover', this.onViewOver, this);
10789         this.innerList.on('mousemove', this.onViewMove, this);
10790         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10791         
10792         if(this.allowBlank && !this.pageSize && !this.disableClear){
10793             this.footer = this.list.createChild({cls:cls+'-ft'});
10794             this.pageTb = new Roo.Toolbar(this.footer);
10795            
10796         }
10797         if(this.pageSize){
10798             this.footer = this.list.createChild({cls:cls+'-ft'});
10799             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10800                     {pageSize: this.pageSize});
10801             
10802         }
10803         
10804         if (this.pageTb && this.allowBlank && !this.disableClear) {
10805             var _this = this;
10806             this.pageTb.add(new Roo.Toolbar.Fill(), {
10807                 cls: 'x-btn-icon x-btn-clear',
10808                 text: '&#160;',
10809                 handler: function()
10810                 {
10811                     _this.collapse();
10812                     _this.clearValue();
10813                     _this.onSelect(false, -1);
10814                 }
10815             });
10816         }
10817         if (this.footer) {
10818             this.assetHeight += this.footer.getHeight();
10819         }
10820         */
10821             
10822         if(!this.tpl){
10823             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10824         }
10825
10826         this.view = new Roo.View(this.list, this.tpl, {
10827             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10828         });
10829         //this.view.wrapEl.setDisplayed(false);
10830         this.view.on('click', this.onViewClick, this);
10831         
10832         
10833         
10834         this.store.on('beforeload', this.onBeforeLoad, this);
10835         this.store.on('load', this.onLoad, this);
10836         this.store.on('loadexception', this.onLoadException, this);
10837         /*
10838         if(this.resizable){
10839             this.resizer = new Roo.Resizable(this.list,  {
10840                pinned:true, handles:'se'
10841             });
10842             this.resizer.on('resize', function(r, w, h){
10843                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10844                 this.listWidth = w;
10845                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10846                 this.restrictHeight();
10847             }, this);
10848             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10849         }
10850         */
10851         if(!this.editable){
10852             this.editable = true;
10853             this.setEditable(false);
10854         }
10855         
10856         /*
10857         
10858         if (typeof(this.events.add.listeners) != 'undefined') {
10859             
10860             this.addicon = this.wrap.createChild(
10861                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10862        
10863             this.addicon.on('click', function(e) {
10864                 this.fireEvent('add', this);
10865             }, this);
10866         }
10867         if (typeof(this.events.edit.listeners) != 'undefined') {
10868             
10869             this.editicon = this.wrap.createChild(
10870                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10871             if (this.addicon) {
10872                 this.editicon.setStyle('margin-left', '40px');
10873             }
10874             this.editicon.on('click', function(e) {
10875                 
10876                 // we fire even  if inothing is selected..
10877                 this.fireEvent('edit', this, this.lastData );
10878                 
10879             }, this);
10880         }
10881         */
10882         
10883         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10884             "up" : function(e){
10885                 this.inKeyMode = true;
10886                 this.selectPrev();
10887             },
10888
10889             "down" : function(e){
10890                 if(!this.isExpanded()){
10891                     this.onTriggerClick();
10892                 }else{
10893                     this.inKeyMode = true;
10894                     this.selectNext();
10895                 }
10896             },
10897
10898             "enter" : function(e){
10899 //                this.onViewClick();
10900                 //return true;
10901                 this.collapse();
10902                 
10903                 if(this.fireEvent("specialkey", this, e)){
10904                     this.onViewClick(false);
10905                 }
10906                 
10907                 return true;
10908             },
10909
10910             "esc" : function(e){
10911                 this.collapse();
10912             },
10913
10914             "tab" : function(e){
10915                 this.collapse();
10916                 
10917                 if(this.fireEvent("specialkey", this, e)){
10918                     this.onViewClick(false);
10919                 }
10920                 
10921                 return true;
10922             },
10923
10924             scope : this,
10925
10926             doRelay : function(foo, bar, hname){
10927                 if(hname == 'down' || this.scope.isExpanded()){
10928                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10929                 }
10930                 return true;
10931             },
10932
10933             forceKeyDown: true
10934         });
10935         
10936         
10937         this.queryDelay = Math.max(this.queryDelay || 10,
10938                 this.mode == 'local' ? 10 : 250);
10939         
10940         
10941         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10942         
10943         if(this.typeAhead){
10944             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10945         }
10946         if(this.editable !== false){
10947             this.inputEl().on("keyup", this.onKeyUp, this);
10948         }
10949         if(this.forceSelection){
10950             this.inputEl().on('blur', this.doForce, this);
10951         }
10952         
10953         if(this.multiple){
10954             this.choices = this.el.select('ul.select2-choices', true).first();
10955             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10956         }
10957     },
10958     
10959     initTickableEvents: function()
10960     {   
10961         this.createList();
10962         
10963         if(this.hiddenName){
10964             
10965             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10966             
10967             this.hiddenField.dom.value =
10968                 this.hiddenValue !== undefined ? this.hiddenValue :
10969                 this.value !== undefined ? this.value : '';
10970
10971             // prevent input submission
10972             this.el.dom.removeAttribute('name');
10973             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10974              
10975              
10976         }
10977         
10978 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10979         
10980         this.choices = this.el.select('ul.select2-choices', true).first();
10981         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10982         if(this.triggerList){
10983             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10984         }
10985          
10986         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10987         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10988         
10989         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10990         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10991         
10992         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10993         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10994         
10995         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10996         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10997         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10998         
10999         this.okBtn.hide();
11000         this.cancelBtn.hide();
11001         
11002         var _this = this;
11003         
11004         (function(){
11005             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11006             _this.list.setWidth(lw);
11007         }).defer(100);
11008         
11009         this.list.on('mouseover', this.onViewOver, this);
11010         this.list.on('mousemove', this.onViewMove, this);
11011         
11012         this.list.on('scroll', this.onViewScroll, this);
11013         
11014         if(!this.tpl){
11015             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11016         }
11017
11018         this.view = new Roo.View(this.list, this.tpl, {
11019             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11020         });
11021         
11022         //this.view.wrapEl.setDisplayed(false);
11023         this.view.on('click', this.onViewClick, this);
11024         
11025         
11026         
11027         this.store.on('beforeload', this.onBeforeLoad, this);
11028         this.store.on('load', this.onLoad, this);
11029         this.store.on('loadexception', this.onLoadException, this);
11030         
11031 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11032 //            "up" : function(e){
11033 //                this.inKeyMode = true;
11034 //                this.selectPrev();
11035 //            },
11036 //
11037 //            "down" : function(e){
11038 //                if(!this.isExpanded()){
11039 //                    this.onTriggerClick();
11040 //                }else{
11041 //                    this.inKeyMode = true;
11042 //                    this.selectNext();
11043 //                }
11044 //            },
11045 //
11046 //            "enter" : function(e){
11047 ////                this.onViewClick();
11048 //                //return true;
11049 //                this.collapse();
11050 //                
11051 //                if(this.fireEvent("specialkey", this, e)){
11052 //                    this.onViewClick(false);
11053 //                }
11054 //                
11055 //                return true;
11056 //            },
11057 //
11058 //            "esc" : function(e){
11059 //                this.collapse();
11060 //            },
11061 //
11062 //            "tab" : function(e){
11063 //                this.collapse();
11064 //                
11065 //                if(this.fireEvent("specialkey", this, e)){
11066 //                    this.onViewClick(false);
11067 //                }
11068 //                
11069 //                return true;
11070 //            },
11071 //
11072 //            scope : this,
11073 //
11074 //            doRelay : function(foo, bar, hname){
11075 //                if(hname == 'down' || this.scope.isExpanded()){
11076 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11077 //                }
11078 //                return true;
11079 //            },
11080 //
11081 //            forceKeyDown: true
11082 //        });
11083         
11084         
11085         this.queryDelay = Math.max(this.queryDelay || 10,
11086                 this.mode == 'local' ? 10 : 250);
11087         
11088         
11089         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11090         
11091         if(this.typeAhead){
11092             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11093         }
11094     },
11095
11096     onDestroy : function(){
11097         if(this.view){
11098             this.view.setStore(null);
11099             this.view.el.removeAllListeners();
11100             this.view.el.remove();
11101             this.view.purgeListeners();
11102         }
11103         if(this.list){
11104             this.list.dom.innerHTML  = '';
11105         }
11106         
11107         if(this.store){
11108             this.store.un('beforeload', this.onBeforeLoad, this);
11109             this.store.un('load', this.onLoad, this);
11110             this.store.un('loadexception', this.onLoadException, this);
11111         }
11112         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11113     },
11114
11115     // private
11116     fireKey : function(e){
11117         if(e.isNavKeyPress() && !this.list.isVisible()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121
11122     // private
11123     onResize: function(w, h){
11124 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11125 //        
11126 //        if(typeof w != 'number'){
11127 //            // we do not handle it!?!?
11128 //            return;
11129 //        }
11130 //        var tw = this.trigger.getWidth();
11131 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11132 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11133 //        var x = w - tw;
11134 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11135 //            
11136 //        //this.trigger.setStyle('left', x+'px');
11137 //        
11138 //        if(this.list && this.listWidth === undefined){
11139 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11140 //            this.list.setWidth(lw);
11141 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11142 //        }
11143         
11144     
11145         
11146     },
11147
11148     /**
11149      * Allow or prevent the user from directly editing the field text.  If false is passed,
11150      * the user will only be able to select from the items defined in the dropdown list.  This method
11151      * is the runtime equivalent of setting the 'editable' config option at config time.
11152      * @param {Boolean} value True to allow the user to directly edit the field text
11153      */
11154     setEditable : function(value){
11155         if(value == this.editable){
11156             return;
11157         }
11158         this.editable = value;
11159         if(!value){
11160             this.inputEl().dom.setAttribute('readOnly', true);
11161             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11162             this.inputEl().addClass('x-combo-noedit');
11163         }else{
11164             this.inputEl().dom.setAttribute('readOnly', false);
11165             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11166             this.inputEl().removeClass('x-combo-noedit');
11167         }
11168     },
11169
11170     // private
11171     
11172     onBeforeLoad : function(combo,opts){
11173         if(!this.hasFocus){
11174             return;
11175         }
11176          if (!opts.add) {
11177             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11178          }
11179         this.restrictHeight();
11180         this.selectedIndex = -1;
11181     },
11182
11183     // private
11184     onLoad : function(){
11185         
11186         this.hasQuery = false;
11187         
11188         if(!this.hasFocus){
11189             return;
11190         }
11191         
11192         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11193             this.loading.hide();
11194         }
11195         
11196         if(this.store.getCount() > 0){
11197             this.expand();
11198 //            this.restrictHeight();
11199             if(this.lastQuery == this.allQuery){
11200                 if(this.editable && !this.tickable){
11201                     this.inputEl().dom.select();
11202                 }
11203                 
11204                 if(
11205                     !this.selectByValue(this.value, true) &&
11206                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11207                     this.store.lastOptions.add != true)
11208                 ){
11209                     this.select(0, true);
11210                 }
11211             }else{
11212                 if(this.autoFocus){
11213                     this.selectNext();
11214                 }
11215                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11216                     this.taTask.delay(this.typeAheadDelay);
11217                 }
11218             }
11219         }else{
11220             this.onEmptyResults();
11221         }
11222         
11223         //this.el.focus();
11224     },
11225     // private
11226     onLoadException : function()
11227     {
11228         this.hasQuery = false;
11229         
11230         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11231             this.loading.hide();
11232         }
11233         
11234         this.collapse();
11235         Roo.log(this.store.reader.jsonData);
11236         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11237             // fixme
11238             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11239         }
11240         
11241         
11242     },
11243     // private
11244     onTypeAhead : function(){
11245         if(this.store.getCount() > 0){
11246             var r = this.store.getAt(0);
11247             var newValue = r.data[this.displayField];
11248             var len = newValue.length;
11249             var selStart = this.getRawValue().length;
11250             
11251             if(selStart != len){
11252                 this.setRawValue(newValue);
11253                 this.selectText(selStart, newValue.length);
11254             }
11255         }
11256     },
11257
11258     // private
11259     onSelect : function(record, index){
11260         
11261         if(this.fireEvent('beforeselect', this, record, index) !== false){
11262         
11263             this.setFromData(index > -1 ? record.data : false);
11264             
11265             this.collapse();
11266             this.fireEvent('select', this, record, index);
11267         }
11268     },
11269
11270     /**
11271      * Returns the currently selected field value or empty string if no value is set.
11272      * @return {String} value The selected value
11273      */
11274     getValue : function(){
11275         
11276         if(this.multiple){
11277             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11278         }
11279         
11280         if(this.valueField){
11281             return typeof this.value != 'undefined' ? this.value : '';
11282         }else{
11283             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11284         }
11285     },
11286
11287     /**
11288      * Clears any text/value currently set in the field
11289      */
11290     clearValue : function(){
11291         if(this.hiddenField){
11292             this.hiddenField.dom.value = '';
11293         }
11294         this.value = '';
11295         this.setRawValue('');
11296         this.lastSelectionText = '';
11297         
11298     },
11299
11300     /**
11301      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11302      * will be displayed in the field.  If the value does not match the data value of an existing item,
11303      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11304      * Otherwise the field will be blank (although the value will still be set).
11305      * @param {String} value The value to match
11306      */
11307     setValue : function(v){
11308         if(this.multiple){
11309             this.syncValue();
11310             return;
11311         }
11312         
11313         var text = v;
11314         if(this.valueField){
11315             var r = this.findRecord(this.valueField, v);
11316             if(r){
11317                 text = r.data[this.displayField];
11318             }else if(this.valueNotFoundText !== undefined){
11319                 text = this.valueNotFoundText;
11320             }
11321         }
11322         this.lastSelectionText = text;
11323         if(this.hiddenField){
11324             this.hiddenField.dom.value = v;
11325         }
11326         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11327         this.value = v;
11328     },
11329     /**
11330      * @property {Object} the last set data for the element
11331      */
11332     
11333     lastData : false,
11334     /**
11335      * Sets the value of the field based on a object which is related to the record format for the store.
11336      * @param {Object} value the value to set as. or false on reset?
11337      */
11338     setFromData : function(o){
11339         
11340         if(this.multiple){
11341             this.addItem(o);
11342             return;
11343         }
11344             
11345         var dv = ''; // display value
11346         var vv = ''; // value value..
11347         this.lastData = o;
11348         if (this.displayField) {
11349             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11350         } else {
11351             // this is an error condition!!!
11352             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11353         }
11354         
11355         if(this.valueField){
11356             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11357         }
11358         
11359         if(this.hiddenField){
11360             this.hiddenField.dom.value = vv;
11361             
11362             this.lastSelectionText = dv;
11363             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11364             this.value = vv;
11365             return;
11366         }
11367         // no hidden field.. - we store the value in 'value', but still display
11368         // display field!!!!
11369         this.lastSelectionText = dv;
11370         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11371         this.value = vv;
11372         
11373         
11374     },
11375     // private
11376     reset : function(){
11377         // overridden so that last data is reset..
11378         this.setValue(this.originalValue);
11379         this.clearInvalid();
11380         this.lastData = false;
11381         if (this.view) {
11382             this.view.clearSelections();
11383         }
11384     },
11385     // private
11386     findRecord : function(prop, value){
11387         var record;
11388         if(this.store.getCount() > 0){
11389             this.store.each(function(r){
11390                 if(r.data[prop] == value){
11391                     record = r;
11392                     return false;
11393                 }
11394                 return true;
11395             });
11396         }
11397         return record;
11398     },
11399     
11400     getName: function()
11401     {
11402         // returns hidden if it's set..
11403         if (!this.rendered) {return ''};
11404         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11405         
11406     },
11407     // private
11408     onViewMove : function(e, t){
11409         this.inKeyMode = false;
11410     },
11411
11412     // private
11413     onViewOver : function(e, t){
11414         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11415             return;
11416         }
11417         var item = this.view.findItemFromChild(t);
11418         
11419         if(item){
11420             var index = this.view.indexOf(item);
11421             this.select(index, false);
11422         }
11423     },
11424
11425     // private
11426     onViewClick : function(view, doFocus, el, e)
11427     {
11428         var index = this.view.getSelectedIndexes()[0];
11429         
11430         var r = this.store.getAt(index);
11431         
11432         if(this.tickable){
11433             
11434             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11435                 return;
11436             }
11437             
11438             var rm = false;
11439             var _this = this;
11440             
11441             Roo.each(this.tickItems, function(v,k){
11442                 
11443                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11444                     _this.tickItems.splice(k, 1);
11445                     rm = true;
11446                     return;
11447                 }
11448             })
11449             
11450             if(rm){
11451                 return;
11452             }
11453             
11454             this.tickItems.push(r.data);
11455             return;
11456         }
11457         
11458         if(r){
11459             this.onSelect(r, index);
11460         }
11461         if(doFocus !== false && !this.blockFocus){
11462             this.inputEl().focus();
11463         }
11464     },
11465
11466     // private
11467     restrictHeight : function(){
11468         //this.innerList.dom.style.height = '';
11469         //var inner = this.innerList.dom;
11470         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11471         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11472         //this.list.beginUpdate();
11473         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11474         this.list.alignTo(this.inputEl(), this.listAlign);
11475         this.list.alignTo(this.inputEl(), this.listAlign);
11476         //this.list.endUpdate();
11477     },
11478
11479     // private
11480     onEmptyResults : function(){
11481         this.collapse();
11482     },
11483
11484     /**
11485      * Returns true if the dropdown list is expanded, else false.
11486      */
11487     isExpanded : function(){
11488         return this.list.isVisible();
11489     },
11490
11491     /**
11492      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11493      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11494      * @param {String} value The data value of the item to select
11495      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11496      * selected item if it is not currently in view (defaults to true)
11497      * @return {Boolean} True if the value matched an item in the list, else false
11498      */
11499     selectByValue : function(v, scrollIntoView){
11500         if(v !== undefined && v !== null){
11501             var r = this.findRecord(this.valueField || this.displayField, v);
11502             if(r){
11503                 this.select(this.store.indexOf(r), scrollIntoView);
11504                 return true;
11505             }
11506         }
11507         return false;
11508     },
11509
11510     /**
11511      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11512      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11513      * @param {Number} index The zero-based index of the list item to select
11514      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11515      * selected item if it is not currently in view (defaults to true)
11516      */
11517     select : function(index, scrollIntoView){
11518         this.selectedIndex = index;
11519         this.view.select(index);
11520         if(scrollIntoView !== false){
11521             var el = this.view.getNode(index);
11522             if(el && !this.multiple && !this.tickable){
11523                 this.list.scrollChildIntoView(el, false);
11524             }
11525         }
11526     },
11527
11528     // private
11529     selectNext : function(){
11530         var ct = this.store.getCount();
11531         if(ct > 0){
11532             if(this.selectedIndex == -1){
11533                 this.select(0);
11534             }else if(this.selectedIndex < ct-1){
11535                 this.select(this.selectedIndex+1);
11536             }
11537         }
11538     },
11539
11540     // private
11541     selectPrev : function(){
11542         var ct = this.store.getCount();
11543         if(ct > 0){
11544             if(this.selectedIndex == -1){
11545                 this.select(0);
11546             }else if(this.selectedIndex != 0){
11547                 this.select(this.selectedIndex-1);
11548             }
11549         }
11550     },
11551
11552     // private
11553     onKeyUp : function(e){
11554         if(this.editable !== false && !e.isSpecialKey()){
11555             this.lastKey = e.getKey();
11556             this.dqTask.delay(this.queryDelay);
11557         }
11558     },
11559
11560     // private
11561     validateBlur : function(){
11562         return !this.list || !this.list.isVisible();   
11563     },
11564
11565     // private
11566     initQuery : function(){
11567         this.doQuery(this.getRawValue());
11568     },
11569
11570     // private
11571     doForce : function(){
11572         if(this.inputEl().dom.value.length > 0){
11573             this.inputEl().dom.value =
11574                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11575              
11576         }
11577     },
11578
11579     /**
11580      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11581      * query allowing the query action to be canceled if needed.
11582      * @param {String} query The SQL query to execute
11583      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11584      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11585      * saved in the current store (defaults to false)
11586      */
11587     doQuery : function(q, forceAll){
11588         
11589         if(q === undefined || q === null){
11590             q = '';
11591         }
11592         var qe = {
11593             query: q,
11594             forceAll: forceAll,
11595             combo: this,
11596             cancel:false
11597         };
11598         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11599             return false;
11600         }
11601         q = qe.query;
11602         
11603         forceAll = qe.forceAll;
11604         if(forceAll === true || (q.length >= this.minChars)){
11605             
11606             this.hasQuery = true;
11607             
11608             if(this.lastQuery != q || this.alwaysQuery){
11609                 this.lastQuery = q;
11610                 if(this.mode == 'local'){
11611                     this.selectedIndex = -1;
11612                     if(forceAll){
11613                         this.store.clearFilter();
11614                     }else{
11615                         this.store.filter(this.displayField, q);
11616                     }
11617                     this.onLoad();
11618                 }else{
11619                     this.store.baseParams[this.queryParam] = q;
11620                     
11621                     var options = {params : this.getParams(q)};
11622                     
11623                     if(this.loadNext){
11624                         options.add = true;
11625                         options.params.start = this.page * this.pageSize;
11626                     }
11627                     
11628                     this.store.load(options);
11629                     /*
11630                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11631                      *  we should expand the list on onLoad
11632                      *  so command out it
11633                      */
11634 //                    this.expand();
11635                 }
11636             }else{
11637                 this.selectedIndex = -1;
11638                 this.onLoad();   
11639             }
11640         }
11641         
11642         this.loadNext = false;
11643     },
11644
11645     // private
11646     getParams : function(q){
11647         var p = {};
11648         //p[this.queryParam] = q;
11649         
11650         if(this.pageSize){
11651             p.start = 0;
11652             p.limit = this.pageSize;
11653         }
11654         return p;
11655     },
11656
11657     /**
11658      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11659      */
11660     collapse : function(){
11661         if(!this.isExpanded()){
11662             return;
11663         }
11664         
11665         this.list.hide();
11666         
11667         if(this.tickable){
11668             this.okBtn.hide();
11669             this.cancelBtn.hide();
11670             this.trigger.show();
11671         }
11672         
11673         Roo.get(document).un('mousedown', this.collapseIf, this);
11674         Roo.get(document).un('mousewheel', this.collapseIf, this);
11675         if (!this.editable) {
11676             Roo.get(document).un('keydown', this.listKeyPress, this);
11677         }
11678         this.fireEvent('collapse', this);
11679     },
11680
11681     // private
11682     collapseIf : function(e){
11683         var in_combo  = e.within(this.el);
11684         var in_list =  e.within(this.list);
11685         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11686         
11687         if (in_combo || in_list || is_list) {
11688             //e.stopPropagation();
11689             return;
11690         }
11691         
11692         if(this.tickable){
11693             this.onTickableFooterButtonClick(e, false, false);
11694         }
11695
11696         this.collapse();
11697         
11698     },
11699
11700     /**
11701      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11702      */
11703     expand : function(){
11704        
11705         if(this.isExpanded() || !this.hasFocus){
11706             return;
11707         }
11708         
11709         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11710         this.list.setWidth(lw);
11711         
11712         
11713          Roo.log('expand');
11714         
11715         this.list.show();
11716         
11717         this.restrictHeight();
11718         
11719         if(this.tickable){
11720             
11721             this.tickItems = Roo.apply([], this.item);
11722             
11723             this.okBtn.show();
11724             this.cancelBtn.show();
11725             this.trigger.hide();
11726             
11727         }
11728         
11729         Roo.get(document).on('mousedown', this.collapseIf, this);
11730         Roo.get(document).on('mousewheel', this.collapseIf, this);
11731         if (!this.editable) {
11732             Roo.get(document).on('keydown', this.listKeyPress, this);
11733         }
11734         
11735         this.fireEvent('expand', this);
11736     },
11737
11738     // private
11739     // Implements the default empty TriggerField.onTriggerClick function
11740     onTriggerClick : function(e)
11741     {
11742         Roo.log('trigger click');
11743         
11744         if(this.disabled || !this.triggerList){
11745             return;
11746         }
11747         
11748         this.page = 0;
11749         this.loadNext = false;
11750         
11751         if(this.isExpanded()){
11752             this.collapse();
11753             if (!this.blockFocus) {
11754                 this.inputEl().focus();
11755             }
11756             
11757         }else {
11758             this.hasFocus = true;
11759             if(this.triggerAction == 'all') {
11760                 this.doQuery(this.allQuery, true);
11761             } else {
11762                 this.doQuery(this.getRawValue());
11763             }
11764             if (!this.blockFocus) {
11765                 this.inputEl().focus();
11766             }
11767         }
11768     },
11769     
11770     onTickableTriggerClick : function(e)
11771     {
11772         if(this.disabled){
11773             return;
11774         }
11775         
11776         this.page = 0;
11777         this.loadNext = false;
11778         this.hasFocus = true;
11779         
11780         if(this.triggerAction == 'all') {
11781             this.doQuery(this.allQuery, true);
11782         } else {
11783             this.doQuery(this.getRawValue());
11784         }
11785     },
11786     
11787     onSearchFieldClick : function(e)
11788     {
11789         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11790             return;
11791         }
11792         
11793         this.page = 0;
11794         this.loadNext = false;
11795         this.hasFocus = true;
11796         
11797         if(this.triggerAction == 'all') {
11798             this.doQuery(this.allQuery, true);
11799         } else {
11800             this.doQuery(this.getRawValue());
11801         }
11802     },
11803     
11804     listKeyPress : function(e)
11805     {
11806         //Roo.log('listkeypress');
11807         // scroll to first matching element based on key pres..
11808         if (e.isSpecialKey()) {
11809             return false;
11810         }
11811         var k = String.fromCharCode(e.getKey()).toUpperCase();
11812         //Roo.log(k);
11813         var match  = false;
11814         var csel = this.view.getSelectedNodes();
11815         var cselitem = false;
11816         if (csel.length) {
11817             var ix = this.view.indexOf(csel[0]);
11818             cselitem  = this.store.getAt(ix);
11819             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11820                 cselitem = false;
11821             }
11822             
11823         }
11824         
11825         this.store.each(function(v) { 
11826             if (cselitem) {
11827                 // start at existing selection.
11828                 if (cselitem.id == v.id) {
11829                     cselitem = false;
11830                 }
11831                 return true;
11832             }
11833                 
11834             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11835                 match = this.store.indexOf(v);
11836                 return false;
11837             }
11838             return true;
11839         }, this);
11840         
11841         if (match === false) {
11842             return true; // no more action?
11843         }
11844         // scroll to?
11845         this.view.select(match);
11846         var sn = Roo.get(this.view.getSelectedNodes()[0])
11847         //sn.scrollIntoView(sn.dom.parentNode, false);
11848     },
11849     
11850     onViewScroll : function(e, t){
11851         
11852         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11853             return;
11854         }
11855         
11856         this.hasQuery = true;
11857         
11858         this.loading = this.list.select('.loading', true).first();
11859         
11860         if(this.loading === null){
11861             this.list.createChild({
11862                 tag: 'div',
11863                 cls: 'loading select2-more-results select2-active',
11864                 html: 'Loading more results...'
11865             })
11866             
11867             this.loading = this.list.select('.loading', true).first();
11868             
11869             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11870             
11871             this.loading.hide();
11872         }
11873         
11874         this.loading.show();
11875         
11876         var _combo = this;
11877         
11878         this.page++;
11879         this.loadNext = true;
11880         
11881         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11882         
11883         return;
11884     },
11885     
11886     addItem : function(o)
11887     {   
11888         var dv = ''; // display value
11889         
11890         if (this.displayField) {
11891             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11892         } else {
11893             // this is an error condition!!!
11894             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11895         }
11896         
11897         if(!dv.length){
11898             return;
11899         }
11900         
11901         var choice = this.choices.createChild({
11902             tag: 'li',
11903             cls: 'select2-search-choice',
11904             cn: [
11905                 {
11906                     tag: 'div',
11907                     html: dv
11908                 },
11909                 {
11910                     tag: 'a',
11911                     href: '#',
11912                     cls: 'select2-search-choice-close',
11913                     tabindex: '-1'
11914                 }
11915             ]
11916             
11917         }, this.searchField);
11918         
11919         var close = choice.select('a.select2-search-choice-close', true).first()
11920         
11921         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11922         
11923         this.item.push(o);
11924         
11925         this.lastData = o;
11926         
11927         this.syncValue();
11928         
11929         this.inputEl().dom.value = '';
11930         
11931     },
11932     
11933     onRemoveItem : function(e, _self, o)
11934     {
11935         e.preventDefault();
11936         var index = this.item.indexOf(o.data) * 1;
11937         
11938         if( index < 0){
11939             Roo.log('not this item?!');
11940             return;
11941         }
11942         
11943         this.item.splice(index, 1);
11944         o.item.remove();
11945         
11946         this.syncValue();
11947         
11948         this.fireEvent('remove', this, e);
11949         
11950     },
11951     
11952     syncValue : function()
11953     {
11954         if(!this.item.length){
11955             this.clearValue();
11956             return;
11957         }
11958             
11959         var value = [];
11960         var _this = this;
11961         Roo.each(this.item, function(i){
11962             if(_this.valueField){
11963                 value.push(i[_this.valueField]);
11964                 return;
11965             }
11966
11967             value.push(i);
11968         });
11969
11970         this.value = value.join(',');
11971
11972         if(this.hiddenField){
11973             this.hiddenField.dom.value = this.value;
11974         }
11975     },
11976     
11977     clearItem : function()
11978     {
11979         if(!this.multiple){
11980             return;
11981         }
11982         
11983         this.item = [];
11984         
11985         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11986            c.remove();
11987         });
11988         
11989         this.syncValue();
11990     },
11991     
11992     inputEl: function ()
11993     {
11994         if(this.tickable){
11995             return this.searchField;
11996         }
11997         return this.el.select('input.form-control',true).first();
11998     },
11999     
12000     
12001     onTickableFooterButtonClick : function(e, btn, el)
12002     {
12003         e.preventDefault();
12004         
12005         if(btn && btn.name == 'cancel'){
12006             this.tickItems = Roo.apply([], this.item);
12007             this.collapse();
12008             return;
12009         }
12010         
12011         this.clearItem();
12012         
12013         var _this = this;
12014         
12015         Roo.each(this.tickItems, function(o){
12016             _this.addItem(o);
12017         });
12018         
12019         this.collapse();
12020         
12021     }
12022     
12023     
12024
12025     /** 
12026     * @cfg {Boolean} grow 
12027     * @hide 
12028     */
12029     /** 
12030     * @cfg {Number} growMin 
12031     * @hide 
12032     */
12033     /** 
12034     * @cfg {Number} growMax 
12035     * @hide 
12036     */
12037     /**
12038      * @hide
12039      * @method autoSize
12040      */
12041 });
12042 /*
12043  * Based on:
12044  * Ext JS Library 1.1.1
12045  * Copyright(c) 2006-2007, Ext JS, LLC.
12046  *
12047  * Originally Released Under LGPL - original licence link has changed is not relivant.
12048  *
12049  * Fork - LGPL
12050  * <script type="text/javascript">
12051  */
12052
12053 /**
12054  * @class Roo.View
12055  * @extends Roo.util.Observable
12056  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12057  * This class also supports single and multi selection modes. <br>
12058  * Create a data model bound view:
12059  <pre><code>
12060  var store = new Roo.data.Store(...);
12061
12062  var view = new Roo.View({
12063     el : "my-element",
12064     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12065  
12066     singleSelect: true,
12067     selectedClass: "ydataview-selected",
12068     store: store
12069  });
12070
12071  // listen for node click?
12072  view.on("click", function(vw, index, node, e){
12073  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12074  });
12075
12076  // load XML data
12077  dataModel.load("foobar.xml");
12078  </code></pre>
12079  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12080  * <br><br>
12081  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12082  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12083  * 
12084  * Note: old style constructor is still suported (container, template, config)
12085  * 
12086  * @constructor
12087  * Create a new View
12088  * @param {Object} config The config object
12089  * 
12090  */
12091 Roo.View = function(config, depreciated_tpl, depreciated_config){
12092     
12093     this.parent = false;
12094     
12095     if (typeof(depreciated_tpl) == 'undefined') {
12096         // new way.. - universal constructor.
12097         Roo.apply(this, config);
12098         this.el  = Roo.get(this.el);
12099     } else {
12100         // old format..
12101         this.el  = Roo.get(config);
12102         this.tpl = depreciated_tpl;
12103         Roo.apply(this, depreciated_config);
12104     }
12105     this.wrapEl  = this.el.wrap().wrap();
12106     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12107     
12108     
12109     if(typeof(this.tpl) == "string"){
12110         this.tpl = new Roo.Template(this.tpl);
12111     } else {
12112         // support xtype ctors..
12113         this.tpl = new Roo.factory(this.tpl, Roo);
12114     }
12115     
12116     
12117     this.tpl.compile();
12118     
12119     /** @private */
12120     this.addEvents({
12121         /**
12122          * @event beforeclick
12123          * Fires before a click is processed. Returns false to cancel the default action.
12124          * @param {Roo.View} this
12125          * @param {Number} index The index of the target node
12126          * @param {HTMLElement} node The target node
12127          * @param {Roo.EventObject} e The raw event object
12128          */
12129             "beforeclick" : true,
12130         /**
12131          * @event click
12132          * Fires when a template node is clicked.
12133          * @param {Roo.View} this
12134          * @param {Number} index The index of the target node
12135          * @param {HTMLElement} node The target node
12136          * @param {Roo.EventObject} e The raw event object
12137          */
12138             "click" : true,
12139         /**
12140          * @event dblclick
12141          * Fires when a template node is double clicked.
12142          * @param {Roo.View} this
12143          * @param {Number} index The index of the target node
12144          * @param {HTMLElement} node The target node
12145          * @param {Roo.EventObject} e The raw event object
12146          */
12147             "dblclick" : true,
12148         /**
12149          * @event contextmenu
12150          * Fires when a template node is right clicked.
12151          * @param {Roo.View} this
12152          * @param {Number} index The index of the target node
12153          * @param {HTMLElement} node The target node
12154          * @param {Roo.EventObject} e The raw event object
12155          */
12156             "contextmenu" : true,
12157         /**
12158          * @event selectionchange
12159          * Fires when the selected nodes change.
12160          * @param {Roo.View} this
12161          * @param {Array} selections Array of the selected nodes
12162          */
12163             "selectionchange" : true,
12164     
12165         /**
12166          * @event beforeselect
12167          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12168          * @param {Roo.View} this
12169          * @param {HTMLElement} node The node to be selected
12170          * @param {Array} selections Array of currently selected nodes
12171          */
12172             "beforeselect" : true,
12173         /**
12174          * @event preparedata
12175          * Fires on every row to render, to allow you to change the data.
12176          * @param {Roo.View} this
12177          * @param {Object} data to be rendered (change this)
12178          */
12179           "preparedata" : true
12180           
12181           
12182         });
12183
12184
12185
12186     this.el.on({
12187         "click": this.onClick,
12188         "dblclick": this.onDblClick,
12189         "contextmenu": this.onContextMenu,
12190         scope:this
12191     });
12192
12193     this.selections = [];
12194     this.nodes = [];
12195     this.cmp = new Roo.CompositeElementLite([]);
12196     if(this.store){
12197         this.store = Roo.factory(this.store, Roo.data);
12198         this.setStore(this.store, true);
12199     }
12200     
12201     if ( this.footer && this.footer.xtype) {
12202            
12203          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12204         
12205         this.footer.dataSource = this.store
12206         this.footer.container = fctr;
12207         this.footer = Roo.factory(this.footer, Roo);
12208         fctr.insertFirst(this.el);
12209         
12210         // this is a bit insane - as the paging toolbar seems to detach the el..
12211 //        dom.parentNode.parentNode.parentNode
12212          // they get detached?
12213     }
12214     
12215     
12216     Roo.View.superclass.constructor.call(this);
12217     
12218     
12219 };
12220
12221 Roo.extend(Roo.View, Roo.util.Observable, {
12222     
12223      /**
12224      * @cfg {Roo.data.Store} store Data store to load data from.
12225      */
12226     store : false,
12227     
12228     /**
12229      * @cfg {String|Roo.Element} el The container element.
12230      */
12231     el : '',
12232     
12233     /**
12234      * @cfg {String|Roo.Template} tpl The template used by this View 
12235      */
12236     tpl : false,
12237     /**
12238      * @cfg {String} dataName the named area of the template to use as the data area
12239      *                          Works with domtemplates roo-name="name"
12240      */
12241     dataName: false,
12242     /**
12243      * @cfg {String} selectedClass The css class to add to selected nodes
12244      */
12245     selectedClass : "x-view-selected",
12246      /**
12247      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12248      */
12249     emptyText : "",
12250     
12251     /**
12252      * @cfg {String} text to display on mask (default Loading)
12253      */
12254     mask : false,
12255     /**
12256      * @cfg {Boolean} multiSelect Allow multiple selection
12257      */
12258     multiSelect : false,
12259     /**
12260      * @cfg {Boolean} singleSelect Allow single selection
12261      */
12262     singleSelect:  false,
12263     
12264     /**
12265      * @cfg {Boolean} toggleSelect - selecting 
12266      */
12267     toggleSelect : false,
12268     
12269     /**
12270      * @cfg {Boolean} tickable - selecting 
12271      */
12272     tickable : false,
12273     
12274     /**
12275      * Returns the element this view is bound to.
12276      * @return {Roo.Element}
12277      */
12278     getEl : function(){
12279         return this.wrapEl;
12280     },
12281     
12282     
12283
12284     /**
12285      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12286      */
12287     refresh : function(){
12288         Roo.log('refresh');
12289         var t = this.tpl;
12290         
12291         // if we are using something like 'domtemplate', then
12292         // the what gets used is:
12293         // t.applySubtemplate(NAME, data, wrapping data..)
12294         // the outer template then get' applied with
12295         //     the store 'extra data'
12296         // and the body get's added to the
12297         //      roo-name="data" node?
12298         //      <span class='roo-tpl-{name}'></span> ?????
12299         
12300         
12301         
12302         this.clearSelections();
12303         this.el.update("");
12304         var html = [];
12305         var records = this.store.getRange();
12306         if(records.length < 1) {
12307             
12308             // is this valid??  = should it render a template??
12309             
12310             this.el.update(this.emptyText);
12311             return;
12312         }
12313         var el = this.el;
12314         if (this.dataName) {
12315             this.el.update(t.apply(this.store.meta)); //????
12316             el = this.el.child('.roo-tpl-' + this.dataName);
12317         }
12318         
12319         for(var i = 0, len = records.length; i < len; i++){
12320             var data = this.prepareData(records[i].data, i, records[i]);
12321             this.fireEvent("preparedata", this, data, i, records[i]);
12322             
12323             var d = Roo.apply({}, data);
12324             
12325             if(this.tickable){
12326                 Roo.apply(d, {'roo-id' : Roo.id()});
12327                 
12328                 var _this = this;
12329             
12330                 Roo.each(this.parent.item, function(item){
12331                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12332                         return;
12333                     }
12334                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12335                 });
12336             }
12337             
12338             html[html.length] = Roo.util.Format.trim(
12339                 this.dataName ?
12340                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12341                     t.apply(d)
12342             );
12343         }
12344         
12345         
12346         
12347         el.update(html.join(""));
12348         this.nodes = el.dom.childNodes;
12349         this.updateIndexes(0);
12350     },
12351     
12352
12353     /**
12354      * Function to override to reformat the data that is sent to
12355      * the template for each node.
12356      * DEPRICATED - use the preparedata event handler.
12357      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12358      * a JSON object for an UpdateManager bound view).
12359      */
12360     prepareData : function(data, index, record)
12361     {
12362         this.fireEvent("preparedata", this, data, index, record);
12363         return data;
12364     },
12365
12366     onUpdate : function(ds, record){
12367          Roo.log('on update');   
12368         this.clearSelections();
12369         var index = this.store.indexOf(record);
12370         var n = this.nodes[index];
12371         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12372         n.parentNode.removeChild(n);
12373         this.updateIndexes(index, index);
12374     },
12375
12376     
12377     
12378 // --------- FIXME     
12379     onAdd : function(ds, records, index)
12380     {
12381         Roo.log(['on Add', ds, records, index] );        
12382         this.clearSelections();
12383         if(this.nodes.length == 0){
12384             this.refresh();
12385             return;
12386         }
12387         var n = this.nodes[index];
12388         for(var i = 0, len = records.length; i < len; i++){
12389             var d = this.prepareData(records[i].data, i, records[i]);
12390             if(n){
12391                 this.tpl.insertBefore(n, d);
12392             }else{
12393                 
12394                 this.tpl.append(this.el, d);
12395             }
12396         }
12397         this.updateIndexes(index);
12398     },
12399
12400     onRemove : function(ds, record, index){
12401         Roo.log('onRemove');
12402         this.clearSelections();
12403         var el = this.dataName  ?
12404             this.el.child('.roo-tpl-' + this.dataName) :
12405             this.el; 
12406         
12407         el.dom.removeChild(this.nodes[index]);
12408         this.updateIndexes(index);
12409     },
12410
12411     /**
12412      * Refresh an individual node.
12413      * @param {Number} index
12414      */
12415     refreshNode : function(index){
12416         this.onUpdate(this.store, this.store.getAt(index));
12417     },
12418
12419     updateIndexes : function(startIndex, endIndex){
12420         var ns = this.nodes;
12421         startIndex = startIndex || 0;
12422         endIndex = endIndex || ns.length - 1;
12423         for(var i = startIndex; i <= endIndex; i++){
12424             ns[i].nodeIndex = i;
12425         }
12426     },
12427
12428     /**
12429      * Changes the data store this view uses and refresh the view.
12430      * @param {Store} store
12431      */
12432     setStore : function(store, initial){
12433         if(!initial && this.store){
12434             this.store.un("datachanged", this.refresh);
12435             this.store.un("add", this.onAdd);
12436             this.store.un("remove", this.onRemove);
12437             this.store.un("update", this.onUpdate);
12438             this.store.un("clear", this.refresh);
12439             this.store.un("beforeload", this.onBeforeLoad);
12440             this.store.un("load", this.onLoad);
12441             this.store.un("loadexception", this.onLoad);
12442         }
12443         if(store){
12444           
12445             store.on("datachanged", this.refresh, this);
12446             store.on("add", this.onAdd, this);
12447             store.on("remove", this.onRemove, this);
12448             store.on("update", this.onUpdate, this);
12449             store.on("clear", this.refresh, this);
12450             store.on("beforeload", this.onBeforeLoad, this);
12451             store.on("load", this.onLoad, this);
12452             store.on("loadexception", this.onLoad, this);
12453         }
12454         
12455         if(store){
12456             this.refresh();
12457         }
12458     },
12459     /**
12460      * onbeforeLoad - masks the loading area.
12461      *
12462      */
12463     onBeforeLoad : function(store,opts)
12464     {
12465          Roo.log('onBeforeLoad');   
12466         if (!opts.add) {
12467             this.el.update("");
12468         }
12469         this.el.mask(this.mask ? this.mask : "Loading" ); 
12470     },
12471     onLoad : function ()
12472     {
12473         this.el.unmask();
12474     },
12475     
12476
12477     /**
12478      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12479      * @param {HTMLElement} node
12480      * @return {HTMLElement} The template node
12481      */
12482     findItemFromChild : function(node){
12483         var el = this.dataName  ?
12484             this.el.child('.roo-tpl-' + this.dataName,true) :
12485             this.el.dom; 
12486         
12487         if(!node || node.parentNode == el){
12488                     return node;
12489             }
12490             var p = node.parentNode;
12491             while(p && p != el){
12492             if(p.parentNode == el){
12493                 return p;
12494             }
12495             p = p.parentNode;
12496         }
12497             return null;
12498     },
12499
12500     /** @ignore */
12501     onClick : function(e){
12502         var item = this.findItemFromChild(e.getTarget());
12503         if(item){
12504             var index = this.indexOf(item);
12505             if(this.onItemClick(item, index, e) !== false){
12506                 this.fireEvent("click", this, index, item, e);
12507             }
12508         }else{
12509             this.clearSelections();
12510         }
12511     },
12512
12513     /** @ignore */
12514     onContextMenu : function(e){
12515         var item = this.findItemFromChild(e.getTarget());
12516         if(item){
12517             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12518         }
12519     },
12520
12521     /** @ignore */
12522     onDblClick : function(e){
12523         var item = this.findItemFromChild(e.getTarget());
12524         if(item){
12525             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12526         }
12527     },
12528
12529     onItemClick : function(item, index, e)
12530     {
12531         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12532             return false;
12533         }
12534         if (this.toggleSelect) {
12535             var m = this.isSelected(item) ? 'unselect' : 'select';
12536             Roo.log(m);
12537             var _t = this;
12538             _t[m](item, true, false);
12539             return true;
12540         }
12541         if(this.multiSelect || this.singleSelect){
12542             if(this.multiSelect && e.shiftKey && this.lastSelection){
12543                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12544             }else{
12545                 this.select(item, this.multiSelect && e.ctrlKey);
12546                 this.lastSelection = item;
12547             }
12548             
12549             if(!this.tickable){
12550                 e.preventDefault();
12551             }
12552             
12553         }
12554         return true;
12555     },
12556
12557     /**
12558      * Get the number of selected nodes.
12559      * @return {Number}
12560      */
12561     getSelectionCount : function(){
12562         return this.selections.length;
12563     },
12564
12565     /**
12566      * Get the currently selected nodes.
12567      * @return {Array} An array of HTMLElements
12568      */
12569     getSelectedNodes : function(){
12570         return this.selections;
12571     },
12572
12573     /**
12574      * Get the indexes of the selected nodes.
12575      * @return {Array}
12576      */
12577     getSelectedIndexes : function(){
12578         var indexes = [], s = this.selections;
12579         for(var i = 0, len = s.length; i < len; i++){
12580             indexes.push(s[i].nodeIndex);
12581         }
12582         return indexes;
12583     },
12584
12585     /**
12586      * Clear all selections
12587      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12588      */
12589     clearSelections : function(suppressEvent){
12590         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12591             this.cmp.elements = this.selections;
12592             this.cmp.removeClass(this.selectedClass);
12593             this.selections = [];
12594             if(!suppressEvent){
12595                 this.fireEvent("selectionchange", this, this.selections);
12596             }
12597         }
12598     },
12599
12600     /**
12601      * Returns true if the passed node is selected
12602      * @param {HTMLElement/Number} node The node or node index
12603      * @return {Boolean}
12604      */
12605     isSelected : function(node){
12606         var s = this.selections;
12607         if(s.length < 1){
12608             return false;
12609         }
12610         node = this.getNode(node);
12611         return s.indexOf(node) !== -1;
12612     },
12613
12614     /**
12615      * Selects nodes.
12616      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
12617      * @param {Boolean} keepExisting (optional) true to keep existing selections
12618      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12619      */
12620     select : function(nodeInfo, keepExisting, suppressEvent){
12621         Roo.log('running Roo.View select!!!!!!!!!!!!!!!!!!!!!!1');
12622         if(nodeInfo instanceof Array){
12623             if(!keepExisting){
12624                 this.clearSelections(true);
12625             }
12626             for(var i = 0, len = nodeInfo.length; i < len; i++){
12627                 this.select(nodeInfo[i], true, true);
12628             }
12629             return;
12630         } 
12631         var node = this.getNode(nodeInfo);
12632         if(!node || this.isSelected(node)){
12633             return; // already selected.
12634         }
12635         if(!keepExisting){
12636             this.clearSelections(true);
12637         }
12638         
12639         Roo.log(this.parent);
12640         Roo.log(this.list);
12641 //        var el = this.view.getNode(index);
12642 //        if(el && !this.multiple && !this.tickable){
12643 //            this.list.scrollChildIntoView(el, false);
12644 //        }
12645         
12646         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12647             Roo.fly(node).addClass(this.selectedClass);
12648             this.selections.push(node);
12649             if(!suppressEvent){
12650                 this.fireEvent("selectionchange", this, this.selections);
12651             }
12652         }
12653         
12654         
12655     },
12656       /**
12657      * Unselects nodes.
12658      * @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
12659      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12660      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12661      */
12662     unselect : function(nodeInfo, keepExisting, suppressEvent)
12663     {
12664         if(nodeInfo instanceof Array){
12665             Roo.each(this.selections, function(s) {
12666                 this.unselect(s, nodeInfo);
12667             }, this);
12668             return;
12669         }
12670         var node = this.getNode(nodeInfo);
12671         if(!node || !this.isSelected(node)){
12672             Roo.log("not selected");
12673             return; // not selected.
12674         }
12675         // fireevent???
12676         var ns = [];
12677         Roo.each(this.selections, function(s) {
12678             if (s == node ) {
12679                 Roo.fly(node).removeClass(this.selectedClass);
12680
12681                 return;
12682             }
12683             ns.push(s);
12684         },this);
12685         
12686         this.selections= ns;
12687         this.fireEvent("selectionchange", this, this.selections);
12688     },
12689
12690     /**
12691      * Gets a template node.
12692      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12693      * @return {HTMLElement} The node or null if it wasn't found
12694      */
12695     getNode : function(nodeInfo){
12696         if(typeof nodeInfo == "string"){
12697             return document.getElementById(nodeInfo);
12698         }else if(typeof nodeInfo == "number"){
12699             return this.nodes[nodeInfo];
12700         }
12701         return nodeInfo;
12702     },
12703
12704     /**
12705      * Gets a range template nodes.
12706      * @param {Number} startIndex
12707      * @param {Number} endIndex
12708      * @return {Array} An array of nodes
12709      */
12710     getNodes : function(start, end){
12711         var ns = this.nodes;
12712         start = start || 0;
12713         end = typeof end == "undefined" ? ns.length - 1 : end;
12714         var nodes = [];
12715         if(start <= end){
12716             for(var i = start; i <= end; i++){
12717                 nodes.push(ns[i]);
12718             }
12719         } else{
12720             for(var i = start; i >= end; i--){
12721                 nodes.push(ns[i]);
12722             }
12723         }
12724         return nodes;
12725     },
12726
12727     /**
12728      * Finds the index of the passed node
12729      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12730      * @return {Number} The index of the node or -1
12731      */
12732     indexOf : function(node){
12733         node = this.getNode(node);
12734         if(typeof node.nodeIndex == "number"){
12735             return node.nodeIndex;
12736         }
12737         var ns = this.nodes;
12738         for(var i = 0, len = ns.length; i < len; i++){
12739             if(ns[i] == node){
12740                 return i;
12741             }
12742         }
12743         return -1;
12744     }
12745 });
12746 /*
12747  * - LGPL
12748  *
12749  * based on jquery fullcalendar
12750  * 
12751  */
12752
12753 Roo.bootstrap = Roo.bootstrap || {};
12754 /**
12755  * @class Roo.bootstrap.Calendar
12756  * @extends Roo.bootstrap.Component
12757  * Bootstrap Calendar class
12758  * @cfg {Boolean} loadMask (true|false) default false
12759  * @cfg {Object} header generate the user specific header of the calendar, default false
12760
12761  * @constructor
12762  * Create a new Container
12763  * @param {Object} config The config object
12764  */
12765
12766
12767
12768 Roo.bootstrap.Calendar = function(config){
12769     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12770      this.addEvents({
12771         /**
12772              * @event select
12773              * Fires when a date is selected
12774              * @param {DatePicker} this
12775              * @param {Date} date The selected date
12776              */
12777         'select': true,
12778         /**
12779              * @event monthchange
12780              * Fires when the displayed month changes 
12781              * @param {DatePicker} this
12782              * @param {Date} date The selected month
12783              */
12784         'monthchange': true,
12785         /**
12786              * @event evententer
12787              * Fires when mouse over an event
12788              * @param {Calendar} this
12789              * @param {event} Event
12790              */
12791         'evententer': true,
12792         /**
12793              * @event eventleave
12794              * Fires when the mouse leaves an
12795              * @param {Calendar} this
12796              * @param {event}
12797              */
12798         'eventleave': true,
12799         /**
12800              * @event eventclick
12801              * Fires when the mouse click an
12802              * @param {Calendar} this
12803              * @param {event}
12804              */
12805         'eventclick': true
12806         
12807     });
12808
12809 };
12810
12811 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12812     
12813      /**
12814      * @cfg {Number} startDay
12815      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12816      */
12817     startDay : 0,
12818     
12819     loadMask : false,
12820     
12821     header : false,
12822       
12823     getAutoCreate : function(){
12824         
12825         
12826         var fc_button = function(name, corner, style, content ) {
12827             return Roo.apply({},{
12828                 tag : 'span',
12829                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12830                          (corner.length ?
12831                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12832                             ''
12833                         ),
12834                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12835                 unselectable: 'on'
12836             });
12837         };
12838         
12839         var header = {};
12840         
12841         if(!this.header){
12842             header = {
12843                 tag : 'table',
12844                 cls : 'fc-header',
12845                 style : 'width:100%',
12846                 cn : [
12847                     {
12848                         tag: 'tr',
12849                         cn : [
12850                             {
12851                                 tag : 'td',
12852                                 cls : 'fc-header-left',
12853                                 cn : [
12854                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12855                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12856                                     { tag: 'span', cls: 'fc-header-space' },
12857                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12858
12859
12860                                 ]
12861                             },
12862
12863                             {
12864                                 tag : 'td',
12865                                 cls : 'fc-header-center',
12866                                 cn : [
12867                                     {
12868                                         tag: 'span',
12869                                         cls: 'fc-header-title',
12870                                         cn : {
12871                                             tag: 'H2',
12872                                             html : 'month / year'
12873                                         }
12874                                     }
12875
12876                                 ]
12877                             },
12878                             {
12879                                 tag : 'td',
12880                                 cls : 'fc-header-right',
12881                                 cn : [
12882                               /*      fc_button('month', 'left', '', 'month' ),
12883                                     fc_button('week', '', '', 'week' ),
12884                                     fc_button('day', 'right', '', 'day' )
12885                                 */    
12886
12887                                 ]
12888                             }
12889
12890                         ]
12891                     }
12892                 ]
12893             };
12894         }
12895         
12896         header = this.header;
12897         
12898        
12899         var cal_heads = function() {
12900             var ret = [];
12901             // fixme - handle this.
12902             
12903             for (var i =0; i < Date.dayNames.length; i++) {
12904                 var d = Date.dayNames[i];
12905                 ret.push({
12906                     tag: 'th',
12907                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12908                     html : d.substring(0,3)
12909                 });
12910                 
12911             }
12912             ret[0].cls += ' fc-first';
12913             ret[6].cls += ' fc-last';
12914             return ret;
12915         };
12916         var cal_cell = function(n) {
12917             return  {
12918                 tag: 'td',
12919                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12920                 cn : [
12921                     {
12922                         cn : [
12923                             {
12924                                 cls: 'fc-day-number',
12925                                 html: 'D'
12926                             },
12927                             {
12928                                 cls: 'fc-day-content',
12929                              
12930                                 cn : [
12931                                      {
12932                                         style: 'position: relative;' // height: 17px;
12933                                     }
12934                                 ]
12935                             }
12936                             
12937                             
12938                         ]
12939                     }
12940                 ]
12941                 
12942             }
12943         };
12944         var cal_rows = function() {
12945             
12946             var ret = []
12947             for (var r = 0; r < 6; r++) {
12948                 var row= {
12949                     tag : 'tr',
12950                     cls : 'fc-week',
12951                     cn : []
12952                 };
12953                 
12954                 for (var i =0; i < Date.dayNames.length; i++) {
12955                     var d = Date.dayNames[i];
12956                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12957
12958                 }
12959                 row.cn[0].cls+=' fc-first';
12960                 row.cn[0].cn[0].style = 'min-height:90px';
12961                 row.cn[6].cls+=' fc-last';
12962                 ret.push(row);
12963                 
12964             }
12965             ret[0].cls += ' fc-first';
12966             ret[4].cls += ' fc-prev-last';
12967             ret[5].cls += ' fc-last';
12968             return ret;
12969             
12970         };
12971         
12972         var cal_table = {
12973             tag: 'table',
12974             cls: 'fc-border-separate',
12975             style : 'width:100%',
12976             cellspacing  : 0,
12977             cn : [
12978                 { 
12979                     tag: 'thead',
12980                     cn : [
12981                         { 
12982                             tag: 'tr',
12983                             cls : 'fc-first fc-last',
12984                             cn : cal_heads()
12985                         }
12986                     ]
12987                 },
12988                 { 
12989                     tag: 'tbody',
12990                     cn : cal_rows()
12991                 }
12992                   
12993             ]
12994         };
12995          
12996          var cfg = {
12997             cls : 'fc fc-ltr',
12998             cn : [
12999                 header,
13000                 {
13001                     cls : 'fc-content',
13002                     style : "position: relative;",
13003                     cn : [
13004                         {
13005                             cls : 'fc-view fc-view-month fc-grid',
13006                             style : 'position: relative',
13007                             unselectable : 'on',
13008                             cn : [
13009                                 {
13010                                     cls : 'fc-event-container',
13011                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13012                                 },
13013                                 cal_table
13014                             ]
13015                         }
13016                     ]
13017     
13018                 }
13019            ] 
13020             
13021         };
13022         
13023          
13024         
13025         return cfg;
13026     },
13027     
13028     
13029     initEvents : function()
13030     {
13031         if(!this.store){
13032             throw "can not find store for calendar";
13033         }
13034         
13035         var mark = {
13036             tag: "div",
13037             cls:"x-dlg-mask",
13038             style: "text-align:center",
13039             cn: [
13040                 {
13041                     tag: "div",
13042                     style: "background-color:white;width:50%;margin:250 auto",
13043                     cn: [
13044                         {
13045                             tag: "img",
13046                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13047                         },
13048                         {
13049                             tag: "span",
13050                             html: "Loading"
13051                         }
13052                         
13053                     ]
13054                 }
13055             ]
13056         }
13057         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13058         
13059         var size = this.el.select('.fc-content', true).first().getSize();
13060         this.maskEl.setSize(size.width, size.height);
13061         this.maskEl.enableDisplayMode("block");
13062         if(!this.loadMask){
13063             this.maskEl.hide();
13064         }
13065         
13066         this.store = Roo.factory(this.store, Roo.data);
13067         this.store.on('load', this.onLoad, this);
13068         this.store.on('beforeload', this.onBeforeLoad, this);
13069         
13070         this.resize();
13071         
13072         this.cells = this.el.select('.fc-day',true);
13073         //Roo.log(this.cells);
13074         this.textNodes = this.el.query('.fc-day-number');
13075         this.cells.addClassOnOver('fc-state-hover');
13076         
13077         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13078         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13079         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13080         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13081         
13082         this.on('monthchange', this.onMonthChange, this);
13083         
13084         this.update(new Date().clearTime());
13085     },
13086     
13087     resize : function() {
13088         var sz  = this.el.getSize();
13089         
13090         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13091         this.el.select('.fc-day-content div',true).setHeight(34);
13092     },
13093     
13094     
13095     // private
13096     showPrevMonth : function(e){
13097         this.update(this.activeDate.add("mo", -1));
13098     },
13099     showToday : function(e){
13100         this.update(new Date().clearTime());
13101     },
13102     // private
13103     showNextMonth : function(e){
13104         this.update(this.activeDate.add("mo", 1));
13105     },
13106
13107     // private
13108     showPrevYear : function(){
13109         this.update(this.activeDate.add("y", -1));
13110     },
13111
13112     // private
13113     showNextYear : function(){
13114         this.update(this.activeDate.add("y", 1));
13115     },
13116
13117     
13118    // private
13119     update : function(date)
13120     {
13121         var vd = this.activeDate;
13122         this.activeDate = date;
13123 //        if(vd && this.el){
13124 //            var t = date.getTime();
13125 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13126 //                Roo.log('using add remove');
13127 //                
13128 //                this.fireEvent('monthchange', this, date);
13129 //                
13130 //                this.cells.removeClass("fc-state-highlight");
13131 //                this.cells.each(function(c){
13132 //                   if(c.dateValue == t){
13133 //                       c.addClass("fc-state-highlight");
13134 //                       setTimeout(function(){
13135 //                            try{c.dom.firstChild.focus();}catch(e){}
13136 //                       }, 50);
13137 //                       return false;
13138 //                   }
13139 //                   return true;
13140 //                });
13141 //                return;
13142 //            }
13143 //        }
13144         
13145         var days = date.getDaysInMonth();
13146         
13147         var firstOfMonth = date.getFirstDateOfMonth();
13148         var startingPos = firstOfMonth.getDay()-this.startDay;
13149         
13150         if(startingPos < this.startDay){
13151             startingPos += 7;
13152         }
13153         
13154         var pm = date.add(Date.MONTH, -1);
13155         var prevStart = pm.getDaysInMonth()-startingPos;
13156 //        
13157         this.cells = this.el.select('.fc-day',true);
13158         this.textNodes = this.el.query('.fc-day-number');
13159         this.cells.addClassOnOver('fc-state-hover');
13160         
13161         var cells = this.cells.elements;
13162         var textEls = this.textNodes;
13163         
13164         Roo.each(cells, function(cell){
13165             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13166         });
13167         
13168         days += startingPos;
13169
13170         // convert everything to numbers so it's fast
13171         var day = 86400000;
13172         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13173         //Roo.log(d);
13174         //Roo.log(pm);
13175         //Roo.log(prevStart);
13176         
13177         var today = new Date().clearTime().getTime();
13178         var sel = date.clearTime().getTime();
13179         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13180         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13181         var ddMatch = this.disabledDatesRE;
13182         var ddText = this.disabledDatesText;
13183         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13184         var ddaysText = this.disabledDaysText;
13185         var format = this.format;
13186         
13187         var setCellClass = function(cal, cell){
13188             cell.row = 0;
13189             cell.events = [];
13190             cell.more = [];
13191             //Roo.log('set Cell Class');
13192             cell.title = "";
13193             var t = d.getTime();
13194             
13195             //Roo.log(d);
13196             
13197             cell.dateValue = t;
13198             if(t == today){
13199                 cell.className += " fc-today";
13200                 cell.className += " fc-state-highlight";
13201                 cell.title = cal.todayText;
13202             }
13203             if(t == sel){
13204                 // disable highlight in other month..
13205                 //cell.className += " fc-state-highlight";
13206                 
13207             }
13208             // disabling
13209             if(t < min) {
13210                 cell.className = " fc-state-disabled";
13211                 cell.title = cal.minText;
13212                 return;
13213             }
13214             if(t > max) {
13215                 cell.className = " fc-state-disabled";
13216                 cell.title = cal.maxText;
13217                 return;
13218             }
13219             if(ddays){
13220                 if(ddays.indexOf(d.getDay()) != -1){
13221                     cell.title = ddaysText;
13222                     cell.className = " fc-state-disabled";
13223                 }
13224             }
13225             if(ddMatch && format){
13226                 var fvalue = d.dateFormat(format);
13227                 if(ddMatch.test(fvalue)){
13228                     cell.title = ddText.replace("%0", fvalue);
13229                     cell.className = " fc-state-disabled";
13230                 }
13231             }
13232             
13233             if (!cell.initialClassName) {
13234                 cell.initialClassName = cell.dom.className;
13235             }
13236             
13237             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13238         };
13239
13240         var i = 0;
13241         
13242         for(; i < startingPos; i++) {
13243             textEls[i].innerHTML = (++prevStart);
13244             d.setDate(d.getDate()+1);
13245             
13246             cells[i].className = "fc-past fc-other-month";
13247             setCellClass(this, cells[i]);
13248         }
13249         
13250         var intDay = 0;
13251         
13252         for(; i < days; i++){
13253             intDay = i - startingPos + 1;
13254             textEls[i].innerHTML = (intDay);
13255             d.setDate(d.getDate()+1);
13256             
13257             cells[i].className = ''; // "x-date-active";
13258             setCellClass(this, cells[i]);
13259         }
13260         var extraDays = 0;
13261         
13262         for(; i < 42; i++) {
13263             textEls[i].innerHTML = (++extraDays);
13264             d.setDate(d.getDate()+1);
13265             
13266             cells[i].className = "fc-future fc-other-month";
13267             setCellClass(this, cells[i]);
13268         }
13269         
13270         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13271         
13272         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13273         
13274         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13275         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13276         
13277         if(totalRows != 6){
13278             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13279             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13280         }
13281         
13282         this.fireEvent('monthchange', this, date);
13283         
13284         
13285         /*
13286         if(!this.internalRender){
13287             var main = this.el.dom.firstChild;
13288             var w = main.offsetWidth;
13289             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13290             Roo.fly(main).setWidth(w);
13291             this.internalRender = true;
13292             // opera does not respect the auto grow header center column
13293             // then, after it gets a width opera refuses to recalculate
13294             // without a second pass
13295             if(Roo.isOpera && !this.secondPass){
13296                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13297                 this.secondPass = true;
13298                 this.update.defer(10, this, [date]);
13299             }
13300         }
13301         */
13302         
13303     },
13304     
13305     findCell : function(dt) {
13306         dt = dt.clearTime().getTime();
13307         var ret = false;
13308         this.cells.each(function(c){
13309             //Roo.log("check " +c.dateValue + '?=' + dt);
13310             if(c.dateValue == dt){
13311                 ret = c;
13312                 return false;
13313             }
13314             return true;
13315         });
13316         
13317         return ret;
13318     },
13319     
13320     findCells : function(ev) {
13321         var s = ev.start.clone().clearTime().getTime();
13322        // Roo.log(s);
13323         var e= ev.end.clone().clearTime().getTime();
13324        // Roo.log(e);
13325         var ret = [];
13326         this.cells.each(function(c){
13327              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13328             
13329             if(c.dateValue > e){
13330                 return ;
13331             }
13332             if(c.dateValue < s){
13333                 return ;
13334             }
13335             ret.push(c);
13336         });
13337         
13338         return ret;    
13339     },
13340     
13341 //    findBestRow: function(cells)
13342 //    {
13343 //        var ret = 0;
13344 //        
13345 //        for (var i =0 ; i < cells.length;i++) {
13346 //            ret  = Math.max(cells[i].rows || 0,ret);
13347 //        }
13348 //        return ret;
13349 //        
13350 //    },
13351     
13352     
13353     addItem : function(ev)
13354     {
13355         // look for vertical location slot in
13356         var cells = this.findCells(ev);
13357         
13358 //        ev.row = this.findBestRow(cells);
13359         
13360         // work out the location.
13361         
13362         var crow = false;
13363         var rows = [];
13364         for(var i =0; i < cells.length; i++) {
13365             
13366             cells[i].row = cells[0].row;
13367             
13368             if(i == 0){
13369                 cells[i].row = cells[i].row + 1;
13370             }
13371             
13372             if (!crow) {
13373                 crow = {
13374                     start : cells[i],
13375                     end :  cells[i]
13376                 };
13377                 continue;
13378             }
13379             if (crow.start.getY() == cells[i].getY()) {
13380                 // on same row.
13381                 crow.end = cells[i];
13382                 continue;
13383             }
13384             // different row.
13385             rows.push(crow);
13386             crow = {
13387                 start: cells[i],
13388                 end : cells[i]
13389             };
13390             
13391         }
13392         
13393         rows.push(crow);
13394         ev.els = [];
13395         ev.rows = rows;
13396         ev.cells = cells;
13397         
13398         cells[0].events.push(ev);
13399         
13400         this.calevents.push(ev);
13401     },
13402     
13403     clearEvents: function() {
13404         
13405         if(!this.calevents){
13406             return;
13407         }
13408         
13409         Roo.each(this.cells.elements, function(c){
13410             c.row = 0;
13411             c.events = [];
13412             c.more = [];
13413         });
13414         
13415         Roo.each(this.calevents, function(e) {
13416             Roo.each(e.els, function(el) {
13417                 el.un('mouseenter' ,this.onEventEnter, this);
13418                 el.un('mouseleave' ,this.onEventLeave, this);
13419                 el.remove();
13420             },this);
13421         },this);
13422         
13423         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13424             e.remove();
13425         });
13426         
13427     },
13428     
13429     renderEvents: function()
13430     {   
13431         var _this = this;
13432         
13433         this.cells.each(function(c) {
13434             
13435             if(c.row < 5){
13436                 return;
13437             }
13438             
13439             var ev = c.events;
13440             
13441             var r = 4;
13442             if(c.row != c.events.length){
13443                 r = 4 - (4 - (c.row - c.events.length));
13444             }
13445             
13446             c.events = ev.slice(0, r);
13447             c.more = ev.slice(r);
13448             
13449             if(c.more.length && c.more.length == 1){
13450                 c.events.push(c.more.pop());
13451             }
13452             
13453             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13454             
13455         });
13456             
13457         this.cells.each(function(c) {
13458             
13459             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13460             
13461             
13462             for (var e = 0; e < c.events.length; e++){
13463                 var ev = c.events[e];
13464                 var rows = ev.rows;
13465                 
13466                 for(var i = 0; i < rows.length; i++) {
13467                 
13468                     // how many rows should it span..
13469
13470                     var  cfg = {
13471                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13472                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13473
13474                         unselectable : "on",
13475                         cn : [
13476                             {
13477                                 cls: 'fc-event-inner',
13478                                 cn : [
13479     //                                {
13480     //                                  tag:'span',
13481     //                                  cls: 'fc-event-time',
13482     //                                  html : cells.length > 1 ? '' : ev.time
13483     //                                },
13484                                     {
13485                                       tag:'span',
13486                                       cls: 'fc-event-title',
13487                                       html : String.format('{0}', ev.title)
13488                                     }
13489
13490
13491                                 ]
13492                             },
13493                             {
13494                                 cls: 'ui-resizable-handle ui-resizable-e',
13495                                 html : '&nbsp;&nbsp;&nbsp'
13496                             }
13497
13498                         ]
13499                     };
13500
13501                     if (i == 0) {
13502                         cfg.cls += ' fc-event-start';
13503                     }
13504                     if ((i+1) == rows.length) {
13505                         cfg.cls += ' fc-event-end';
13506                     }
13507
13508                     var ctr = _this.el.select('.fc-event-container',true).first();
13509                     var cg = ctr.createChild(cfg);
13510
13511                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13512                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13513
13514                     var r = (c.more.length) ? 1 : 0;
13515                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13516                     cg.setWidth(ebox.right - sbox.x -2);
13517
13518                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13519                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13520                     cg.on('click', _this.onEventClick, _this, ev);
13521
13522                     ev.els.push(cg);
13523                     
13524                 }
13525                 
13526             }
13527             
13528             
13529             if(c.more.length){
13530                 var  cfg = {
13531                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13532                     style : 'position: absolute',
13533                     unselectable : "on",
13534                     cn : [
13535                         {
13536                             cls: 'fc-event-inner',
13537                             cn : [
13538                                 {
13539                                   tag:'span',
13540                                   cls: 'fc-event-title',
13541                                   html : 'More'
13542                                 }
13543
13544
13545                             ]
13546                         },
13547                         {
13548                             cls: 'ui-resizable-handle ui-resizable-e',
13549                             html : '&nbsp;&nbsp;&nbsp'
13550                         }
13551
13552                     ]
13553                 };
13554
13555                 var ctr = _this.el.select('.fc-event-container',true).first();
13556                 var cg = ctr.createChild(cfg);
13557
13558                 var sbox = c.select('.fc-day-content',true).first().getBox();
13559                 var ebox = c.select('.fc-day-content',true).first().getBox();
13560                 //Roo.log(cg);
13561                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13562                 cg.setWidth(ebox.right - sbox.x -2);
13563
13564                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13565                 
13566             }
13567             
13568         });
13569         
13570         
13571         
13572     },
13573     
13574     onEventEnter: function (e, el,event,d) {
13575         this.fireEvent('evententer', this, el, event);
13576     },
13577     
13578     onEventLeave: function (e, el,event,d) {
13579         this.fireEvent('eventleave', this, el, event);
13580     },
13581     
13582     onEventClick: function (e, el,event,d) {
13583         this.fireEvent('eventclick', this, el, event);
13584     },
13585     
13586     onMonthChange: function () {
13587         this.store.load();
13588     },
13589     
13590     onMoreEventClick: function(e, el, more)
13591     {
13592         var _this = this;
13593         
13594         this.calpopover.placement = 'right';
13595         this.calpopover.setTitle('More');
13596         
13597         this.calpopover.setContent('');
13598         
13599         var ctr = this.calpopover.el.select('.popover-content', true).first();
13600         
13601         Roo.each(more, function(m){
13602             var cfg = {
13603                 cls : 'fc-event-hori fc-event-draggable',
13604                 html : m.title
13605             }
13606             var cg = ctr.createChild(cfg);
13607             
13608             cg.on('click', _this.onEventClick, _this, m);
13609         });
13610         
13611         this.calpopover.show(el);
13612         
13613         
13614     },
13615     
13616     onLoad: function () 
13617     {   
13618         this.calevents = [];
13619         var cal = this;
13620         
13621         if(this.store.getCount() > 0){
13622             this.store.data.each(function(d){
13623                cal.addItem({
13624                     id : d.data.id,
13625                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13626                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13627                     time : d.data.start_time,
13628                     title : d.data.title,
13629                     description : d.data.description,
13630                     venue : d.data.venue
13631                 });
13632             });
13633         }
13634         
13635         this.renderEvents();
13636         
13637         if(this.calevents.length && this.loadMask){
13638             this.maskEl.hide();
13639         }
13640     },
13641     
13642     onBeforeLoad: function()
13643     {
13644         this.clearEvents();
13645         if(this.loadMask){
13646             this.maskEl.show();
13647         }
13648     }
13649 });
13650
13651  
13652  /*
13653  * - LGPL
13654  *
13655  * element
13656  * 
13657  */
13658
13659 /**
13660  * @class Roo.bootstrap.Popover
13661  * @extends Roo.bootstrap.Component
13662  * Bootstrap Popover class
13663  * @cfg {String} html contents of the popover   (or false to use children..)
13664  * @cfg {String} title of popover (or false to hide)
13665  * @cfg {String} placement how it is placed
13666  * @cfg {String} trigger click || hover (or false to trigger manually)
13667  * @cfg {String} over what (parent or false to trigger manually.)
13668  * @cfg {Number} delay - delay before showing
13669  
13670  * @constructor
13671  * Create a new Popover
13672  * @param {Object} config The config object
13673  */
13674
13675 Roo.bootstrap.Popover = function(config){
13676     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13677 };
13678
13679 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13680     
13681     title: 'Fill in a title',
13682     html: false,
13683     
13684     placement : 'right',
13685     trigger : 'hover', // hover
13686     
13687     delay : 0,
13688     
13689     over: 'parent',
13690     
13691     can_build_overlaid : false,
13692     
13693     getChildContainer : function()
13694     {
13695         return this.el.select('.popover-content',true).first();
13696     },
13697     
13698     getAutoCreate : function(){
13699          Roo.log('make popover?');
13700         var cfg = {
13701            cls : 'popover roo-dynamic',
13702            style: 'display:block',
13703            cn : [
13704                 {
13705                     cls : 'arrow'
13706                 },
13707                 {
13708                     cls : 'popover-inner',
13709                     cn : [
13710                         {
13711                             tag: 'h3',
13712                             cls: 'popover-title',
13713                             html : this.title
13714                         },
13715                         {
13716                             cls : 'popover-content',
13717                             html : this.html
13718                         }
13719                     ]
13720                     
13721                 }
13722            ]
13723         };
13724         
13725         return cfg;
13726     },
13727     setTitle: function(str)
13728     {
13729         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13730     },
13731     setContent: function(str)
13732     {
13733         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13734     },
13735     // as it get's added to the bottom of the page.
13736     onRender : function(ct, position)
13737     {
13738         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13739         if(!this.el){
13740             var cfg = Roo.apply({},  this.getAutoCreate());
13741             cfg.id = Roo.id();
13742             
13743             if (this.cls) {
13744                 cfg.cls += ' ' + this.cls;
13745             }
13746             if (this.style) {
13747                 cfg.style = this.style;
13748             }
13749             Roo.log("adding to ")
13750             this.el = Roo.get(document.body).createChild(cfg, position);
13751             Roo.log(this.el);
13752         }
13753         this.initEvents();
13754     },
13755     
13756     initEvents : function()
13757     {
13758         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13759         this.el.enableDisplayMode('block');
13760         this.el.hide();
13761         if (this.over === false) {
13762             return; 
13763         }
13764         if (this.triggers === false) {
13765             return;
13766         }
13767         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13768         var triggers = this.trigger ? this.trigger.split(' ') : [];
13769         Roo.each(triggers, function(trigger) {
13770         
13771             if (trigger == 'click') {
13772                 on_el.on('click', this.toggle, this);
13773             } else if (trigger != 'manual') {
13774                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13775                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13776       
13777                 on_el.on(eventIn  ,this.enter, this);
13778                 on_el.on(eventOut, this.leave, this);
13779             }
13780         }, this);
13781         
13782     },
13783     
13784     
13785     // private
13786     timeout : null,
13787     hoverState : null,
13788     
13789     toggle : function () {
13790         this.hoverState == 'in' ? this.leave() : this.enter();
13791     },
13792     
13793     enter : function () {
13794        
13795     
13796         clearTimeout(this.timeout);
13797     
13798         this.hoverState = 'in'
13799     
13800         if (!this.delay || !this.delay.show) {
13801             this.show();
13802             return 
13803         }
13804         var _t = this;
13805         this.timeout = setTimeout(function () {
13806             if (_t.hoverState == 'in') {
13807                 _t.show();
13808             }
13809         }, this.delay.show)
13810     },
13811     leave : function() {
13812         clearTimeout(this.timeout);
13813     
13814         this.hoverState = 'out'
13815     
13816         if (!this.delay || !this.delay.hide) {
13817             this.hide();
13818             return 
13819         }
13820         var _t = this;
13821         this.timeout = setTimeout(function () {
13822             if (_t.hoverState == 'out') {
13823                 _t.hide();
13824             }
13825         }, this.delay.hide)
13826     },
13827     
13828     show : function (on_el)
13829     {
13830         if (!on_el) {
13831             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13832         }
13833         // set content.
13834         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13835         if (this.html !== false) {
13836             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13837         }
13838         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13839         if (!this.title.length) {
13840             this.el.select('.popover-title',true).hide();
13841         }
13842         
13843         var placement = typeof this.placement == 'function' ?
13844             this.placement.call(this, this.el, on_el) :
13845             this.placement;
13846             
13847         var autoToken = /\s?auto?\s?/i;
13848         var autoPlace = autoToken.test(placement);
13849         if (autoPlace) {
13850             placement = placement.replace(autoToken, '') || 'top';
13851         }
13852         
13853         //this.el.detach()
13854         //this.el.setXY([0,0]);
13855         this.el.show();
13856         this.el.dom.style.display='block';
13857         this.el.addClass(placement);
13858         
13859         //this.el.appendTo(on_el);
13860         
13861         var p = this.getPosition();
13862         var box = this.el.getBox();
13863         
13864         if (autoPlace) {
13865             // fixme..
13866         }
13867         var align = Roo.bootstrap.Popover.alignment[placement]
13868         this.el.alignTo(on_el, align[0],align[1]);
13869         //var arrow = this.el.select('.arrow',true).first();
13870         //arrow.set(align[2], 
13871         
13872         this.el.addClass('in');
13873         this.hoverState = null;
13874         
13875         if (this.el.hasClass('fade')) {
13876             // fade it?
13877         }
13878         
13879     },
13880     hide : function()
13881     {
13882         this.el.setXY([0,0]);
13883         this.el.removeClass('in');
13884         this.el.hide();
13885         
13886     }
13887     
13888 });
13889
13890 Roo.bootstrap.Popover.alignment = {
13891     'left' : ['r-l', [-10,0], 'right'],
13892     'right' : ['l-r', [10,0], 'left'],
13893     'bottom' : ['t-b', [0,10], 'top'],
13894     'top' : [ 'b-t', [0,-10], 'bottom']
13895 };
13896
13897  /*
13898  * - LGPL
13899  *
13900  * Progress
13901  * 
13902  */
13903
13904 /**
13905  * @class Roo.bootstrap.Progress
13906  * @extends Roo.bootstrap.Component
13907  * Bootstrap Progress class
13908  * @cfg {Boolean} striped striped of the progress bar
13909  * @cfg {Boolean} active animated of the progress bar
13910  * 
13911  * 
13912  * @constructor
13913  * Create a new Progress
13914  * @param {Object} config The config object
13915  */
13916
13917 Roo.bootstrap.Progress = function(config){
13918     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13919 };
13920
13921 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13922     
13923     striped : false,
13924     active: false,
13925     
13926     getAutoCreate : function(){
13927         var cfg = {
13928             tag: 'div',
13929             cls: 'progress'
13930         };
13931         
13932         
13933         if(this.striped){
13934             cfg.cls += ' progress-striped';
13935         }
13936       
13937         if(this.active){
13938             cfg.cls += ' active';
13939         }
13940         
13941         
13942         return cfg;
13943     }
13944    
13945 });
13946
13947  
13948
13949  /*
13950  * - LGPL
13951  *
13952  * ProgressBar
13953  * 
13954  */
13955
13956 /**
13957  * @class Roo.bootstrap.ProgressBar
13958  * @extends Roo.bootstrap.Component
13959  * Bootstrap ProgressBar class
13960  * @cfg {Number} aria_valuenow aria-value now
13961  * @cfg {Number} aria_valuemin aria-value min
13962  * @cfg {Number} aria_valuemax aria-value max
13963  * @cfg {String} label label for the progress bar
13964  * @cfg {String} panel (success | info | warning | danger )
13965  * @cfg {String} role role of the progress bar
13966  * @cfg {String} sr_only text
13967  * 
13968  * 
13969  * @constructor
13970  * Create a new ProgressBar
13971  * @param {Object} config The config object
13972  */
13973
13974 Roo.bootstrap.ProgressBar = function(config){
13975     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13976 };
13977
13978 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13979     
13980     aria_valuenow : 0,
13981     aria_valuemin : 0,
13982     aria_valuemax : 100,
13983     label : false,
13984     panel : false,
13985     role : false,
13986     sr_only: false,
13987     
13988     getAutoCreate : function()
13989     {
13990         
13991         var cfg = {
13992             tag: 'div',
13993             cls: 'progress-bar',
13994             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13995         };
13996         
13997         if(this.sr_only){
13998             cfg.cn = {
13999                 tag: 'span',
14000                 cls: 'sr-only',
14001                 html: this.sr_only
14002             }
14003         }
14004         
14005         if(this.role){
14006             cfg.role = this.role;
14007         }
14008         
14009         if(this.aria_valuenow){
14010             cfg['aria-valuenow'] = this.aria_valuenow;
14011         }
14012         
14013         if(this.aria_valuemin){
14014             cfg['aria-valuemin'] = this.aria_valuemin;
14015         }
14016         
14017         if(this.aria_valuemax){
14018             cfg['aria-valuemax'] = this.aria_valuemax;
14019         }
14020         
14021         if(this.label && !this.sr_only){
14022             cfg.html = this.label;
14023         }
14024         
14025         if(this.panel){
14026             cfg.cls += ' progress-bar-' + this.panel;
14027         }
14028         
14029         return cfg;
14030     },
14031     
14032     update : function(aria_valuenow)
14033     {
14034         this.aria_valuenow = aria_valuenow;
14035         
14036         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14037     }
14038    
14039 });
14040
14041  
14042
14043  /*
14044  * - LGPL
14045  *
14046  * column
14047  * 
14048  */
14049
14050 /**
14051  * @class Roo.bootstrap.TabGroup
14052  * @extends Roo.bootstrap.Column
14053  * Bootstrap Column class
14054  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14055  * @cfg {Boolean} carousel true to make the group behave like a carousel
14056  * 
14057  * @constructor
14058  * Create a new TabGroup
14059  * @param {Object} config The config object
14060  */
14061
14062 Roo.bootstrap.TabGroup = function(config){
14063     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14064     if (!this.navId) {
14065         this.navId = Roo.id();
14066     }
14067     this.tabs = [];
14068     Roo.bootstrap.TabGroup.register(this);
14069     
14070 };
14071
14072 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14073     
14074     carousel : false,
14075     transition : false,
14076      
14077     getAutoCreate : function()
14078     {
14079         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14080         
14081         cfg.cls += ' tab-content';
14082         
14083         if (this.carousel) {
14084             cfg.cls += ' carousel slide';
14085             cfg.cn = [{
14086                cls : 'carousel-inner'
14087             }]
14088         }
14089         
14090         
14091         return cfg;
14092     },
14093     getChildContainer : function()
14094     {
14095         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14096     },
14097     
14098     /**
14099     * register a Navigation item
14100     * @param {Roo.bootstrap.NavItem} the navitem to add
14101     */
14102     register : function(item)
14103     {
14104         this.tabs.push( item);
14105         item.navId = this.navId; // not really needed..
14106     
14107     },
14108     
14109     getActivePanel : function()
14110     {
14111         var r = false;
14112         Roo.each(this.tabs, function(t) {
14113             if (t.active) {
14114                 r = t;
14115                 return false;
14116             }
14117             return null;
14118         });
14119         return r;
14120         
14121     },
14122     getPanelByName : function(n)
14123     {
14124         var r = false;
14125         Roo.each(this.tabs, function(t) {
14126             if (t.tabId == n) {
14127                 r = t;
14128                 return false;
14129             }
14130             return null;
14131         });
14132         return r;
14133     },
14134     indexOfPanel : function(p)
14135     {
14136         var r = false;
14137         Roo.each(this.tabs, function(t,i) {
14138             if (t.tabId == p.tabId) {
14139                 r = i;
14140                 return false;
14141             }
14142             return null;
14143         });
14144         return r;
14145     },
14146     /**
14147      * show a specific panel
14148      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14149      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14150      */
14151     showPanel : function (pan)
14152     {
14153         
14154         if (typeof(pan) == 'number') {
14155             pan = this.tabs[pan];
14156         }
14157         if (typeof(pan) == 'string') {
14158             pan = this.getPanelByName(pan);
14159         }
14160         if (pan.tabId == this.getActivePanel().tabId) {
14161             return true;
14162         }
14163         var cur = this.getActivePanel();
14164         
14165         if (false === cur.fireEvent('beforedeactivate')) {
14166             return false;
14167         }
14168         
14169         if (this.carousel) {
14170             this.transition = true;
14171             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14172             var lr = dir == 'next' ? 'left' : 'right';
14173             pan.el.addClass(dir); // or prev
14174             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14175             cur.el.addClass(lr); // or right
14176             pan.el.addClass(lr);
14177             
14178             var _this = this;
14179             cur.el.on('transitionend', function() {
14180                 Roo.log("trans end?");
14181                 
14182                 pan.el.removeClass([lr,dir]);
14183                 pan.setActive(true);
14184                 
14185                 cur.el.removeClass([lr]);
14186                 cur.setActive(false);
14187                 
14188                 _this.transition = false;
14189                 
14190             }, this, { single:  true } );
14191             return true;
14192         }
14193         
14194         cur.setActive(false);
14195         pan.setActive(true);
14196         return true;
14197         
14198     },
14199     showPanelNext : function()
14200     {
14201         var i = this.indexOfPanel(this.getActivePanel());
14202         if (i > this.tabs.length) {
14203             return;
14204         }
14205         this.showPanel(this.tabs[i+1]);
14206     },
14207     showPanelPrev : function()
14208     {
14209         var i = this.indexOfPanel(this.getActivePanel());
14210         if (i  < 1) {
14211             return;
14212         }
14213         this.showPanel(this.tabs[i-1]);
14214     }
14215     
14216     
14217   
14218 });
14219
14220  
14221
14222  
14223  
14224 Roo.apply(Roo.bootstrap.TabGroup, {
14225     
14226     groups: {},
14227      /**
14228     * register a Navigation Group
14229     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14230     */
14231     register : function(navgrp)
14232     {
14233         this.groups[navgrp.navId] = navgrp;
14234         
14235     },
14236     /**
14237     * fetch a Navigation Group based on the navigation ID
14238     * if one does not exist , it will get created.
14239     * @param {string} the navgroup to add
14240     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14241     */
14242     get: function(navId) {
14243         if (typeof(this.groups[navId]) == 'undefined') {
14244             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14245         }
14246         return this.groups[navId] ;
14247     }
14248     
14249     
14250     
14251 });
14252
14253  /*
14254  * - LGPL
14255  *
14256  * TabPanel
14257  * 
14258  */
14259
14260 /**
14261  * @class Roo.bootstrap.TabPanel
14262  * @extends Roo.bootstrap.Component
14263  * Bootstrap TabPanel class
14264  * @cfg {Boolean} active panel active
14265  * @cfg {String} html panel content
14266  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14267  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14268  * 
14269  * 
14270  * @constructor
14271  * Create a new TabPanel
14272  * @param {Object} config The config object
14273  */
14274
14275 Roo.bootstrap.TabPanel = function(config){
14276     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14277     this.addEvents({
14278         /**
14279              * @event changed
14280              * Fires when the active status changes
14281              * @param {Roo.bootstrap.TabPanel} this
14282              * @param {Boolean} state the new state
14283             
14284          */
14285         'changed': true,
14286         /**
14287              * @event beforedeactivate
14288              * Fires before a tab is de-activated - can be used to do validation on a form.
14289              * @param {Roo.bootstrap.TabPanel} this
14290              * @return {Boolean} false if there is an error
14291             
14292          */
14293         'beforedeactivate': true
14294      });
14295     
14296     this.tabId = this.tabId || Roo.id();
14297   
14298 };
14299
14300 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14301     
14302     active: false,
14303     html: false,
14304     tabId: false,
14305     navId : false,
14306     
14307     getAutoCreate : function(){
14308         var cfg = {
14309             tag: 'div',
14310             // item is needed for carousel - not sure if it has any effect otherwise
14311             cls: 'tab-pane item',
14312             html: this.html || ''
14313         };
14314         
14315         if(this.active){
14316             cfg.cls += ' active';
14317         }
14318         
14319         if(this.tabId){
14320             cfg.tabId = this.tabId;
14321         }
14322         
14323         
14324         return cfg;
14325     },
14326     
14327     initEvents:  function()
14328     {
14329         Roo.log('-------- init events on tab panel ---------');
14330         
14331         var p = this.parent();
14332         this.navId = this.navId || p.navId;
14333         
14334         if (typeof(this.navId) != 'undefined') {
14335             // not really needed.. but just in case.. parent should be a NavGroup.
14336             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14337             Roo.log(['register', tg, this]);
14338             tg.register(this);
14339         }
14340     },
14341     
14342     
14343     onRender : function(ct, position)
14344     {
14345        // Roo.log("Call onRender: " + this.xtype);
14346         
14347         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14348         
14349         
14350         
14351         
14352         
14353     },
14354     
14355     setActive: function(state)
14356     {
14357         Roo.log("panel - set active " + this.tabId + "=" + state);
14358         
14359         this.active = state;
14360         if (!state) {
14361             this.el.removeClass('active');
14362             
14363         } else  if (!this.el.hasClass('active')) {
14364             this.el.addClass('active');
14365         }
14366         this.fireEvent('changed', this, state);
14367     }
14368     
14369     
14370 });
14371  
14372
14373  
14374
14375  /*
14376  * - LGPL
14377  *
14378  * DateField
14379  * 
14380  */
14381
14382 /**
14383  * @class Roo.bootstrap.DateField
14384  * @extends Roo.bootstrap.Input
14385  * Bootstrap DateField class
14386  * @cfg {Number} weekStart default 0
14387  * @cfg {Number} weekStart default 0
14388  * @cfg {Number} viewMode default empty, (months|years)
14389  * @cfg {Number} minViewMode default empty, (months|years)
14390  * @cfg {Number} startDate default -Infinity
14391  * @cfg {Number} endDate default Infinity
14392  * @cfg {Boolean} todayHighlight default false
14393  * @cfg {Boolean} todayBtn default false
14394  * @cfg {Boolean} calendarWeeks default false
14395  * @cfg {Object} daysOfWeekDisabled default empty
14396  * 
14397  * @cfg {Boolean} keyboardNavigation default true
14398  * @cfg {String} language default en
14399  * 
14400  * @constructor
14401  * Create a new DateField
14402  * @param {Object} config The config object
14403  */
14404
14405 Roo.bootstrap.DateField = function(config){
14406     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14407      this.addEvents({
14408             /**
14409              * @event show
14410              * Fires when this field show.
14411              * @param {Roo.bootstrap.DateField} this
14412              * @param {Mixed} date The date value
14413              */
14414             show : true,
14415             /**
14416              * @event show
14417              * Fires when this field hide.
14418              * @param {Roo.bootstrap.DateField} this
14419              * @param {Mixed} date The date value
14420              */
14421             hide : true,
14422             /**
14423              * @event select
14424              * Fires when select a date.
14425              * @param {Roo.bootstrap.DateField} this
14426              * @param {Mixed} date The date value
14427              */
14428             select : true
14429         });
14430 };
14431
14432 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14433     
14434     /**
14435      * @cfg {String} format
14436      * The default date format string which can be overriden for localization support.  The format must be
14437      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14438      */
14439     format : "m/d/y",
14440     /**
14441      * @cfg {String} altFormats
14442      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14443      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14444      */
14445     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14446     
14447     weekStart : 0,
14448     
14449     viewMode : '',
14450     
14451     minViewMode : '',
14452     
14453     todayHighlight : false,
14454     
14455     todayBtn: false,
14456     
14457     language: 'en',
14458     
14459     keyboardNavigation: true,
14460     
14461     calendarWeeks: false,
14462     
14463     startDate: -Infinity,
14464     
14465     endDate: Infinity,
14466     
14467     daysOfWeekDisabled: [],
14468     
14469     _events: [],
14470     
14471     UTCDate: function()
14472     {
14473         return new Date(Date.UTC.apply(Date, arguments));
14474     },
14475     
14476     UTCToday: function()
14477     {
14478         var today = new Date();
14479         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14480     },
14481     
14482     getDate: function() {
14483             var d = this.getUTCDate();
14484             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14485     },
14486     
14487     getUTCDate: function() {
14488             return this.date;
14489     },
14490     
14491     setDate: function(d) {
14492             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14493     },
14494     
14495     setUTCDate: function(d) {
14496             this.date = d;
14497             this.setValue(this.formatDate(this.date));
14498     },
14499         
14500     onRender: function(ct, position)
14501     {
14502         
14503         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14504         
14505         this.language = this.language || 'en';
14506         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14507         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14508         
14509         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14510         this.format = this.format || 'm/d/y';
14511         this.isInline = false;
14512         this.isInput = true;
14513         this.component = this.el.select('.add-on', true).first() || false;
14514         this.component = (this.component && this.component.length === 0) ? false : this.component;
14515         this.hasInput = this.component && this.inputEL().length;
14516         
14517         if (typeof(this.minViewMode === 'string')) {
14518             switch (this.minViewMode) {
14519                 case 'months':
14520                     this.minViewMode = 1;
14521                     break;
14522                 case 'years':
14523                     this.minViewMode = 2;
14524                     break;
14525                 default:
14526                     this.minViewMode = 0;
14527                     break;
14528             }
14529         }
14530         
14531         if (typeof(this.viewMode === 'string')) {
14532             switch (this.viewMode) {
14533                 case 'months':
14534                     this.viewMode = 1;
14535                     break;
14536                 case 'years':
14537                     this.viewMode = 2;
14538                     break;
14539                 default:
14540                     this.viewMode = 0;
14541                     break;
14542             }
14543         }
14544                 
14545         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14546         
14547 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14548         
14549         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14550         
14551         this.picker().on('mousedown', this.onMousedown, this);
14552         this.picker().on('click', this.onClick, this);
14553         
14554         this.picker().addClass('datepicker-dropdown');
14555         
14556         this.startViewMode = this.viewMode;
14557         
14558         
14559         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14560             if(!this.calendarWeeks){
14561                 v.remove();
14562                 return;
14563             };
14564             
14565             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14566             v.attr('colspan', function(i, val){
14567                 return parseInt(val) + 1;
14568             });
14569         })
14570                         
14571         
14572         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14573         
14574         this.setStartDate(this.startDate);
14575         this.setEndDate(this.endDate);
14576         
14577         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14578         
14579         this.fillDow();
14580         this.fillMonths();
14581         this.update();
14582         this.showMode();
14583         
14584         if(this.isInline) {
14585             this.show();
14586         }
14587     },
14588     
14589     picker : function()
14590     {
14591         return this.pickerEl;
14592 //        return this.el.select('.datepicker', true).first();
14593     },
14594     
14595     fillDow: function()
14596     {
14597         var dowCnt = this.weekStart;
14598         
14599         var dow = {
14600             tag: 'tr',
14601             cn: [
14602                 
14603             ]
14604         };
14605         
14606         if(this.calendarWeeks){
14607             dow.cn.push({
14608                 tag: 'th',
14609                 cls: 'cw',
14610                 html: '&nbsp;'
14611             })
14612         }
14613         
14614         while (dowCnt < this.weekStart + 7) {
14615             dow.cn.push({
14616                 tag: 'th',
14617                 cls: 'dow',
14618                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14619             });
14620         }
14621         
14622         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14623     },
14624     
14625     fillMonths: function()
14626     {    
14627         var i = 0
14628         var months = this.picker().select('>.datepicker-months td', true).first();
14629         
14630         months.dom.innerHTML = '';
14631         
14632         while (i < 12) {
14633             var month = {
14634                 tag: 'span',
14635                 cls: 'month',
14636                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14637             }
14638             
14639             months.createChild(month);
14640         }
14641         
14642     },
14643     
14644     update: function()
14645     {
14646         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;
14647         
14648         if (this.date < this.startDate) {
14649             this.viewDate = new Date(this.startDate);
14650         } else if (this.date > this.endDate) {
14651             this.viewDate = new Date(this.endDate);
14652         } else {
14653             this.viewDate = new Date(this.date);
14654         }
14655         
14656         this.fill();
14657     },
14658     
14659     fill: function() 
14660     {
14661         var d = new Date(this.viewDate),
14662                 year = d.getUTCFullYear(),
14663                 month = d.getUTCMonth(),
14664                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14665                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14666                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14667                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14668                 currentDate = this.date && this.date.valueOf(),
14669                 today = this.UTCToday();
14670         
14671         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14672         
14673 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14674         
14675 //        this.picker.select('>tfoot th.today').
14676 //                                              .text(dates[this.language].today)
14677 //                                              .toggle(this.todayBtn !== false);
14678     
14679         this.updateNavArrows();
14680         this.fillMonths();
14681                                                 
14682         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14683         
14684         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14685          
14686         prevMonth.setUTCDate(day);
14687         
14688         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14689         
14690         var nextMonth = new Date(prevMonth);
14691         
14692         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14693         
14694         nextMonth = nextMonth.valueOf();
14695         
14696         var fillMonths = false;
14697         
14698         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14699         
14700         while(prevMonth.valueOf() < nextMonth) {
14701             var clsName = '';
14702             
14703             if (prevMonth.getUTCDay() === this.weekStart) {
14704                 if(fillMonths){
14705                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14706                 }
14707                     
14708                 fillMonths = {
14709                     tag: 'tr',
14710                     cn: []
14711                 };
14712                 
14713                 if(this.calendarWeeks){
14714                     // ISO 8601: First week contains first thursday.
14715                     // ISO also states week starts on Monday, but we can be more abstract here.
14716                     var
14717                     // Start of current week: based on weekstart/current date
14718                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14719                     // Thursday of this week
14720                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14721                     // First Thursday of year, year from thursday
14722                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14723                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14724                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14725                     
14726                     fillMonths.cn.push({
14727                         tag: 'td',
14728                         cls: 'cw',
14729                         html: calWeek
14730                     });
14731                 }
14732             }
14733             
14734             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14735                 clsName += ' old';
14736             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14737                 clsName += ' new';
14738             }
14739             if (this.todayHighlight &&
14740                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14741                 prevMonth.getUTCMonth() == today.getMonth() &&
14742                 prevMonth.getUTCDate() == today.getDate()) {
14743                 clsName += ' today';
14744             }
14745             
14746             if (currentDate && prevMonth.valueOf() === currentDate) {
14747                 clsName += ' active';
14748             }
14749             
14750             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14751                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14752                     clsName += ' disabled';
14753             }
14754             
14755             fillMonths.cn.push({
14756                 tag: 'td',
14757                 cls: 'day ' + clsName,
14758                 html: prevMonth.getDate()
14759             })
14760             
14761             prevMonth.setDate(prevMonth.getDate()+1);
14762         }
14763           
14764         var currentYear = this.date && this.date.getUTCFullYear();
14765         var currentMonth = this.date && this.date.getUTCMonth();
14766         
14767         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14768         
14769         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14770             v.removeClass('active');
14771             
14772             if(currentYear === year && k === currentMonth){
14773                 v.addClass('active');
14774             }
14775             
14776             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14777                 v.addClass('disabled');
14778             }
14779             
14780         });
14781         
14782         
14783         year = parseInt(year/10, 10) * 10;
14784         
14785         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14786         
14787         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14788         
14789         year -= 1;
14790         for (var i = -1; i < 11; i++) {
14791             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14792                 tag: 'span',
14793                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14794                 html: year
14795             })
14796             
14797             year += 1;
14798         }
14799     },
14800     
14801     showMode: function(dir) 
14802     {
14803         if (dir) {
14804             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14805         }
14806         Roo.each(this.picker().select('>div',true).elements, function(v){
14807             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14808             v.hide();
14809         });
14810         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14811     },
14812     
14813     place: function()
14814     {
14815         if(this.isInline) return;
14816         
14817         this.picker().removeClass(['bottom', 'top']);
14818         
14819         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14820             /*
14821              * place to the top of element!
14822              *
14823              */
14824             
14825             this.picker().addClass('top');
14826             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14827             
14828             return;
14829         }
14830         
14831         this.picker().addClass('bottom');
14832         
14833         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14834     },
14835     
14836     parseDate : function(value)
14837     {
14838         if(!value || value instanceof Date){
14839             return value;
14840         }
14841         var v = Date.parseDate(value, this.format);
14842         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14843             v = Date.parseDate(value, 'Y-m-d');
14844         }
14845         if(!v && this.altFormats){
14846             if(!this.altFormatsArray){
14847                 this.altFormatsArray = this.altFormats.split("|");
14848             }
14849             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14850                 v = Date.parseDate(value, this.altFormatsArray[i]);
14851             }
14852         }
14853         return v;
14854     },
14855     
14856     formatDate : function(date, fmt)
14857     {
14858         return (!date || !(date instanceof Date)) ?
14859         date : date.dateFormat(fmt || this.format);
14860     },
14861     
14862     onFocus : function()
14863     {
14864         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14865         this.show();
14866     },
14867     
14868     onBlur : function()
14869     {
14870         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14871         
14872         var d = this.inputEl().getValue();
14873         
14874         this.setValue(d);
14875                 
14876         this.hide();
14877     },
14878     
14879     show : function()
14880     {
14881         this.picker().show();
14882         this.update();
14883         this.place();
14884         
14885         this.fireEvent('show', this, this.date);
14886     },
14887     
14888     hide : function()
14889     {
14890         if(this.isInline) return;
14891         this.picker().hide();
14892         this.viewMode = this.startViewMode;
14893         this.showMode();
14894         
14895         this.fireEvent('hide', this, this.date);
14896         
14897     },
14898     
14899     onMousedown: function(e)
14900     {
14901         e.stopPropagation();
14902         e.preventDefault();
14903     },
14904     
14905     keyup: function(e)
14906     {
14907         Roo.bootstrap.DateField.superclass.keyup.call(this);
14908         this.update();
14909     },
14910
14911     setValue: function(v)
14912     {
14913         
14914         // v can be a string or a date..
14915         
14916         
14917         var d = new Date(this.parseDate(v) ).clearTime();
14918         
14919         Roo.log(d);
14920         Roo.log(d);
14921         if(isNaN(d.getTime())){
14922             this.date = this.viewDate = '';
14923             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14924             return;
14925         }
14926         
14927         v = this.formatDate(d);
14928         
14929         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14930         
14931         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14932      
14933         this.update();
14934
14935         this.fireEvent('select', this, this.date);
14936         
14937     },
14938     
14939     getValue: function()
14940     {
14941         return this.formatDate(this.date);
14942     },
14943     
14944     fireKey: function(e)
14945     {
14946         if (!this.picker().isVisible()){
14947             if (e.keyCode == 27) // allow escape to hide and re-show picker
14948                 this.show();
14949             return;
14950         }
14951         
14952         var dateChanged = false,
14953         dir, day, month,
14954         newDate, newViewDate;
14955         
14956         switch(e.keyCode){
14957             case 27: // escape
14958                 this.hide();
14959                 e.preventDefault();
14960                 break;
14961             case 37: // left
14962             case 39: // right
14963                 if (!this.keyboardNavigation) break;
14964                 dir = e.keyCode == 37 ? -1 : 1;
14965                 
14966                 if (e.ctrlKey){
14967                     newDate = this.moveYear(this.date, dir);
14968                     newViewDate = this.moveYear(this.viewDate, dir);
14969                 } else if (e.shiftKey){
14970                     newDate = this.moveMonth(this.date, dir);
14971                     newViewDate = this.moveMonth(this.viewDate, dir);
14972                 } else {
14973                     newDate = new Date(this.date);
14974                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14975                     newViewDate = new Date(this.viewDate);
14976                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14977                 }
14978                 if (this.dateWithinRange(newDate)){
14979                     this.date = newDate;
14980                     this.viewDate = newViewDate;
14981                     this.setValue(this.formatDate(this.date));
14982 //                    this.update();
14983                     e.preventDefault();
14984                     dateChanged = true;
14985                 }
14986                 break;
14987             case 38: // up
14988             case 40: // down
14989                 if (!this.keyboardNavigation) break;
14990                 dir = e.keyCode == 38 ? -1 : 1;
14991                 if (e.ctrlKey){
14992                     newDate = this.moveYear(this.date, dir);
14993                     newViewDate = this.moveYear(this.viewDate, dir);
14994                 } else if (e.shiftKey){
14995                     newDate = this.moveMonth(this.date, dir);
14996                     newViewDate = this.moveMonth(this.viewDate, dir);
14997                 } else {
14998                     newDate = new Date(this.date);
14999                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15000                     newViewDate = new Date(this.viewDate);
15001                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15002                 }
15003                 if (this.dateWithinRange(newDate)){
15004                     this.date = newDate;
15005                     this.viewDate = newViewDate;
15006                     this.setValue(this.formatDate(this.date));
15007 //                    this.update();
15008                     e.preventDefault();
15009                     dateChanged = true;
15010                 }
15011                 break;
15012             case 13: // enter
15013                 this.setValue(this.formatDate(this.date));
15014                 this.hide();
15015                 e.preventDefault();
15016                 break;
15017             case 9: // tab
15018                 this.setValue(this.formatDate(this.date));
15019                 this.hide();
15020                 break;
15021             case 16: // shift
15022             case 17: // ctrl
15023             case 18: // alt
15024                 break;
15025             default :
15026                 this.hide();
15027                 
15028         }
15029     },
15030     
15031     
15032     onClick: function(e) 
15033     {
15034         e.stopPropagation();
15035         e.preventDefault();
15036         
15037         var target = e.getTarget();
15038         
15039         if(target.nodeName.toLowerCase() === 'i'){
15040             target = Roo.get(target).dom.parentNode;
15041         }
15042         
15043         var nodeName = target.nodeName;
15044         var className = target.className;
15045         var html = target.innerHTML;
15046         //Roo.log(nodeName);
15047         
15048         switch(nodeName.toLowerCase()) {
15049             case 'th':
15050                 switch(className) {
15051                     case 'switch':
15052                         this.showMode(1);
15053                         break;
15054                     case 'prev':
15055                     case 'next':
15056                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15057                         switch(this.viewMode){
15058                                 case 0:
15059                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15060                                         break;
15061                                 case 1:
15062                                 case 2:
15063                                         this.viewDate = this.moveYear(this.viewDate, dir);
15064                                         break;
15065                         }
15066                         this.fill();
15067                         break;
15068                     case 'today':
15069                         var date = new Date();
15070                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15071 //                        this.fill()
15072                         this.setValue(this.formatDate(this.date));
15073                         
15074                         this.hide();
15075                         break;
15076                 }
15077                 break;
15078             case 'span':
15079                 if (className.indexOf('disabled') < 0) {
15080                     this.viewDate.setUTCDate(1);
15081                     if (className.indexOf('month') > -1) {
15082                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15083                     } else {
15084                         var year = parseInt(html, 10) || 0;
15085                         this.viewDate.setUTCFullYear(year);
15086                         
15087                     }
15088                     this.showMode(-1);
15089                     this.fill();
15090                 }
15091                 break;
15092                 
15093             case 'td':
15094                 //Roo.log(className);
15095                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15096                     var day = parseInt(html, 10) || 1;
15097                     var year = this.viewDate.getUTCFullYear(),
15098                         month = this.viewDate.getUTCMonth();
15099
15100                     if (className.indexOf('old') > -1) {
15101                         if(month === 0 ){
15102                             month = 11;
15103                             year -= 1;
15104                         }else{
15105                             month -= 1;
15106                         }
15107                     } else if (className.indexOf('new') > -1) {
15108                         if (month == 11) {
15109                             month = 0;
15110                             year += 1;
15111                         } else {
15112                             month += 1;
15113                         }
15114                     }
15115                     //Roo.log([year,month,day]);
15116                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15117                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15118 //                    this.fill();
15119                     //Roo.log(this.formatDate(this.date));
15120                     this.setValue(this.formatDate(this.date));
15121                     this.hide();
15122                 }
15123                 break;
15124         }
15125     },
15126     
15127     setStartDate: function(startDate)
15128     {
15129         this.startDate = startDate || -Infinity;
15130         if (this.startDate !== -Infinity) {
15131             this.startDate = this.parseDate(this.startDate);
15132         }
15133         this.update();
15134         this.updateNavArrows();
15135     },
15136
15137     setEndDate: function(endDate)
15138     {
15139         this.endDate = endDate || Infinity;
15140         if (this.endDate !== Infinity) {
15141             this.endDate = this.parseDate(this.endDate);
15142         }
15143         this.update();
15144         this.updateNavArrows();
15145     },
15146     
15147     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15148     {
15149         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15150         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15151             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15152         }
15153         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15154             return parseInt(d, 10);
15155         });
15156         this.update();
15157         this.updateNavArrows();
15158     },
15159     
15160     updateNavArrows: function() 
15161     {
15162         var d = new Date(this.viewDate),
15163         year = d.getUTCFullYear(),
15164         month = d.getUTCMonth();
15165         
15166         Roo.each(this.picker().select('.prev', true).elements, function(v){
15167             v.show();
15168             switch (this.viewMode) {
15169                 case 0:
15170
15171                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15172                         v.hide();
15173                     }
15174                     break;
15175                 case 1:
15176                 case 2:
15177                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15178                         v.hide();
15179                     }
15180                     break;
15181             }
15182         });
15183         
15184         Roo.each(this.picker().select('.next', true).elements, function(v){
15185             v.show();
15186             switch (this.viewMode) {
15187                 case 0:
15188
15189                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15190                         v.hide();
15191                     }
15192                     break;
15193                 case 1:
15194                 case 2:
15195                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15196                         v.hide();
15197                     }
15198                     break;
15199             }
15200         })
15201     },
15202     
15203     moveMonth: function(date, dir)
15204     {
15205         if (!dir) return date;
15206         var new_date = new Date(date.valueOf()),
15207         day = new_date.getUTCDate(),
15208         month = new_date.getUTCMonth(),
15209         mag = Math.abs(dir),
15210         new_month, test;
15211         dir = dir > 0 ? 1 : -1;
15212         if (mag == 1){
15213             test = dir == -1
15214             // If going back one month, make sure month is not current month
15215             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15216             ? function(){
15217                 return new_date.getUTCMonth() == month;
15218             }
15219             // If going forward one month, make sure month is as expected
15220             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15221             : function(){
15222                 return new_date.getUTCMonth() != new_month;
15223             };
15224             new_month = month + dir;
15225             new_date.setUTCMonth(new_month);
15226             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15227             if (new_month < 0 || new_month > 11)
15228                 new_month = (new_month + 12) % 12;
15229         } else {
15230             // For magnitudes >1, move one month at a time...
15231             for (var i=0; i<mag; i++)
15232                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15233                 new_date = this.moveMonth(new_date, dir);
15234             // ...then reset the day, keeping it in the new month
15235             new_month = new_date.getUTCMonth();
15236             new_date.setUTCDate(day);
15237             test = function(){
15238                 return new_month != new_date.getUTCMonth();
15239             };
15240         }
15241         // Common date-resetting loop -- if date is beyond end of month, make it
15242         // end of month
15243         while (test()){
15244             new_date.setUTCDate(--day);
15245             new_date.setUTCMonth(new_month);
15246         }
15247         return new_date;
15248     },
15249
15250     moveYear: function(date, dir)
15251     {
15252         return this.moveMonth(date, dir*12);
15253     },
15254
15255     dateWithinRange: function(date)
15256     {
15257         return date >= this.startDate && date <= this.endDate;
15258     },
15259
15260     
15261     remove: function() 
15262     {
15263         this.picker().remove();
15264     }
15265    
15266 });
15267
15268 Roo.apply(Roo.bootstrap.DateField,  {
15269     
15270     head : {
15271         tag: 'thead',
15272         cn: [
15273         {
15274             tag: 'tr',
15275             cn: [
15276             {
15277                 tag: 'th',
15278                 cls: 'prev',
15279                 html: '<i class="fa fa-arrow-left"/>'
15280             },
15281             {
15282                 tag: 'th',
15283                 cls: 'switch',
15284                 colspan: '5'
15285             },
15286             {
15287                 tag: 'th',
15288                 cls: 'next',
15289                 html: '<i class="fa fa-arrow-right"/>'
15290             }
15291
15292             ]
15293         }
15294         ]
15295     },
15296     
15297     content : {
15298         tag: 'tbody',
15299         cn: [
15300         {
15301             tag: 'tr',
15302             cn: [
15303             {
15304                 tag: 'td',
15305                 colspan: '7'
15306             }
15307             ]
15308         }
15309         ]
15310     },
15311     
15312     footer : {
15313         tag: 'tfoot',
15314         cn: [
15315         {
15316             tag: 'tr',
15317             cn: [
15318             {
15319                 tag: 'th',
15320                 colspan: '7',
15321                 cls: 'today'
15322             }
15323                     
15324             ]
15325         }
15326         ]
15327     },
15328     
15329     dates:{
15330         en: {
15331             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15332             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15333             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15334             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15335             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15336             today: "Today"
15337         }
15338     },
15339     
15340     modes: [
15341     {
15342         clsName: 'days',
15343         navFnc: 'Month',
15344         navStep: 1
15345     },
15346     {
15347         clsName: 'months',
15348         navFnc: 'FullYear',
15349         navStep: 1
15350     },
15351     {
15352         clsName: 'years',
15353         navFnc: 'FullYear',
15354         navStep: 10
15355     }]
15356 });
15357
15358 Roo.apply(Roo.bootstrap.DateField,  {
15359   
15360     template : {
15361         tag: 'div',
15362         cls: 'datepicker dropdown-menu',
15363         cn: [
15364         {
15365             tag: 'div',
15366             cls: 'datepicker-days',
15367             cn: [
15368             {
15369                 tag: 'table',
15370                 cls: 'table-condensed',
15371                 cn:[
15372                 Roo.bootstrap.DateField.head,
15373                 {
15374                     tag: 'tbody'
15375                 },
15376                 Roo.bootstrap.DateField.footer
15377                 ]
15378             }
15379             ]
15380         },
15381         {
15382             tag: 'div',
15383             cls: 'datepicker-months',
15384             cn: [
15385             {
15386                 tag: 'table',
15387                 cls: 'table-condensed',
15388                 cn:[
15389                 Roo.bootstrap.DateField.head,
15390                 Roo.bootstrap.DateField.content,
15391                 Roo.bootstrap.DateField.footer
15392                 ]
15393             }
15394             ]
15395         },
15396         {
15397             tag: 'div',
15398             cls: 'datepicker-years',
15399             cn: [
15400             {
15401                 tag: 'table',
15402                 cls: 'table-condensed',
15403                 cn:[
15404                 Roo.bootstrap.DateField.head,
15405                 Roo.bootstrap.DateField.content,
15406                 Roo.bootstrap.DateField.footer
15407                 ]
15408             }
15409             ]
15410         }
15411         ]
15412     }
15413 });
15414
15415  
15416
15417  /*
15418  * - LGPL
15419  *
15420  * TimeField
15421  * 
15422  */
15423
15424 /**
15425  * @class Roo.bootstrap.TimeField
15426  * @extends Roo.bootstrap.Input
15427  * Bootstrap DateField class
15428  * 
15429  * 
15430  * @constructor
15431  * Create a new TimeField
15432  * @param {Object} config The config object
15433  */
15434
15435 Roo.bootstrap.TimeField = function(config){
15436     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15437     this.addEvents({
15438             /**
15439              * @event show
15440              * Fires when this field show.
15441              * @param {Roo.bootstrap.DateField} this
15442              * @param {Mixed} date The date value
15443              */
15444             show : true,
15445             /**
15446              * @event show
15447              * Fires when this field hide.
15448              * @param {Roo.bootstrap.DateField} this
15449              * @param {Mixed} date The date value
15450              */
15451             hide : true,
15452             /**
15453              * @event select
15454              * Fires when select a date.
15455              * @param {Roo.bootstrap.DateField} this
15456              * @param {Mixed} date The date value
15457              */
15458             select : true
15459         });
15460 };
15461
15462 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15463     
15464     /**
15465      * @cfg {String} format
15466      * The default time format string which can be overriden for localization support.  The format must be
15467      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15468      */
15469     format : "H:i",
15470        
15471     onRender: function(ct, position)
15472     {
15473         
15474         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15475                 
15476         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15477         
15478         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15479         
15480         this.pop = this.picker().select('>.datepicker-time',true).first();
15481         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15482         
15483         this.picker().on('mousedown', this.onMousedown, this);
15484         this.picker().on('click', this.onClick, this);
15485         
15486         this.picker().addClass('datepicker-dropdown');
15487     
15488         this.fillTime();
15489         this.update();
15490             
15491         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15492         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15493         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15494         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15495         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15496         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15497
15498     },
15499     
15500     fireKey: function(e){
15501         if (!this.picker().isVisible()){
15502             if (e.keyCode == 27) // allow escape to hide and re-show picker
15503                 this.show();
15504             return;
15505         }
15506
15507         e.preventDefault();
15508         
15509         switch(e.keyCode){
15510             case 27: // escape
15511                 this.hide();
15512                 break;
15513             case 37: // left
15514             case 39: // right
15515                 this.onTogglePeriod();
15516                 break;
15517             case 38: // up
15518                 this.onIncrementMinutes();
15519                 break;
15520             case 40: // down
15521                 this.onDecrementMinutes();
15522                 break;
15523             case 13: // enter
15524             case 9: // tab
15525                 this.setTime();
15526                 break;
15527         }
15528     },
15529     
15530     onClick: function(e) {
15531         e.stopPropagation();
15532         e.preventDefault();
15533     },
15534     
15535     picker : function()
15536     {
15537         return this.el.select('.datepicker', true).first();
15538     },
15539     
15540     fillTime: function()
15541     {    
15542         var time = this.pop.select('tbody', true).first();
15543         
15544         time.dom.innerHTML = '';
15545         
15546         time.createChild({
15547             tag: 'tr',
15548             cn: [
15549                 {
15550                     tag: 'td',
15551                     cn: [
15552                         {
15553                             tag: 'a',
15554                             href: '#',
15555                             cls: 'btn',
15556                             cn: [
15557                                 {
15558                                     tag: 'span',
15559                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15560                                 }
15561                             ]
15562                         } 
15563                     ]
15564                 },
15565                 {
15566                     tag: 'td',
15567                     cls: 'separator'
15568                 },
15569                 {
15570                     tag: 'td',
15571                     cn: [
15572                         {
15573                             tag: 'a',
15574                             href: '#',
15575                             cls: 'btn',
15576                             cn: [
15577                                 {
15578                                     tag: 'span',
15579                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15580                                 }
15581                             ]
15582                         }
15583                     ]
15584                 },
15585                 {
15586                     tag: 'td',
15587                     cls: 'separator'
15588                 }
15589             ]
15590         });
15591         
15592         time.createChild({
15593             tag: 'tr',
15594             cn: [
15595                 {
15596                     tag: 'td',
15597                     cn: [
15598                         {
15599                             tag: 'span',
15600                             cls: 'timepicker-hour',
15601                             html: '00'
15602                         }  
15603                     ]
15604                 },
15605                 {
15606                     tag: 'td',
15607                     cls: 'separator',
15608                     html: ':'
15609                 },
15610                 {
15611                     tag: 'td',
15612                     cn: [
15613                         {
15614                             tag: 'span',
15615                             cls: 'timepicker-minute',
15616                             html: '00'
15617                         }  
15618                     ]
15619                 },
15620                 {
15621                     tag: 'td',
15622                     cls: 'separator'
15623                 },
15624                 {
15625                     tag: 'td',
15626                     cn: [
15627                         {
15628                             tag: 'button',
15629                             type: 'button',
15630                             cls: 'btn btn-primary period',
15631                             html: 'AM'
15632                             
15633                         }
15634                     ]
15635                 }
15636             ]
15637         });
15638         
15639         time.createChild({
15640             tag: 'tr',
15641             cn: [
15642                 {
15643                     tag: 'td',
15644                     cn: [
15645                         {
15646                             tag: 'a',
15647                             href: '#',
15648                             cls: 'btn',
15649                             cn: [
15650                                 {
15651                                     tag: 'span',
15652                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15653                                 }
15654                             ]
15655                         }
15656                     ]
15657                 },
15658                 {
15659                     tag: 'td',
15660                     cls: 'separator'
15661                 },
15662                 {
15663                     tag: 'td',
15664                     cn: [
15665                         {
15666                             tag: 'a',
15667                             href: '#',
15668                             cls: 'btn',
15669                             cn: [
15670                                 {
15671                                     tag: 'span',
15672                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15673                                 }
15674                             ]
15675                         }
15676                     ]
15677                 },
15678                 {
15679                     tag: 'td',
15680                     cls: 'separator'
15681                 }
15682             ]
15683         });
15684         
15685     },
15686     
15687     update: function()
15688     {
15689         
15690         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15691         
15692         this.fill();
15693     },
15694     
15695     fill: function() 
15696     {
15697         var hours = this.time.getHours();
15698         var minutes = this.time.getMinutes();
15699         var period = 'AM';
15700         
15701         if(hours > 11){
15702             period = 'PM';
15703         }
15704         
15705         if(hours == 0){
15706             hours = 12;
15707         }
15708         
15709         
15710         if(hours > 12){
15711             hours = hours - 12;
15712         }
15713         
15714         if(hours < 10){
15715             hours = '0' + hours;
15716         }
15717         
15718         if(minutes < 10){
15719             minutes = '0' + minutes;
15720         }
15721         
15722         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15723         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15724         this.pop.select('button', true).first().dom.innerHTML = period;
15725         
15726     },
15727     
15728     place: function()
15729     {   
15730         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15731         
15732         var cls = ['bottom'];
15733         
15734         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15735             cls.pop();
15736             cls.push('top');
15737         }
15738         
15739         cls.push('right');
15740         
15741         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15742             cls.pop();
15743             cls.push('left');
15744         }
15745         
15746         this.picker().addClass(cls.join('-'));
15747         
15748         var _this = this;
15749         
15750         Roo.each(cls, function(c){
15751             if(c == 'bottom'){
15752                 _this.picker().setTop(_this.inputEl().getHeight());
15753                 return;
15754             }
15755             if(c == 'top'){
15756                 _this.picker().setTop(0 - _this.picker().getHeight());
15757                 return;
15758             }
15759             
15760             if(c == 'left'){
15761                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15762                 return;
15763             }
15764             if(c == 'right'){
15765                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15766                 return;
15767             }
15768         });
15769         
15770     },
15771   
15772     onFocus : function()
15773     {
15774         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15775         this.show();
15776     },
15777     
15778     onBlur : function()
15779     {
15780         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15781         this.hide();
15782     },
15783     
15784     show : function()
15785     {
15786         this.picker().show();
15787         this.pop.show();
15788         this.update();
15789         this.place();
15790         
15791         this.fireEvent('show', this, this.date);
15792     },
15793     
15794     hide : function()
15795     {
15796         this.picker().hide();
15797         this.pop.hide();
15798         
15799         this.fireEvent('hide', this, this.date);
15800     },
15801     
15802     setTime : function()
15803     {
15804         this.hide();
15805         this.setValue(this.time.format(this.format));
15806         
15807         this.fireEvent('select', this, this.date);
15808         
15809         
15810     },
15811     
15812     onMousedown: function(e){
15813         e.stopPropagation();
15814         e.preventDefault();
15815     },
15816     
15817     onIncrementHours: function()
15818     {
15819         Roo.log('onIncrementHours');
15820         this.time = this.time.add(Date.HOUR, 1);
15821         this.update();
15822         
15823     },
15824     
15825     onDecrementHours: function()
15826     {
15827         Roo.log('onDecrementHours');
15828         this.time = this.time.add(Date.HOUR, -1);
15829         this.update();
15830     },
15831     
15832     onIncrementMinutes: function()
15833     {
15834         Roo.log('onIncrementMinutes');
15835         this.time = this.time.add(Date.MINUTE, 1);
15836         this.update();
15837     },
15838     
15839     onDecrementMinutes: function()
15840     {
15841         Roo.log('onDecrementMinutes');
15842         this.time = this.time.add(Date.MINUTE, -1);
15843         this.update();
15844     },
15845     
15846     onTogglePeriod: function()
15847     {
15848         Roo.log('onTogglePeriod');
15849         this.time = this.time.add(Date.HOUR, 12);
15850         this.update();
15851     }
15852     
15853    
15854 });
15855
15856 Roo.apply(Roo.bootstrap.TimeField,  {
15857     
15858     content : {
15859         tag: 'tbody',
15860         cn: [
15861             {
15862                 tag: 'tr',
15863                 cn: [
15864                 {
15865                     tag: 'td',
15866                     colspan: '7'
15867                 }
15868                 ]
15869             }
15870         ]
15871     },
15872     
15873     footer : {
15874         tag: 'tfoot',
15875         cn: [
15876             {
15877                 tag: 'tr',
15878                 cn: [
15879                 {
15880                     tag: 'th',
15881                     colspan: '7',
15882                     cls: '',
15883                     cn: [
15884                         {
15885                             tag: 'button',
15886                             cls: 'btn btn-info ok',
15887                             html: 'OK'
15888                         }
15889                     ]
15890                 }
15891
15892                 ]
15893             }
15894         ]
15895     }
15896 });
15897
15898 Roo.apply(Roo.bootstrap.TimeField,  {
15899   
15900     template : {
15901         tag: 'div',
15902         cls: 'datepicker dropdown-menu',
15903         cn: [
15904             {
15905                 tag: 'div',
15906                 cls: 'datepicker-time',
15907                 cn: [
15908                 {
15909                     tag: 'table',
15910                     cls: 'table-condensed',
15911                     cn:[
15912                     Roo.bootstrap.TimeField.content,
15913                     Roo.bootstrap.TimeField.footer
15914                     ]
15915                 }
15916                 ]
15917             }
15918         ]
15919     }
15920 });
15921
15922  
15923
15924  /*
15925  * - LGPL
15926  *
15927  * CheckBox
15928  * 
15929  */
15930
15931 /**
15932  * @class Roo.bootstrap.CheckBox
15933  * @extends Roo.bootstrap.Input
15934  * Bootstrap CheckBox class
15935  * 
15936  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15937  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15938  * @cfg {String} boxLabel The text that appears beside the checkbox
15939  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15940  * @cfg {Boolean} checked initnal the element
15941  * 
15942  * 
15943  * @constructor
15944  * Create a new CheckBox
15945  * @param {Object} config The config object
15946  */
15947
15948 Roo.bootstrap.CheckBox = function(config){
15949     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15950    
15951         this.addEvents({
15952             /**
15953             * @event check
15954             * Fires when the element is checked or unchecked.
15955             * @param {Roo.bootstrap.CheckBox} this This input
15956             * @param {Boolean} checked The new checked value
15957             */
15958            check : true
15959         });
15960 };
15961
15962 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15963     
15964     inputType: 'checkbox',
15965     inputValue: 1,
15966     valueOff: 0,
15967     boxLabel: false,
15968     checked: false,
15969     weight : false,
15970     
15971     getAutoCreate : function()
15972     {
15973         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15974         
15975         var id = Roo.id();
15976         
15977         var cfg = {};
15978         
15979         cfg.cls = 'form-group checkbox' //input-group
15980         
15981         
15982         
15983         
15984         var input =  {
15985             tag: 'input',
15986             id : id,
15987             type : this.inputType,
15988             value : (!this.checked) ? this.valueOff : this.inputValue,
15989             cls : 'roo-checkbox', //'form-box',
15990             placeholder : this.placeholder || ''
15991             
15992         };
15993         
15994         if (this.weight) { // Validity check?
15995             cfg.cls += " checkbox-" + this.weight;
15996         }
15997         
15998         if (this.disabled) {
15999             input.disabled=true;
16000         }
16001         
16002         if(this.checked){
16003             input.checked = this.checked;
16004         }
16005         
16006         if (this.name) {
16007             input.name = this.name;
16008         }
16009         
16010         if (this.size) {
16011             input.cls += ' input-' + this.size;
16012         }
16013         
16014         var settings=this;
16015         ['xs','sm','md','lg'].map(function(size){
16016             if (settings[size]) {
16017                 cfg.cls += ' col-' + size + '-' + settings[size];
16018             }
16019         });
16020         
16021        
16022         
16023         var inputblock = input;
16024         
16025         
16026         
16027         
16028         if (this.before || this.after) {
16029             
16030             inputblock = {
16031                 cls : 'input-group',
16032                 cn :  [] 
16033             };
16034             if (this.before) {
16035                 inputblock.cn.push({
16036                     tag :'span',
16037                     cls : 'input-group-addon',
16038                     html : this.before
16039                 });
16040             }
16041             inputblock.cn.push(input);
16042             if (this.after) {
16043                 inputblock.cn.push({
16044                     tag :'span',
16045                     cls : 'input-group-addon',
16046                     html : this.after
16047                 });
16048             }
16049             
16050         };
16051         
16052         if (align ==='left' && this.fieldLabel.length) {
16053                 Roo.log("left and has label");
16054                 cfg.cn = [
16055                     
16056                     {
16057                         tag: 'label',
16058                         'for' :  id,
16059                         cls : 'control-label col-md-' + this.labelWidth,
16060                         html : this.fieldLabel
16061                         
16062                     },
16063                     {
16064                         cls : "col-md-" + (12 - this.labelWidth), 
16065                         cn: [
16066                             inputblock
16067                         ]
16068                     }
16069                     
16070                 ];
16071         } else if ( this.fieldLabel.length) {
16072                 Roo.log(" label");
16073                 cfg.cn = [
16074                    
16075                     {
16076                         tag: this.boxLabel ? 'span' : 'label',
16077                         'for': id,
16078                         cls: 'control-label box-input-label',
16079                         //cls : 'input-group-addon',
16080                         html : this.fieldLabel
16081                         
16082                     },
16083                     
16084                     inputblock
16085                     
16086                 ];
16087
16088         } else {
16089             
16090                 Roo.log(" no label && no align");
16091                 cfg.cn = [  inputblock ] ;
16092                 
16093                 
16094         };
16095          if(this.boxLabel){
16096             cfg.cn.push( {
16097                 tag: 'label',
16098                 'for': id,
16099                 cls: 'box-label',
16100                 html: this.boxLabel
16101                 
16102             });
16103         }
16104         
16105         
16106        
16107         return cfg;
16108         
16109     },
16110     
16111     /**
16112      * return the real input element.
16113      */
16114     inputEl: function ()
16115     {
16116         return this.el.select('input.roo-checkbox',true).first();
16117     },
16118     
16119     label: function()
16120     {
16121         return this.el.select('label.control-label',true).first();
16122     },
16123     
16124     initEvents : function()
16125     {
16126 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16127         
16128         this.inputEl().on('click', this.onClick,  this);
16129         
16130     },
16131     
16132     onClick : function()
16133     {   
16134         this.setChecked(!this.checked);
16135     },
16136     
16137     setChecked : function(state,suppressEvent)
16138     {
16139         this.checked = state;
16140         
16141         this.inputEl().dom.checked = state;
16142         
16143         if(suppressEvent !== true){
16144             this.fireEvent('check', this, state);
16145         }
16146         
16147         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16148         
16149     },
16150     
16151     setValue : function(v,suppressEvent)
16152     {
16153         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16154     }
16155     
16156 });
16157
16158  
16159 /*
16160  * - LGPL
16161  *
16162  * Radio
16163  * 
16164  */
16165
16166 /**
16167  * @class Roo.bootstrap.Radio
16168  * @extends Roo.bootstrap.CheckBox
16169  * Bootstrap Radio class
16170
16171  * @constructor
16172  * Create a new Radio
16173  * @param {Object} config The config object
16174  */
16175
16176 Roo.bootstrap.Radio = function(config){
16177     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16178    
16179 };
16180
16181 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16182     
16183     inputType: 'radio',
16184     inputValue: '',
16185     valueOff: '',
16186     
16187     getAutoCreate : function()
16188     {
16189         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16190         
16191         var id = Roo.id();
16192         
16193         var cfg = {};
16194         
16195         cfg.cls = 'form-group radio' //input-group
16196         
16197         var input =  {
16198             tag: 'input',
16199             id : id,
16200             type : this.inputType,
16201             value : (!this.checked) ? this.valueOff : this.inputValue,
16202             cls : 'roo-radio',
16203             placeholder : this.placeholder || ''
16204             
16205         };
16206           if (this.weight) { // Validity check?
16207             cfg.cls += " radio-" + this.weight;
16208         }
16209         if (this.disabled) {
16210             input.disabled=true;
16211         }
16212         
16213         if(this.checked){
16214             input.checked = this.checked;
16215         }
16216         
16217         if (this.name) {
16218             input.name = this.name;
16219         }
16220         
16221         if (this.size) {
16222             input.cls += ' input-' + this.size;
16223         }
16224         
16225         var settings=this;
16226         ['xs','sm','md','lg'].map(function(size){
16227             if (settings[size]) {
16228                 cfg.cls += ' col-' + size + '-' + settings[size];
16229             }
16230         });
16231         
16232         var inputblock = input;
16233         
16234         if (this.before || this.after) {
16235             
16236             inputblock = {
16237                 cls : 'input-group',
16238                 cn :  [] 
16239             };
16240             if (this.before) {
16241                 inputblock.cn.push({
16242                     tag :'span',
16243                     cls : 'input-group-addon',
16244                     html : this.before
16245                 });
16246             }
16247             inputblock.cn.push(input);
16248             if (this.after) {
16249                 inputblock.cn.push({
16250                     tag :'span',
16251                     cls : 'input-group-addon',
16252                     html : this.after
16253                 });
16254             }
16255             
16256         };
16257         
16258         if (align ==='left' && this.fieldLabel.length) {
16259                 Roo.log("left and has label");
16260                 cfg.cn = [
16261                     
16262                     {
16263                         tag: 'label',
16264                         'for' :  id,
16265                         cls : 'control-label col-md-' + this.labelWidth,
16266                         html : this.fieldLabel
16267                         
16268                     },
16269                     {
16270                         cls : "col-md-" + (12 - this.labelWidth), 
16271                         cn: [
16272                             inputblock
16273                         ]
16274                     }
16275                     
16276                 ];
16277         } else if ( this.fieldLabel.length) {
16278                 Roo.log(" label");
16279                  cfg.cn = [
16280                    
16281                     {
16282                         tag: 'label',
16283                         'for': id,
16284                         cls: 'control-label box-input-label',
16285                         //cls : 'input-group-addon',
16286                         html : this.fieldLabel
16287                         
16288                     },
16289                     
16290                     inputblock
16291                     
16292                 ];
16293
16294         } else {
16295             
16296                    Roo.log(" no label && no align");
16297                 cfg.cn = [
16298                     
16299                         inputblock
16300                     
16301                 ];
16302                 
16303                 
16304         };
16305         
16306         if(this.boxLabel){
16307             cfg.cn.push({
16308                 tag: 'label',
16309                 'for': id,
16310                 cls: 'box-label',
16311                 html: this.boxLabel
16312             })
16313         }
16314         
16315         return cfg;
16316         
16317     },
16318     inputEl: function ()
16319     {
16320         return this.el.select('input.roo-radio',true).first();
16321     },
16322     onClick : function()
16323     {   
16324         this.setChecked(true);
16325     },
16326     
16327     setChecked : function(state,suppressEvent)
16328     {
16329         if(state){
16330             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16331                 v.dom.checked = false;
16332             });
16333         }
16334         
16335         this.checked = state;
16336         this.inputEl().dom.checked = state;
16337         
16338         if(suppressEvent !== true){
16339             this.fireEvent('check', this, state);
16340         }
16341         
16342         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16343         
16344     },
16345     
16346     getGroupValue : function()
16347     {
16348         var value = ''
16349         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16350             if(v.dom.checked == true){
16351                 value = v.dom.value;
16352             }
16353         });
16354         
16355         return value;
16356     },
16357     
16358     /**
16359      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16360      * @return {Mixed} value The field value
16361      */
16362     getValue : function(){
16363         return this.getGroupValue();
16364     }
16365     
16366 });
16367
16368  
16369 //<script type="text/javascript">
16370
16371 /*
16372  * Based  Ext JS Library 1.1.1
16373  * Copyright(c) 2006-2007, Ext JS, LLC.
16374  * LGPL
16375  *
16376  */
16377  
16378 /**
16379  * @class Roo.HtmlEditorCore
16380  * @extends Roo.Component
16381  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16382  *
16383  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16384  */
16385
16386 Roo.HtmlEditorCore = function(config){
16387     
16388     
16389     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16390     
16391     
16392     this.addEvents({
16393         /**
16394          * @event initialize
16395          * Fires when the editor is fully initialized (including the iframe)
16396          * @param {Roo.HtmlEditorCore} this
16397          */
16398         initialize: true,
16399         /**
16400          * @event activate
16401          * Fires when the editor is first receives the focus. Any insertion must wait
16402          * until after this event.
16403          * @param {Roo.HtmlEditorCore} this
16404          */
16405         activate: true,
16406          /**
16407          * @event beforesync
16408          * Fires before the textarea is updated with content from the editor iframe. Return false
16409          * to cancel the sync.
16410          * @param {Roo.HtmlEditorCore} this
16411          * @param {String} html
16412          */
16413         beforesync: true,
16414          /**
16415          * @event beforepush
16416          * Fires before the iframe editor is updated with content from the textarea. Return false
16417          * to cancel the push.
16418          * @param {Roo.HtmlEditorCore} this
16419          * @param {String} html
16420          */
16421         beforepush: true,
16422          /**
16423          * @event sync
16424          * Fires when the textarea is updated with content from the editor iframe.
16425          * @param {Roo.HtmlEditorCore} this
16426          * @param {String} html
16427          */
16428         sync: true,
16429          /**
16430          * @event push
16431          * Fires when the iframe editor is updated with content from the textarea.
16432          * @param {Roo.HtmlEditorCore} this
16433          * @param {String} html
16434          */
16435         push: true,
16436         
16437         /**
16438          * @event editorevent
16439          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16440          * @param {Roo.HtmlEditorCore} this
16441          */
16442         editorevent: true
16443     });
16444     
16445     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16446     
16447     // defaults : white / black...
16448     this.applyBlacklists();
16449     
16450     
16451     
16452 };
16453
16454
16455 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16456
16457
16458      /**
16459      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16460      */
16461     
16462     owner : false,
16463     
16464      /**
16465      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16466      *                        Roo.resizable.
16467      */
16468     resizable : false,
16469      /**
16470      * @cfg {Number} height (in pixels)
16471      */   
16472     height: 300,
16473    /**
16474      * @cfg {Number} width (in pixels)
16475      */   
16476     width: 500,
16477     
16478     /**
16479      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16480      * 
16481      */
16482     stylesheets: false,
16483     
16484     // id of frame..
16485     frameId: false,
16486     
16487     // private properties
16488     validationEvent : false,
16489     deferHeight: true,
16490     initialized : false,
16491     activated : false,
16492     sourceEditMode : false,
16493     onFocus : Roo.emptyFn,
16494     iframePad:3,
16495     hideMode:'offsets',
16496     
16497     clearUp: true,
16498     
16499     // blacklist + whitelisted elements..
16500     black: false,
16501     white: false,
16502      
16503     
16504
16505     /**
16506      * Protected method that will not generally be called directly. It
16507      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16508      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16509      */
16510     getDocMarkup : function(){
16511         // body styles..
16512         var st = '';
16513         Roo.log(this.stylesheets);
16514         
16515         // inherit styels from page...?? 
16516         if (this.stylesheets === false) {
16517             
16518             Roo.get(document.head).select('style').each(function(node) {
16519                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16520             });
16521             
16522             Roo.get(document.head).select('link').each(function(node) { 
16523                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16524             });
16525             
16526         } else if (!this.stylesheets.length) {
16527                 // simple..
16528                 st = '<style type="text/css">' +
16529                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16530                    '</style>';
16531         } else {
16532             Roo.each(this.stylesheets, function(s) {
16533                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16534             });
16535             
16536         }
16537         
16538         st +=  '<style type="text/css">' +
16539             'IMG { cursor: pointer } ' +
16540         '</style>';
16541
16542         
16543         return '<html><head>' + st  +
16544             //<style type="text/css">' +
16545             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16546             //'</style>' +
16547             ' </head><body class="roo-htmleditor-body"></body></html>';
16548     },
16549
16550     // private
16551     onRender : function(ct, position)
16552     {
16553         var _t = this;
16554         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16555         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16556         
16557         
16558         this.el.dom.style.border = '0 none';
16559         this.el.dom.setAttribute('tabIndex', -1);
16560         this.el.addClass('x-hidden hide');
16561         
16562         
16563         
16564         if(Roo.isIE){ // fix IE 1px bogus margin
16565             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16566         }
16567        
16568         
16569         this.frameId = Roo.id();
16570         
16571          
16572         
16573         var iframe = this.owner.wrap.createChild({
16574             tag: 'iframe',
16575             cls: 'form-control', // bootstrap..
16576             id: this.frameId,
16577             name: this.frameId,
16578             frameBorder : 'no',
16579             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16580         }, this.el
16581         );
16582         
16583         
16584         this.iframe = iframe.dom;
16585
16586          this.assignDocWin();
16587         
16588         this.doc.designMode = 'on';
16589        
16590         this.doc.open();
16591         this.doc.write(this.getDocMarkup());
16592         this.doc.close();
16593
16594         
16595         var task = { // must defer to wait for browser to be ready
16596             run : function(){
16597                 //console.log("run task?" + this.doc.readyState);
16598                 this.assignDocWin();
16599                 if(this.doc.body || this.doc.readyState == 'complete'){
16600                     try {
16601                         this.doc.designMode="on";
16602                     } catch (e) {
16603                         return;
16604                     }
16605                     Roo.TaskMgr.stop(task);
16606                     this.initEditor.defer(10, this);
16607                 }
16608             },
16609             interval : 10,
16610             duration: 10000,
16611             scope: this
16612         };
16613         Roo.TaskMgr.start(task);
16614
16615         
16616          
16617     },
16618
16619     // private
16620     onResize : function(w, h)
16621     {
16622          Roo.log('resize: ' +w + ',' + h );
16623         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16624         if(!this.iframe){
16625             return;
16626         }
16627         if(typeof w == 'number'){
16628             
16629             this.iframe.style.width = w + 'px';
16630         }
16631         if(typeof h == 'number'){
16632             
16633             this.iframe.style.height = h + 'px';
16634             if(this.doc){
16635                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16636             }
16637         }
16638         
16639     },
16640
16641     /**
16642      * Toggles the editor between standard and source edit mode.
16643      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16644      */
16645     toggleSourceEdit : function(sourceEditMode){
16646         
16647         this.sourceEditMode = sourceEditMode === true;
16648         
16649         if(this.sourceEditMode){
16650  
16651             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16652             
16653         }else{
16654             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16655             //this.iframe.className = '';
16656             this.deferFocus();
16657         }
16658         //this.setSize(this.owner.wrap.getSize());
16659         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16660     },
16661
16662     
16663   
16664
16665     /**
16666      * Protected method that will not generally be called directly. If you need/want
16667      * custom HTML cleanup, this is the method you should override.
16668      * @param {String} html The HTML to be cleaned
16669      * return {String} The cleaned HTML
16670      */
16671     cleanHtml : function(html){
16672         html = String(html);
16673         if(html.length > 5){
16674             if(Roo.isSafari){ // strip safari nonsense
16675                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16676             }
16677         }
16678         if(html == '&nbsp;'){
16679             html = '';
16680         }
16681         return html;
16682     },
16683
16684     /**
16685      * HTML Editor -> Textarea
16686      * Protected method that will not generally be called directly. Syncs the contents
16687      * of the editor iframe with the textarea.
16688      */
16689     syncValue : function(){
16690         if(this.initialized){
16691             var bd = (this.doc.body || this.doc.documentElement);
16692             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16693             var html = bd.innerHTML;
16694             if(Roo.isSafari){
16695                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16696                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16697                 if(m && m[1]){
16698                     html = '<div style="'+m[0]+'">' + html + '</div>';
16699                 }
16700             }
16701             html = this.cleanHtml(html);
16702             // fix up the special chars.. normaly like back quotes in word...
16703             // however we do not want to do this with chinese..
16704             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16705                 var cc = b.charCodeAt();
16706                 if (
16707                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16708                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16709                     (cc >= 0xf900 && cc < 0xfb00 )
16710                 ) {
16711                         return b;
16712                 }
16713                 return "&#"+cc+";" 
16714             });
16715             if(this.owner.fireEvent('beforesync', this, html) !== false){
16716                 this.el.dom.value = html;
16717                 this.owner.fireEvent('sync', this, html);
16718             }
16719         }
16720     },
16721
16722     /**
16723      * Protected method that will not generally be called directly. Pushes the value of the textarea
16724      * into the iframe editor.
16725      */
16726     pushValue : function(){
16727         if(this.initialized){
16728             var v = this.el.dom.value.trim();
16729             
16730 //            if(v.length < 1){
16731 //                v = '&#160;';
16732 //            }
16733             
16734             if(this.owner.fireEvent('beforepush', this, v) !== false){
16735                 var d = (this.doc.body || this.doc.documentElement);
16736                 d.innerHTML = v;
16737                 this.cleanUpPaste();
16738                 this.el.dom.value = d.innerHTML;
16739                 this.owner.fireEvent('push', this, v);
16740             }
16741         }
16742     },
16743
16744     // private
16745     deferFocus : function(){
16746         this.focus.defer(10, this);
16747     },
16748
16749     // doc'ed in Field
16750     focus : function(){
16751         if(this.win && !this.sourceEditMode){
16752             this.win.focus();
16753         }else{
16754             this.el.focus();
16755         }
16756     },
16757     
16758     assignDocWin: function()
16759     {
16760         var iframe = this.iframe;
16761         
16762          if(Roo.isIE){
16763             this.doc = iframe.contentWindow.document;
16764             this.win = iframe.contentWindow;
16765         } else {
16766 //            if (!Roo.get(this.frameId)) {
16767 //                return;
16768 //            }
16769 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16770 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16771             
16772             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16773                 return;
16774             }
16775             
16776             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16777             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16778         }
16779     },
16780     
16781     // private
16782     initEditor : function(){
16783         //console.log("INIT EDITOR");
16784         this.assignDocWin();
16785         
16786         
16787         
16788         this.doc.designMode="on";
16789         this.doc.open();
16790         this.doc.write(this.getDocMarkup());
16791         this.doc.close();
16792         
16793         var dbody = (this.doc.body || this.doc.documentElement);
16794         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16795         // this copies styles from the containing element into thsi one..
16796         // not sure why we need all of this..
16797         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16798         
16799         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16800         //ss['background-attachment'] = 'fixed'; // w3c
16801         dbody.bgProperties = 'fixed'; // ie
16802         //Roo.DomHelper.applyStyles(dbody, ss);
16803         Roo.EventManager.on(this.doc, {
16804             //'mousedown': this.onEditorEvent,
16805             'mouseup': this.onEditorEvent,
16806             'dblclick': this.onEditorEvent,
16807             'click': this.onEditorEvent,
16808             'keyup': this.onEditorEvent,
16809             buffer:100,
16810             scope: this
16811         });
16812         if(Roo.isGecko){
16813             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16814         }
16815         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16816             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16817         }
16818         this.initialized = true;
16819
16820         this.owner.fireEvent('initialize', this);
16821         this.pushValue();
16822     },
16823
16824     // private
16825     onDestroy : function(){
16826         
16827         
16828         
16829         if(this.rendered){
16830             
16831             //for (var i =0; i < this.toolbars.length;i++) {
16832             //    // fixme - ask toolbars for heights?
16833             //    this.toolbars[i].onDestroy();
16834            // }
16835             
16836             //this.wrap.dom.innerHTML = '';
16837             //this.wrap.remove();
16838         }
16839     },
16840
16841     // private
16842     onFirstFocus : function(){
16843         
16844         this.assignDocWin();
16845         
16846         
16847         this.activated = true;
16848          
16849     
16850         if(Roo.isGecko){ // prevent silly gecko errors
16851             this.win.focus();
16852             var s = this.win.getSelection();
16853             if(!s.focusNode || s.focusNode.nodeType != 3){
16854                 var r = s.getRangeAt(0);
16855                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16856                 r.collapse(true);
16857                 this.deferFocus();
16858             }
16859             try{
16860                 this.execCmd('useCSS', true);
16861                 this.execCmd('styleWithCSS', false);
16862             }catch(e){}
16863         }
16864         this.owner.fireEvent('activate', this);
16865     },
16866
16867     // private
16868     adjustFont: function(btn){
16869         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16870         //if(Roo.isSafari){ // safari
16871         //    adjust *= 2;
16872        // }
16873         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16874         if(Roo.isSafari){ // safari
16875             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16876             v =  (v < 10) ? 10 : v;
16877             v =  (v > 48) ? 48 : v;
16878             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16879             
16880         }
16881         
16882         
16883         v = Math.max(1, v+adjust);
16884         
16885         this.execCmd('FontSize', v  );
16886     },
16887
16888     onEditorEvent : function(e){
16889         this.owner.fireEvent('editorevent', this, e);
16890       //  this.updateToolbar();
16891         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16892     },
16893
16894     insertTag : function(tg)
16895     {
16896         // could be a bit smarter... -> wrap the current selected tRoo..
16897         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16898             
16899             range = this.createRange(this.getSelection());
16900             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16901             wrappingNode.appendChild(range.extractContents());
16902             range.insertNode(wrappingNode);
16903
16904             return;
16905             
16906             
16907             
16908         }
16909         this.execCmd("formatblock",   tg);
16910         
16911     },
16912     
16913     insertText : function(txt)
16914     {
16915         
16916         
16917         var range = this.createRange();
16918         range.deleteContents();
16919                //alert(Sender.getAttribute('label'));
16920                
16921         range.insertNode(this.doc.createTextNode(txt));
16922     } ,
16923     
16924      
16925
16926     /**
16927      * Executes a Midas editor command on the editor document and performs necessary focus and
16928      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16929      * @param {String} cmd The Midas command
16930      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16931      */
16932     relayCmd : function(cmd, value){
16933         this.win.focus();
16934         this.execCmd(cmd, value);
16935         this.owner.fireEvent('editorevent', this);
16936         //this.updateToolbar();
16937         this.owner.deferFocus();
16938     },
16939
16940     /**
16941      * Executes a Midas editor command directly on the editor document.
16942      * For visual commands, you should use {@link #relayCmd} instead.
16943      * <b>This should only be called after the editor is initialized.</b>
16944      * @param {String} cmd The Midas command
16945      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16946      */
16947     execCmd : function(cmd, value){
16948         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16949         this.syncValue();
16950     },
16951  
16952  
16953    
16954     /**
16955      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16956      * to insert tRoo.
16957      * @param {String} text | dom node.. 
16958      */
16959     insertAtCursor : function(text)
16960     {
16961         
16962         
16963         
16964         if(!this.activated){
16965             return;
16966         }
16967         /*
16968         if(Roo.isIE){
16969             this.win.focus();
16970             var r = this.doc.selection.createRange();
16971             if(r){
16972                 r.collapse(true);
16973                 r.pasteHTML(text);
16974                 this.syncValue();
16975                 this.deferFocus();
16976             
16977             }
16978             return;
16979         }
16980         */
16981         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16982             this.win.focus();
16983             
16984             
16985             // from jquery ui (MIT licenced)
16986             var range, node;
16987             var win = this.win;
16988             
16989             if (win.getSelection && win.getSelection().getRangeAt) {
16990                 range = win.getSelection().getRangeAt(0);
16991                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16992                 range.insertNode(node);
16993             } else if (win.document.selection && win.document.selection.createRange) {
16994                 // no firefox support
16995                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16996                 win.document.selection.createRange().pasteHTML(txt);
16997             } else {
16998                 // no firefox support
16999                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17000                 this.execCmd('InsertHTML', txt);
17001             } 
17002             
17003             this.syncValue();
17004             
17005             this.deferFocus();
17006         }
17007     },
17008  // private
17009     mozKeyPress : function(e){
17010         if(e.ctrlKey){
17011             var c = e.getCharCode(), cmd;
17012           
17013             if(c > 0){
17014                 c = String.fromCharCode(c).toLowerCase();
17015                 switch(c){
17016                     case 'b':
17017                         cmd = 'bold';
17018                         break;
17019                     case 'i':
17020                         cmd = 'italic';
17021                         break;
17022                     
17023                     case 'u':
17024                         cmd = 'underline';
17025                         break;
17026                     
17027                     case 'v':
17028                         this.cleanUpPaste.defer(100, this);
17029                         return;
17030                         
17031                 }
17032                 if(cmd){
17033                     this.win.focus();
17034                     this.execCmd(cmd);
17035                     this.deferFocus();
17036                     e.preventDefault();
17037                 }
17038                 
17039             }
17040         }
17041     },
17042
17043     // private
17044     fixKeys : function(){ // load time branching for fastest keydown performance
17045         if(Roo.isIE){
17046             return function(e){
17047                 var k = e.getKey(), r;
17048                 if(k == e.TAB){
17049                     e.stopEvent();
17050                     r = this.doc.selection.createRange();
17051                     if(r){
17052                         r.collapse(true);
17053                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17054                         this.deferFocus();
17055                     }
17056                     return;
17057                 }
17058                 
17059                 if(k == e.ENTER){
17060                     r = this.doc.selection.createRange();
17061                     if(r){
17062                         var target = r.parentElement();
17063                         if(!target || target.tagName.toLowerCase() != 'li'){
17064                             e.stopEvent();
17065                             r.pasteHTML('<br />');
17066                             r.collapse(false);
17067                             r.select();
17068                         }
17069                     }
17070                 }
17071                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17072                     this.cleanUpPaste.defer(100, this);
17073                     return;
17074                 }
17075                 
17076                 
17077             };
17078         }else if(Roo.isOpera){
17079             return function(e){
17080                 var k = e.getKey();
17081                 if(k == e.TAB){
17082                     e.stopEvent();
17083                     this.win.focus();
17084                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17085                     this.deferFocus();
17086                 }
17087                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17088                     this.cleanUpPaste.defer(100, this);
17089                     return;
17090                 }
17091                 
17092             };
17093         }else if(Roo.isSafari){
17094             return function(e){
17095                 var k = e.getKey();
17096                 
17097                 if(k == e.TAB){
17098                     e.stopEvent();
17099                     this.execCmd('InsertText','\t');
17100                     this.deferFocus();
17101                     return;
17102                 }
17103                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17104                     this.cleanUpPaste.defer(100, this);
17105                     return;
17106                 }
17107                 
17108              };
17109         }
17110     }(),
17111     
17112     getAllAncestors: function()
17113     {
17114         var p = this.getSelectedNode();
17115         var a = [];
17116         if (!p) {
17117             a.push(p); // push blank onto stack..
17118             p = this.getParentElement();
17119         }
17120         
17121         
17122         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17123             a.push(p);
17124             p = p.parentNode;
17125         }
17126         a.push(this.doc.body);
17127         return a;
17128     },
17129     lastSel : false,
17130     lastSelNode : false,
17131     
17132     
17133     getSelection : function() 
17134     {
17135         this.assignDocWin();
17136         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17137     },
17138     
17139     getSelectedNode: function() 
17140     {
17141         // this may only work on Gecko!!!
17142         
17143         // should we cache this!!!!
17144         
17145         
17146         
17147          
17148         var range = this.createRange(this.getSelection()).cloneRange();
17149         
17150         if (Roo.isIE) {
17151             var parent = range.parentElement();
17152             while (true) {
17153                 var testRange = range.duplicate();
17154                 testRange.moveToElementText(parent);
17155                 if (testRange.inRange(range)) {
17156                     break;
17157                 }
17158                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17159                     break;
17160                 }
17161                 parent = parent.parentElement;
17162             }
17163             return parent;
17164         }
17165         
17166         // is ancestor a text element.
17167         var ac =  range.commonAncestorContainer;
17168         if (ac.nodeType == 3) {
17169             ac = ac.parentNode;
17170         }
17171         
17172         var ar = ac.childNodes;
17173          
17174         var nodes = [];
17175         var other_nodes = [];
17176         var has_other_nodes = false;
17177         for (var i=0;i<ar.length;i++) {
17178             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17179                 continue;
17180             }
17181             // fullly contained node.
17182             
17183             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17184                 nodes.push(ar[i]);
17185                 continue;
17186             }
17187             
17188             // probably selected..
17189             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17190                 other_nodes.push(ar[i]);
17191                 continue;
17192             }
17193             // outer..
17194             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17195                 continue;
17196             }
17197             
17198             
17199             has_other_nodes = true;
17200         }
17201         if (!nodes.length && other_nodes.length) {
17202             nodes= other_nodes;
17203         }
17204         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17205             return false;
17206         }
17207         
17208         return nodes[0];
17209     },
17210     createRange: function(sel)
17211     {
17212         // this has strange effects when using with 
17213         // top toolbar - not sure if it's a great idea.
17214         //this.editor.contentWindow.focus();
17215         if (typeof sel != "undefined") {
17216             try {
17217                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17218             } catch(e) {
17219                 return this.doc.createRange();
17220             }
17221         } else {
17222             return this.doc.createRange();
17223         }
17224     },
17225     getParentElement: function()
17226     {
17227         
17228         this.assignDocWin();
17229         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17230         
17231         var range = this.createRange(sel);
17232          
17233         try {
17234             var p = range.commonAncestorContainer;
17235             while (p.nodeType == 3) { // text node
17236                 p = p.parentNode;
17237             }
17238             return p;
17239         } catch (e) {
17240             return null;
17241         }
17242     
17243     },
17244     /***
17245      *
17246      * Range intersection.. the hard stuff...
17247      *  '-1' = before
17248      *  '0' = hits..
17249      *  '1' = after.
17250      *         [ -- selected range --- ]
17251      *   [fail]                        [fail]
17252      *
17253      *    basically..
17254      *      if end is before start or  hits it. fail.
17255      *      if start is after end or hits it fail.
17256      *
17257      *   if either hits (but other is outside. - then it's not 
17258      *   
17259      *    
17260      **/
17261     
17262     
17263     // @see http://www.thismuchiknow.co.uk/?p=64.
17264     rangeIntersectsNode : function(range, node)
17265     {
17266         var nodeRange = node.ownerDocument.createRange();
17267         try {
17268             nodeRange.selectNode(node);
17269         } catch (e) {
17270             nodeRange.selectNodeContents(node);
17271         }
17272     
17273         var rangeStartRange = range.cloneRange();
17274         rangeStartRange.collapse(true);
17275     
17276         var rangeEndRange = range.cloneRange();
17277         rangeEndRange.collapse(false);
17278     
17279         var nodeStartRange = nodeRange.cloneRange();
17280         nodeStartRange.collapse(true);
17281     
17282         var nodeEndRange = nodeRange.cloneRange();
17283         nodeEndRange.collapse(false);
17284     
17285         return rangeStartRange.compareBoundaryPoints(
17286                  Range.START_TO_START, nodeEndRange) == -1 &&
17287                rangeEndRange.compareBoundaryPoints(
17288                  Range.START_TO_START, nodeStartRange) == 1;
17289         
17290          
17291     },
17292     rangeCompareNode : function(range, node)
17293     {
17294         var nodeRange = node.ownerDocument.createRange();
17295         try {
17296             nodeRange.selectNode(node);
17297         } catch (e) {
17298             nodeRange.selectNodeContents(node);
17299         }
17300         
17301         
17302         range.collapse(true);
17303     
17304         nodeRange.collapse(true);
17305      
17306         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17307         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17308          
17309         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17310         
17311         var nodeIsBefore   =  ss == 1;
17312         var nodeIsAfter    = ee == -1;
17313         
17314         if (nodeIsBefore && nodeIsAfter)
17315             return 0; // outer
17316         if (!nodeIsBefore && nodeIsAfter)
17317             return 1; //right trailed.
17318         
17319         if (nodeIsBefore && !nodeIsAfter)
17320             return 2;  // left trailed.
17321         // fully contined.
17322         return 3;
17323     },
17324
17325     // private? - in a new class?
17326     cleanUpPaste :  function()
17327     {
17328         // cleans up the whole document..
17329         Roo.log('cleanuppaste');
17330         
17331         this.cleanUpChildren(this.doc.body);
17332         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17333         if (clean != this.doc.body.innerHTML) {
17334             this.doc.body.innerHTML = clean;
17335         }
17336         
17337     },
17338     
17339     cleanWordChars : function(input) {// change the chars to hex code
17340         var he = Roo.HtmlEditorCore;
17341         
17342         var output = input;
17343         Roo.each(he.swapCodes, function(sw) { 
17344             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17345             
17346             output = output.replace(swapper, sw[1]);
17347         });
17348         
17349         return output;
17350     },
17351     
17352     
17353     cleanUpChildren : function (n)
17354     {
17355         if (!n.childNodes.length) {
17356             return;
17357         }
17358         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17359            this.cleanUpChild(n.childNodes[i]);
17360         }
17361     },
17362     
17363     
17364         
17365     
17366     cleanUpChild : function (node)
17367     {
17368         var ed = this;
17369         //console.log(node);
17370         if (node.nodeName == "#text") {
17371             // clean up silly Windows -- stuff?
17372             return; 
17373         }
17374         if (node.nodeName == "#comment") {
17375             node.parentNode.removeChild(node);
17376             // clean up silly Windows -- stuff?
17377             return; 
17378         }
17379         var lcname = node.tagName.toLowerCase();
17380         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17381         // whitelist of tags..
17382         
17383         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17384             // remove node.
17385             node.parentNode.removeChild(node);
17386             return;
17387             
17388         }
17389         
17390         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17391         
17392         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17393         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17394         
17395         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17396         //    remove_keep_children = true;
17397         //}
17398         
17399         if (remove_keep_children) {
17400             this.cleanUpChildren(node);
17401             // inserts everything just before this node...
17402             while (node.childNodes.length) {
17403                 var cn = node.childNodes[0];
17404                 node.removeChild(cn);
17405                 node.parentNode.insertBefore(cn, node);
17406             }
17407             node.parentNode.removeChild(node);
17408             return;
17409         }
17410         
17411         if (!node.attributes || !node.attributes.length) {
17412             this.cleanUpChildren(node);
17413             return;
17414         }
17415         
17416         function cleanAttr(n,v)
17417         {
17418             
17419             if (v.match(/^\./) || v.match(/^\//)) {
17420                 return;
17421             }
17422             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17423                 return;
17424             }
17425             if (v.match(/^#/)) {
17426                 return;
17427             }
17428 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17429             node.removeAttribute(n);
17430             
17431         }
17432         
17433         var cwhite = this.cwhite;
17434         var cblack = this.cblack;
17435             
17436         function cleanStyle(n,v)
17437         {
17438             if (v.match(/expression/)) { //XSS?? should we even bother..
17439                 node.removeAttribute(n);
17440                 return;
17441             }
17442             
17443             var parts = v.split(/;/);
17444             var clean = [];
17445             
17446             Roo.each(parts, function(p) {
17447                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17448                 if (!p.length) {
17449                     return true;
17450                 }
17451                 var l = p.split(':').shift().replace(/\s+/g,'');
17452                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17453                 
17454                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17455 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17456                     //node.removeAttribute(n);
17457                     return true;
17458                 }
17459                 //Roo.log()
17460                 // only allow 'c whitelisted system attributes'
17461                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17462 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17463                     //node.removeAttribute(n);
17464                     return true;
17465                 }
17466                 
17467                 
17468                  
17469                 
17470                 clean.push(p);
17471                 return true;
17472             });
17473             if (clean.length) { 
17474                 node.setAttribute(n, clean.join(';'));
17475             } else {
17476                 node.removeAttribute(n);
17477             }
17478             
17479         }
17480         
17481         
17482         for (var i = node.attributes.length-1; i > -1 ; i--) {
17483             var a = node.attributes[i];
17484             //console.log(a);
17485             
17486             if (a.name.toLowerCase().substr(0,2)=='on')  {
17487                 node.removeAttribute(a.name);
17488                 continue;
17489             }
17490             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17491                 node.removeAttribute(a.name);
17492                 continue;
17493             }
17494             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17495                 cleanAttr(a.name,a.value); // fixme..
17496                 continue;
17497             }
17498             if (a.name == 'style') {
17499                 cleanStyle(a.name,a.value);
17500                 continue;
17501             }
17502             /// clean up MS crap..
17503             // tecnically this should be a list of valid class'es..
17504             
17505             
17506             if (a.name == 'class') {
17507                 if (a.value.match(/^Mso/)) {
17508                     node.className = '';
17509                 }
17510                 
17511                 if (a.value.match(/body/)) {
17512                     node.className = '';
17513                 }
17514                 continue;
17515             }
17516             
17517             // style cleanup!?
17518             // class cleanup?
17519             
17520         }
17521         
17522         
17523         this.cleanUpChildren(node);
17524         
17525         
17526     },
17527     /**
17528      * Clean up MS wordisms...
17529      */
17530     cleanWord : function(node)
17531     {
17532         var _t = this;
17533         var cleanWordChildren = function()
17534         {
17535             if (!node.childNodes.length) {
17536                 return;
17537             }
17538             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17539                _t.cleanWord(node.childNodes[i]);
17540             }
17541         }
17542         
17543         
17544         if (!node) {
17545             this.cleanWord(this.doc.body);
17546             return;
17547         }
17548         if (node.nodeName == "#text") {
17549             // clean up silly Windows -- stuff?
17550             return; 
17551         }
17552         if (node.nodeName == "#comment") {
17553             node.parentNode.removeChild(node);
17554             // clean up silly Windows -- stuff?
17555             return; 
17556         }
17557         
17558         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17559             node.parentNode.removeChild(node);
17560             return;
17561         }
17562         
17563         // remove - but keep children..
17564         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17565             while (node.childNodes.length) {
17566                 var cn = node.childNodes[0];
17567                 node.removeChild(cn);
17568                 node.parentNode.insertBefore(cn, node);
17569             }
17570             node.parentNode.removeChild(node);
17571             cleanWordChildren();
17572             return;
17573         }
17574         // clean styles
17575         if (node.className.length) {
17576             
17577             var cn = node.className.split(/\W+/);
17578             var cna = [];
17579             Roo.each(cn, function(cls) {
17580                 if (cls.match(/Mso[a-zA-Z]+/)) {
17581                     return;
17582                 }
17583                 cna.push(cls);
17584             });
17585             node.className = cna.length ? cna.join(' ') : '';
17586             if (!cna.length) {
17587                 node.removeAttribute("class");
17588             }
17589         }
17590         
17591         if (node.hasAttribute("lang")) {
17592             node.removeAttribute("lang");
17593         }
17594         
17595         if (node.hasAttribute("style")) {
17596             
17597             var styles = node.getAttribute("style").split(";");
17598             var nstyle = [];
17599             Roo.each(styles, function(s) {
17600                 if (!s.match(/:/)) {
17601                     return;
17602                 }
17603                 var kv = s.split(":");
17604                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17605                     return;
17606                 }
17607                 // what ever is left... we allow.
17608                 nstyle.push(s);
17609             });
17610             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17611             if (!nstyle.length) {
17612                 node.removeAttribute('style');
17613             }
17614         }
17615         
17616         cleanWordChildren();
17617         
17618         
17619     },
17620     domToHTML : function(currentElement, depth, nopadtext) {
17621         
17622         depth = depth || 0;
17623         nopadtext = nopadtext || false;
17624     
17625         if (!currentElement) {
17626             return this.domToHTML(this.doc.body);
17627         }
17628         
17629         //Roo.log(currentElement);
17630         var j;
17631         var allText = false;
17632         var nodeName = currentElement.nodeName;
17633         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17634         
17635         if  (nodeName == '#text') {
17636             return currentElement.nodeValue;
17637         }
17638         
17639         
17640         var ret = '';
17641         if (nodeName != 'BODY') {
17642              
17643             var i = 0;
17644             // Prints the node tagName, such as <A>, <IMG>, etc
17645             if (tagName) {
17646                 var attr = [];
17647                 for(i = 0; i < currentElement.attributes.length;i++) {
17648                     // quoting?
17649                     var aname = currentElement.attributes.item(i).name;
17650                     if (!currentElement.attributes.item(i).value.length) {
17651                         continue;
17652                     }
17653                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17654                 }
17655                 
17656                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17657             } 
17658             else {
17659                 
17660                 // eack
17661             }
17662         } else {
17663             tagName = false;
17664         }
17665         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17666             return ret;
17667         }
17668         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17669             nopadtext = true;
17670         }
17671         
17672         
17673         // Traverse the tree
17674         i = 0;
17675         var currentElementChild = currentElement.childNodes.item(i);
17676         var allText = true;
17677         var innerHTML  = '';
17678         lastnode = '';
17679         while (currentElementChild) {
17680             // Formatting code (indent the tree so it looks nice on the screen)
17681             var nopad = nopadtext;
17682             if (lastnode == 'SPAN') {
17683                 nopad  = true;
17684             }
17685             // text
17686             if  (currentElementChild.nodeName == '#text') {
17687                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17688                 if (!nopad && toadd.length > 80) {
17689                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17690                 }
17691                 innerHTML  += toadd;
17692                 
17693                 i++;
17694                 currentElementChild = currentElement.childNodes.item(i);
17695                 lastNode = '';
17696                 continue;
17697             }
17698             allText = false;
17699             
17700             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17701                 
17702             // Recursively traverse the tree structure of the child node
17703             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17704             lastnode = currentElementChild.nodeName;
17705             i++;
17706             currentElementChild=currentElement.childNodes.item(i);
17707         }
17708         
17709         ret += innerHTML;
17710         
17711         if (!allText) {
17712                 // The remaining code is mostly for formatting the tree
17713             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17714         }
17715         
17716         
17717         if (tagName) {
17718             ret+= "</"+tagName+">";
17719         }
17720         return ret;
17721         
17722     },
17723         
17724     applyBlacklists : function()
17725     {
17726         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17727         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17728         
17729         this.white = [];
17730         this.black = [];
17731         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17732             if (b.indexOf(tag) > -1) {
17733                 return;
17734             }
17735             this.white.push(tag);
17736             
17737         }, this);
17738         
17739         Roo.each(w, function(tag) {
17740             if (b.indexOf(tag) > -1) {
17741                 return;
17742             }
17743             if (this.white.indexOf(tag) > -1) {
17744                 return;
17745             }
17746             this.white.push(tag);
17747             
17748         }, this);
17749         
17750         
17751         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17752             if (w.indexOf(tag) > -1) {
17753                 return;
17754             }
17755             this.black.push(tag);
17756             
17757         }, this);
17758         
17759         Roo.each(b, function(tag) {
17760             if (w.indexOf(tag) > -1) {
17761                 return;
17762             }
17763             if (this.black.indexOf(tag) > -1) {
17764                 return;
17765             }
17766             this.black.push(tag);
17767             
17768         }, this);
17769         
17770         
17771         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17772         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17773         
17774         this.cwhite = [];
17775         this.cblack = [];
17776         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17777             if (b.indexOf(tag) > -1) {
17778                 return;
17779             }
17780             this.cwhite.push(tag);
17781             
17782         }, this);
17783         
17784         Roo.each(w, function(tag) {
17785             if (b.indexOf(tag) > -1) {
17786                 return;
17787             }
17788             if (this.cwhite.indexOf(tag) > -1) {
17789                 return;
17790             }
17791             this.cwhite.push(tag);
17792             
17793         }, this);
17794         
17795         
17796         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17797             if (w.indexOf(tag) > -1) {
17798                 return;
17799             }
17800             this.cblack.push(tag);
17801             
17802         }, this);
17803         
17804         Roo.each(b, function(tag) {
17805             if (w.indexOf(tag) > -1) {
17806                 return;
17807             }
17808             if (this.cblack.indexOf(tag) > -1) {
17809                 return;
17810             }
17811             this.cblack.push(tag);
17812             
17813         }, this);
17814     }
17815     
17816     // hide stuff that is not compatible
17817     /**
17818      * @event blur
17819      * @hide
17820      */
17821     /**
17822      * @event change
17823      * @hide
17824      */
17825     /**
17826      * @event focus
17827      * @hide
17828      */
17829     /**
17830      * @event specialkey
17831      * @hide
17832      */
17833     /**
17834      * @cfg {String} fieldClass @hide
17835      */
17836     /**
17837      * @cfg {String} focusClass @hide
17838      */
17839     /**
17840      * @cfg {String} autoCreate @hide
17841      */
17842     /**
17843      * @cfg {String} inputType @hide
17844      */
17845     /**
17846      * @cfg {String} invalidClass @hide
17847      */
17848     /**
17849      * @cfg {String} invalidText @hide
17850      */
17851     /**
17852      * @cfg {String} msgFx @hide
17853      */
17854     /**
17855      * @cfg {String} validateOnBlur @hide
17856      */
17857 });
17858
17859 Roo.HtmlEditorCore.white = [
17860         'area', 'br', 'img', 'input', 'hr', 'wbr',
17861         
17862        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17863        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17864        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17865        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17866        'table',   'ul',         'xmp', 
17867        
17868        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17869       'thead',   'tr', 
17870      
17871       'dir', 'menu', 'ol', 'ul', 'dl',
17872        
17873       'embed',  'object'
17874 ];
17875
17876
17877 Roo.HtmlEditorCore.black = [
17878     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17879         'applet', // 
17880         'base',   'basefont', 'bgsound', 'blink',  'body', 
17881         'frame',  'frameset', 'head',    'html',   'ilayer', 
17882         'iframe', 'layer',  'link',     'meta',    'object',   
17883         'script', 'style' ,'title',  'xml' // clean later..
17884 ];
17885 Roo.HtmlEditorCore.clean = [
17886     'script', 'style', 'title', 'xml'
17887 ];
17888 Roo.HtmlEditorCore.remove = [
17889     'font'
17890 ];
17891 // attributes..
17892
17893 Roo.HtmlEditorCore.ablack = [
17894     'on'
17895 ];
17896     
17897 Roo.HtmlEditorCore.aclean = [ 
17898     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17899 ];
17900
17901 // protocols..
17902 Roo.HtmlEditorCore.pwhite= [
17903         'http',  'https',  'mailto'
17904 ];
17905
17906 // white listed style attributes.
17907 Roo.HtmlEditorCore.cwhite= [
17908       //  'text-align', /// default is to allow most things..
17909       
17910          
17911 //        'font-size'//??
17912 ];
17913
17914 // black listed style attributes.
17915 Roo.HtmlEditorCore.cblack= [
17916       //  'font-size' -- this can be set by the project 
17917 ];
17918
17919
17920 Roo.HtmlEditorCore.swapCodes   =[ 
17921     [    8211, "--" ], 
17922     [    8212, "--" ], 
17923     [    8216,  "'" ],  
17924     [    8217, "'" ],  
17925     [    8220, '"' ],  
17926     [    8221, '"' ],  
17927     [    8226, "*" ],  
17928     [    8230, "..." ]
17929 ]; 
17930
17931     /*
17932  * - LGPL
17933  *
17934  * HtmlEditor
17935  * 
17936  */
17937
17938 /**
17939  * @class Roo.bootstrap.HtmlEditor
17940  * @extends Roo.bootstrap.TextArea
17941  * Bootstrap HtmlEditor class
17942
17943  * @constructor
17944  * Create a new HtmlEditor
17945  * @param {Object} config The config object
17946  */
17947
17948 Roo.bootstrap.HtmlEditor = function(config){
17949     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17950     if (!this.toolbars) {
17951         this.toolbars = [];
17952     }
17953     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17954     this.addEvents({
17955             /**
17956              * @event initialize
17957              * Fires when the editor is fully initialized (including the iframe)
17958              * @param {HtmlEditor} this
17959              */
17960             initialize: true,
17961             /**
17962              * @event activate
17963              * Fires when the editor is first receives the focus. Any insertion must wait
17964              * until after this event.
17965              * @param {HtmlEditor} this
17966              */
17967             activate: true,
17968              /**
17969              * @event beforesync
17970              * Fires before the textarea is updated with content from the editor iframe. Return false
17971              * to cancel the sync.
17972              * @param {HtmlEditor} this
17973              * @param {String} html
17974              */
17975             beforesync: true,
17976              /**
17977              * @event beforepush
17978              * Fires before the iframe editor is updated with content from the textarea. Return false
17979              * to cancel the push.
17980              * @param {HtmlEditor} this
17981              * @param {String} html
17982              */
17983             beforepush: true,
17984              /**
17985              * @event sync
17986              * Fires when the textarea is updated with content from the editor iframe.
17987              * @param {HtmlEditor} this
17988              * @param {String} html
17989              */
17990             sync: true,
17991              /**
17992              * @event push
17993              * Fires when the iframe editor is updated with content from the textarea.
17994              * @param {HtmlEditor} this
17995              * @param {String} html
17996              */
17997             push: true,
17998              /**
17999              * @event editmodechange
18000              * Fires when the editor switches edit modes
18001              * @param {HtmlEditor} this
18002              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18003              */
18004             editmodechange: true,
18005             /**
18006              * @event editorevent
18007              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18008              * @param {HtmlEditor} this
18009              */
18010             editorevent: true,
18011             /**
18012              * @event firstfocus
18013              * Fires when on first focus - needed by toolbars..
18014              * @param {HtmlEditor} this
18015              */
18016             firstfocus: true,
18017             /**
18018              * @event autosave
18019              * Auto save the htmlEditor value as a file into Events
18020              * @param {HtmlEditor} this
18021              */
18022             autosave: true,
18023             /**
18024              * @event savedpreview
18025              * preview the saved version of htmlEditor
18026              * @param {HtmlEditor} this
18027              */
18028             savedpreview: true
18029         });
18030 };
18031
18032
18033 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18034     
18035     
18036       /**
18037      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18038      */
18039     toolbars : false,
18040    
18041      /**
18042      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18043      *                        Roo.resizable.
18044      */
18045     resizable : false,
18046      /**
18047      * @cfg {Number} height (in pixels)
18048      */   
18049     height: 300,
18050    /**
18051      * @cfg {Number} width (in pixels)
18052      */   
18053     width: false,
18054     
18055     /**
18056      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18057      * 
18058      */
18059     stylesheets: false,
18060     
18061     // id of frame..
18062     frameId: false,
18063     
18064     // private properties
18065     validationEvent : false,
18066     deferHeight: true,
18067     initialized : false,
18068     activated : false,
18069     
18070     onFocus : Roo.emptyFn,
18071     iframePad:3,
18072     hideMode:'offsets',
18073     
18074     
18075     tbContainer : false,
18076     
18077     toolbarContainer :function() {
18078         return this.wrap.select('.x-html-editor-tb',true).first();
18079     },
18080
18081     /**
18082      * Protected method that will not generally be called directly. It
18083      * is called when the editor creates its toolbar. Override this method if you need to
18084      * add custom toolbar buttons.
18085      * @param {HtmlEditor} editor
18086      */
18087     createToolbar : function(){
18088         
18089         Roo.log("create toolbars");
18090         
18091         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18092         this.toolbars[0].render(this.toolbarContainer());
18093         
18094         return;
18095         
18096 //        if (!editor.toolbars || !editor.toolbars.length) {
18097 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18098 //        }
18099 //        
18100 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18101 //            editor.toolbars[i] = Roo.factory(
18102 //                    typeof(editor.toolbars[i]) == 'string' ?
18103 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18104 //                Roo.bootstrap.HtmlEditor);
18105 //            editor.toolbars[i].init(editor);
18106 //        }
18107     },
18108
18109      
18110     // private
18111     onRender : function(ct, position)
18112     {
18113        // Roo.log("Call onRender: " + this.xtype);
18114         var _t = this;
18115         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18116       
18117         this.wrap = this.inputEl().wrap({
18118             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18119         });
18120         
18121         this.editorcore.onRender(ct, position);
18122          
18123         if (this.resizable) {
18124             this.resizeEl = new Roo.Resizable(this.wrap, {
18125                 pinned : true,
18126                 wrap: true,
18127                 dynamic : true,
18128                 minHeight : this.height,
18129                 height: this.height,
18130                 handles : this.resizable,
18131                 width: this.width,
18132                 listeners : {
18133                     resize : function(r, w, h) {
18134                         _t.onResize(w,h); // -something
18135                     }
18136                 }
18137             });
18138             
18139         }
18140         this.createToolbar(this);
18141        
18142         
18143         if(!this.width && this.resizable){
18144             this.setSize(this.wrap.getSize());
18145         }
18146         if (this.resizeEl) {
18147             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18148             // should trigger onReize..
18149         }
18150         
18151     },
18152
18153     // private
18154     onResize : function(w, h)
18155     {
18156         Roo.log('resize: ' +w + ',' + h );
18157         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18158         var ew = false;
18159         var eh = false;
18160         
18161         if(this.inputEl() ){
18162             if(typeof w == 'number'){
18163                 var aw = w - this.wrap.getFrameWidth('lr');
18164                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18165                 ew = aw;
18166             }
18167             if(typeof h == 'number'){
18168                  var tbh = -11;  // fixme it needs to tool bar size!
18169                 for (var i =0; i < this.toolbars.length;i++) {
18170                     // fixme - ask toolbars for heights?
18171                     tbh += this.toolbars[i].el.getHeight();
18172                     //if (this.toolbars[i].footer) {
18173                     //    tbh += this.toolbars[i].footer.el.getHeight();
18174                     //}
18175                 }
18176               
18177                 
18178                 
18179                 
18180                 
18181                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18182                 ah -= 5; // knock a few pixes off for look..
18183                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18184                 var eh = ah;
18185             }
18186         }
18187         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18188         this.editorcore.onResize(ew,eh);
18189         
18190     },
18191
18192     /**
18193      * Toggles the editor between standard and source edit mode.
18194      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18195      */
18196     toggleSourceEdit : function(sourceEditMode)
18197     {
18198         this.editorcore.toggleSourceEdit(sourceEditMode);
18199         
18200         if(this.editorcore.sourceEditMode){
18201             Roo.log('editor - showing textarea');
18202             
18203 //            Roo.log('in');
18204 //            Roo.log(this.syncValue());
18205             this.syncValue();
18206             this.inputEl().removeClass(['hide', 'x-hidden']);
18207             this.inputEl().dom.removeAttribute('tabIndex');
18208             this.inputEl().focus();
18209         }else{
18210             Roo.log('editor - hiding textarea');
18211 //            Roo.log('out')
18212 //            Roo.log(this.pushValue()); 
18213             this.pushValue();
18214             
18215             this.inputEl().addClass(['hide', 'x-hidden']);
18216             this.inputEl().dom.setAttribute('tabIndex', -1);
18217             //this.deferFocus();
18218         }
18219          
18220         if(this.resizable){
18221             this.setSize(this.wrap.getSize());
18222         }
18223         
18224         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18225     },
18226  
18227     // private (for BoxComponent)
18228     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18229
18230     // private (for BoxComponent)
18231     getResizeEl : function(){
18232         return this.wrap;
18233     },
18234
18235     // private (for BoxComponent)
18236     getPositionEl : function(){
18237         return this.wrap;
18238     },
18239
18240     // private
18241     initEvents : function(){
18242         this.originalValue = this.getValue();
18243     },
18244
18245 //    /**
18246 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18247 //     * @method
18248 //     */
18249 //    markInvalid : Roo.emptyFn,
18250 //    /**
18251 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18252 //     * @method
18253 //     */
18254 //    clearInvalid : Roo.emptyFn,
18255
18256     setValue : function(v){
18257         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18258         this.editorcore.pushValue();
18259     },
18260
18261      
18262     // private
18263     deferFocus : function(){
18264         this.focus.defer(10, this);
18265     },
18266
18267     // doc'ed in Field
18268     focus : function(){
18269         this.editorcore.focus();
18270         
18271     },
18272       
18273
18274     // private
18275     onDestroy : function(){
18276         
18277         
18278         
18279         if(this.rendered){
18280             
18281             for (var i =0; i < this.toolbars.length;i++) {
18282                 // fixme - ask toolbars for heights?
18283                 this.toolbars[i].onDestroy();
18284             }
18285             
18286             this.wrap.dom.innerHTML = '';
18287             this.wrap.remove();
18288         }
18289     },
18290
18291     // private
18292     onFirstFocus : function(){
18293         //Roo.log("onFirstFocus");
18294         this.editorcore.onFirstFocus();
18295          for (var i =0; i < this.toolbars.length;i++) {
18296             this.toolbars[i].onFirstFocus();
18297         }
18298         
18299     },
18300     
18301     // private
18302     syncValue : function()
18303     {   
18304         this.editorcore.syncValue();
18305     },
18306     
18307     pushValue : function()
18308     {   
18309         this.editorcore.pushValue();
18310     }
18311      
18312     
18313     // hide stuff that is not compatible
18314     /**
18315      * @event blur
18316      * @hide
18317      */
18318     /**
18319      * @event change
18320      * @hide
18321      */
18322     /**
18323      * @event focus
18324      * @hide
18325      */
18326     /**
18327      * @event specialkey
18328      * @hide
18329      */
18330     /**
18331      * @cfg {String} fieldClass @hide
18332      */
18333     /**
18334      * @cfg {String} focusClass @hide
18335      */
18336     /**
18337      * @cfg {String} autoCreate @hide
18338      */
18339     /**
18340      * @cfg {String} inputType @hide
18341      */
18342     /**
18343      * @cfg {String} invalidClass @hide
18344      */
18345     /**
18346      * @cfg {String} invalidText @hide
18347      */
18348     /**
18349      * @cfg {String} msgFx @hide
18350      */
18351     /**
18352      * @cfg {String} validateOnBlur @hide
18353      */
18354 });
18355  
18356     
18357    
18358    
18359    
18360       
18361 Roo.namespace('Roo.bootstrap.htmleditor');
18362 /**
18363  * @class Roo.bootstrap.HtmlEditorToolbar1
18364  * Basic Toolbar
18365  * 
18366  * Usage:
18367  *
18368  new Roo.bootstrap.HtmlEditor({
18369     ....
18370     toolbars : [
18371         new Roo.bootstrap.HtmlEditorToolbar1({
18372             disable : { fonts: 1 , format: 1, ..., ... , ...],
18373             btns : [ .... ]
18374         })
18375     }
18376      
18377  * 
18378  * @cfg {Object} disable List of elements to disable..
18379  * @cfg {Array} btns List of additional buttons.
18380  * 
18381  * 
18382  * NEEDS Extra CSS? 
18383  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18384  */
18385  
18386 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18387 {
18388     
18389     Roo.apply(this, config);
18390     
18391     // default disabled, based on 'good practice'..
18392     this.disable = this.disable || {};
18393     Roo.applyIf(this.disable, {
18394         fontSize : true,
18395         colors : true,
18396         specialElements : true
18397     });
18398     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18399     
18400     this.editor = config.editor;
18401     this.editorcore = config.editor.editorcore;
18402     
18403     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18404     
18405     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18406     // dont call parent... till later.
18407 }
18408 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18409      
18410     bar : true,
18411     
18412     editor : false,
18413     editorcore : false,
18414     
18415     
18416     formats : [
18417         "p" ,  
18418         "h1","h2","h3","h4","h5","h6", 
18419         "pre", "code", 
18420         "abbr", "acronym", "address", "cite", "samp", "var",
18421         'div','span'
18422     ],
18423     
18424     onRender : function(ct, position)
18425     {
18426        // Roo.log("Call onRender: " + this.xtype);
18427         
18428        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18429        Roo.log(this.el);
18430        this.el.dom.style.marginBottom = '0';
18431        var _this = this;
18432        var editorcore = this.editorcore;
18433        var editor= this.editor;
18434        
18435        var children = [];
18436        var btn = function(id,cmd , toggle, handler){
18437        
18438             var  event = toggle ? 'toggle' : 'click';
18439        
18440             var a = {
18441                 size : 'sm',
18442                 xtype: 'Button',
18443                 xns: Roo.bootstrap,
18444                 glyphicon : id,
18445                 cmd : id || cmd,
18446                 enableToggle:toggle !== false,
18447                 //html : 'submit'
18448                 pressed : toggle ? false : null,
18449                 listeners : {}
18450             }
18451             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18452                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18453             }
18454             children.push(a);
18455             return a;
18456        }
18457         
18458         var style = {
18459                 xtype: 'Button',
18460                 size : 'sm',
18461                 xns: Roo.bootstrap,
18462                 glyphicon : 'font',
18463                 //html : 'submit'
18464                 menu : {
18465                     xtype: 'Menu',
18466                     xns: Roo.bootstrap,
18467                     items:  []
18468                 }
18469         };
18470         Roo.each(this.formats, function(f) {
18471             style.menu.items.push({
18472                 xtype :'MenuItem',
18473                 xns: Roo.bootstrap,
18474                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18475                 tagname : f,
18476                 listeners : {
18477                     click : function()
18478                     {
18479                         editorcore.insertTag(this.tagname);
18480                         editor.focus();
18481                     }
18482                 }
18483                 
18484             });
18485         });
18486          children.push(style);   
18487             
18488             
18489         btn('bold',false,true);
18490         btn('italic',false,true);
18491         btn('align-left', 'justifyleft',true);
18492         btn('align-center', 'justifycenter',true);
18493         btn('align-right' , 'justifyright',true);
18494         btn('link', false, false, function(btn) {
18495             //Roo.log("create link?");
18496             var url = prompt(this.createLinkText, this.defaultLinkValue);
18497             if(url && url != 'http:/'+'/'){
18498                 this.editorcore.relayCmd('createlink', url);
18499             }
18500         }),
18501         btn('list','insertunorderedlist',true);
18502         btn('pencil', false,true, function(btn){
18503                 Roo.log(this);
18504                 
18505                 this.toggleSourceEdit(btn.pressed);
18506         });
18507         /*
18508         var cog = {
18509                 xtype: 'Button',
18510                 size : 'sm',
18511                 xns: Roo.bootstrap,
18512                 glyphicon : 'cog',
18513                 //html : 'submit'
18514                 menu : {
18515                     xtype: 'Menu',
18516                     xns: Roo.bootstrap,
18517                     items:  []
18518                 }
18519         };
18520         
18521         cog.menu.items.push({
18522             xtype :'MenuItem',
18523             xns: Roo.bootstrap,
18524             html : Clean styles,
18525             tagname : f,
18526             listeners : {
18527                 click : function()
18528                 {
18529                     editorcore.insertTag(this.tagname);
18530                     editor.focus();
18531                 }
18532             }
18533             
18534         });
18535        */
18536         
18537          
18538        this.xtype = 'NavSimplebar';
18539         
18540         for(var i=0;i< children.length;i++) {
18541             
18542             this.buttons.add(this.addxtypeChild(children[i]));
18543             
18544         }
18545         
18546         editor.on('editorevent', this.updateToolbar, this);
18547     },
18548     onBtnClick : function(id)
18549     {
18550        this.editorcore.relayCmd(id);
18551        this.editorcore.focus();
18552     },
18553     
18554     /**
18555      * Protected method that will not generally be called directly. It triggers
18556      * a toolbar update by reading the markup state of the current selection in the editor.
18557      */
18558     updateToolbar: function(){
18559
18560         if(!this.editorcore.activated){
18561             this.editor.onFirstFocus(); // is this neeed?
18562             return;
18563         }
18564
18565         var btns = this.buttons; 
18566         var doc = this.editorcore.doc;
18567         btns.get('bold').setActive(doc.queryCommandState('bold'));
18568         btns.get('italic').setActive(doc.queryCommandState('italic'));
18569         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18570         
18571         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18572         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18573         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18574         
18575         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18576         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18577          /*
18578         
18579         var ans = this.editorcore.getAllAncestors();
18580         if (this.formatCombo) {
18581             
18582             
18583             var store = this.formatCombo.store;
18584             this.formatCombo.setValue("");
18585             for (var i =0; i < ans.length;i++) {
18586                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18587                     // select it..
18588                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18589                     break;
18590                 }
18591             }
18592         }
18593         
18594         
18595         
18596         // hides menus... - so this cant be on a menu...
18597         Roo.bootstrap.MenuMgr.hideAll();
18598         */
18599         Roo.bootstrap.MenuMgr.hideAll();
18600         //this.editorsyncValue();
18601     },
18602     onFirstFocus: function() {
18603         this.buttons.each(function(item){
18604            item.enable();
18605         });
18606     },
18607     toggleSourceEdit : function(sourceEditMode){
18608         
18609           
18610         if(sourceEditMode){
18611             Roo.log("disabling buttons");
18612            this.buttons.each( function(item){
18613                 if(item.cmd != 'pencil'){
18614                     item.disable();
18615                 }
18616             });
18617           
18618         }else{
18619             Roo.log("enabling buttons");
18620             if(this.editorcore.initialized){
18621                 this.buttons.each( function(item){
18622                     item.enable();
18623                 });
18624             }
18625             
18626         }
18627         Roo.log("calling toggole on editor");
18628         // tell the editor that it's been pressed..
18629         this.editor.toggleSourceEdit(sourceEditMode);
18630        
18631     }
18632 });
18633
18634
18635
18636
18637
18638 /**
18639  * @class Roo.bootstrap.Table.AbstractSelectionModel
18640  * @extends Roo.util.Observable
18641  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18642  * implemented by descendant classes.  This class should not be directly instantiated.
18643  * @constructor
18644  */
18645 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18646     this.locked = false;
18647     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18648 };
18649
18650
18651 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18652     /** @ignore Called by the grid automatically. Do not call directly. */
18653     init : function(grid){
18654         this.grid = grid;
18655         this.initEvents();
18656     },
18657
18658     /**
18659      * Locks the selections.
18660      */
18661     lock : function(){
18662         this.locked = true;
18663     },
18664
18665     /**
18666      * Unlocks the selections.
18667      */
18668     unlock : function(){
18669         this.locked = false;
18670     },
18671
18672     /**
18673      * Returns true if the selections are locked.
18674      * @return {Boolean}
18675      */
18676     isLocked : function(){
18677         return this.locked;
18678     }
18679 });
18680 /**
18681  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18682  * @class Roo.bootstrap.Table.RowSelectionModel
18683  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18684  * It supports multiple selections and keyboard selection/navigation. 
18685  * @constructor
18686  * @param {Object} config
18687  */
18688
18689 Roo.bootstrap.Table.RowSelectionModel = function(config){
18690     Roo.apply(this, config);
18691     this.selections = new Roo.util.MixedCollection(false, function(o){
18692         return o.id;
18693     });
18694
18695     this.last = false;
18696     this.lastActive = false;
18697
18698     this.addEvents({
18699         /**
18700              * @event selectionchange
18701              * Fires when the selection changes
18702              * @param {SelectionModel} this
18703              */
18704             "selectionchange" : true,
18705         /**
18706              * @event afterselectionchange
18707              * Fires after the selection changes (eg. by key press or clicking)
18708              * @param {SelectionModel} this
18709              */
18710             "afterselectionchange" : true,
18711         /**
18712              * @event beforerowselect
18713              * Fires when a row is selected being selected, return false to cancel.
18714              * @param {SelectionModel} this
18715              * @param {Number} rowIndex The selected index
18716              * @param {Boolean} keepExisting False if other selections will be cleared
18717              */
18718             "beforerowselect" : true,
18719         /**
18720              * @event rowselect
18721              * Fires when a row is selected.
18722              * @param {SelectionModel} this
18723              * @param {Number} rowIndex The selected index
18724              * @param {Roo.data.Record} r The record
18725              */
18726             "rowselect" : true,
18727         /**
18728              * @event rowdeselect
18729              * Fires when a row is deselected.
18730              * @param {SelectionModel} this
18731              * @param {Number} rowIndex The selected index
18732              */
18733         "rowdeselect" : true
18734     });
18735     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18736     this.locked = false;
18737 };
18738
18739 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18740     /**
18741      * @cfg {Boolean} singleSelect
18742      * True to allow selection of only one row at a time (defaults to false)
18743      */
18744     singleSelect : false,
18745
18746     // private
18747     initEvents : function(){
18748
18749         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18750             this.grid.on("mousedown", this.handleMouseDown, this);
18751         }else{ // allow click to work like normal
18752             this.grid.on("rowclick", this.handleDragableRowClick, this);
18753         }
18754
18755         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18756             "up" : function(e){
18757                 if(!e.shiftKey){
18758                     this.selectPrevious(e.shiftKey);
18759                 }else if(this.last !== false && this.lastActive !== false){
18760                     var last = this.last;
18761                     this.selectRange(this.last,  this.lastActive-1);
18762                     this.grid.getView().focusRow(this.lastActive);
18763                     if(last !== false){
18764                         this.last = last;
18765                     }
18766                 }else{
18767                     this.selectFirstRow();
18768                 }
18769                 this.fireEvent("afterselectionchange", this);
18770             },
18771             "down" : function(e){
18772                 if(!e.shiftKey){
18773                     this.selectNext(e.shiftKey);
18774                 }else if(this.last !== false && this.lastActive !== false){
18775                     var last = this.last;
18776                     this.selectRange(this.last,  this.lastActive+1);
18777                     this.grid.getView().focusRow(this.lastActive);
18778                     if(last !== false){
18779                         this.last = last;
18780                     }
18781                 }else{
18782                     this.selectFirstRow();
18783                 }
18784                 this.fireEvent("afterselectionchange", this);
18785             },
18786             scope: this
18787         });
18788
18789         var view = this.grid.view;
18790         view.on("refresh", this.onRefresh, this);
18791         view.on("rowupdated", this.onRowUpdated, this);
18792         view.on("rowremoved", this.onRemove, this);
18793     },
18794
18795     // private
18796     onRefresh : function(){
18797         var ds = this.grid.dataSource, i, v = this.grid.view;
18798         var s = this.selections;
18799         s.each(function(r){
18800             if((i = ds.indexOfId(r.id)) != -1){
18801                 v.onRowSelect(i);
18802             }else{
18803                 s.remove(r);
18804             }
18805         });
18806     },
18807
18808     // private
18809     onRemove : function(v, index, r){
18810         this.selections.remove(r);
18811     },
18812
18813     // private
18814     onRowUpdated : function(v, index, r){
18815         if(this.isSelected(r)){
18816             v.onRowSelect(index);
18817         }
18818     },
18819
18820     /**
18821      * Select records.
18822      * @param {Array} records The records to select
18823      * @param {Boolean} keepExisting (optional) True to keep existing selections
18824      */
18825     selectRecords : function(records, keepExisting){
18826         if(!keepExisting){
18827             this.clearSelections();
18828         }
18829         var ds = this.grid.dataSource;
18830         for(var i = 0, len = records.length; i < len; i++){
18831             this.selectRow(ds.indexOf(records[i]), true);
18832         }
18833     },
18834
18835     /**
18836      * Gets the number of selected rows.
18837      * @return {Number}
18838      */
18839     getCount : function(){
18840         return this.selections.length;
18841     },
18842
18843     /**
18844      * Selects the first row in the grid.
18845      */
18846     selectFirstRow : function(){
18847         this.selectRow(0);
18848     },
18849
18850     /**
18851      * Select the last row.
18852      * @param {Boolean} keepExisting (optional) True to keep existing selections
18853      */
18854     selectLastRow : function(keepExisting){
18855         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18856     },
18857
18858     /**
18859      * Selects the row immediately following the last selected row.
18860      * @param {Boolean} keepExisting (optional) True to keep existing selections
18861      */
18862     selectNext : function(keepExisting){
18863         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18864             this.selectRow(this.last+1, keepExisting);
18865             this.grid.getView().focusRow(this.last);
18866         }
18867     },
18868
18869     /**
18870      * Selects the row that precedes the last selected row.
18871      * @param {Boolean} keepExisting (optional) True to keep existing selections
18872      */
18873     selectPrevious : function(keepExisting){
18874         if(this.last){
18875             this.selectRow(this.last-1, keepExisting);
18876             this.grid.getView().focusRow(this.last);
18877         }
18878     },
18879
18880     /**
18881      * Returns the selected records
18882      * @return {Array} Array of selected records
18883      */
18884     getSelections : function(){
18885         return [].concat(this.selections.items);
18886     },
18887
18888     /**
18889      * Returns the first selected record.
18890      * @return {Record}
18891      */
18892     getSelected : function(){
18893         return this.selections.itemAt(0);
18894     },
18895
18896
18897     /**
18898      * Clears all selections.
18899      */
18900     clearSelections : function(fast){
18901         if(this.locked) return;
18902         if(fast !== true){
18903             var ds = this.grid.dataSource;
18904             var s = this.selections;
18905             s.each(function(r){
18906                 this.deselectRow(ds.indexOfId(r.id));
18907             }, this);
18908             s.clear();
18909         }else{
18910             this.selections.clear();
18911         }
18912         this.last = false;
18913     },
18914
18915
18916     /**
18917      * Selects all rows.
18918      */
18919     selectAll : function(){
18920         if(this.locked) return;
18921         this.selections.clear();
18922         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18923             this.selectRow(i, true);
18924         }
18925     },
18926
18927     /**
18928      * Returns True if there is a selection.
18929      * @return {Boolean}
18930      */
18931     hasSelection : function(){
18932         return this.selections.length > 0;
18933     },
18934
18935     /**
18936      * Returns True if the specified row is selected.
18937      * @param {Number/Record} record The record or index of the record to check
18938      * @return {Boolean}
18939      */
18940     isSelected : function(index){
18941         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18942         return (r && this.selections.key(r.id) ? true : false);
18943     },
18944
18945     /**
18946      * Returns True if the specified record id is selected.
18947      * @param {String} id The id of record to check
18948      * @return {Boolean}
18949      */
18950     isIdSelected : function(id){
18951         return (this.selections.key(id) ? true : false);
18952     },
18953
18954     // private
18955     handleMouseDown : function(e, t){
18956         var view = this.grid.getView(), rowIndex;
18957         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18958             return;
18959         };
18960         if(e.shiftKey && this.last !== false){
18961             var last = this.last;
18962             this.selectRange(last, rowIndex, e.ctrlKey);
18963             this.last = last; // reset the last
18964             view.focusRow(rowIndex);
18965         }else{
18966             var isSelected = this.isSelected(rowIndex);
18967             if(e.button !== 0 && isSelected){
18968                 view.focusRow(rowIndex);
18969             }else if(e.ctrlKey && isSelected){
18970                 this.deselectRow(rowIndex);
18971             }else if(!isSelected){
18972                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18973                 view.focusRow(rowIndex);
18974             }
18975         }
18976         this.fireEvent("afterselectionchange", this);
18977     },
18978     // private
18979     handleDragableRowClick :  function(grid, rowIndex, e) 
18980     {
18981         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18982             this.selectRow(rowIndex, false);
18983             grid.view.focusRow(rowIndex);
18984              this.fireEvent("afterselectionchange", this);
18985         }
18986     },
18987     
18988     /**
18989      * Selects multiple rows.
18990      * @param {Array} rows Array of the indexes of the row to select
18991      * @param {Boolean} keepExisting (optional) True to keep existing selections
18992      */
18993     selectRows : function(rows, keepExisting){
18994         if(!keepExisting){
18995             this.clearSelections();
18996         }
18997         for(var i = 0, len = rows.length; i < len; i++){
18998             this.selectRow(rows[i], true);
18999         }
19000     },
19001
19002     /**
19003      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19004      * @param {Number} startRow The index of the first row in the range
19005      * @param {Number} endRow The index of the last row in the range
19006      * @param {Boolean} keepExisting (optional) True to retain existing selections
19007      */
19008     selectRange : function(startRow, endRow, keepExisting){
19009         if(this.locked) return;
19010         if(!keepExisting){
19011             this.clearSelections();
19012         }
19013         if(startRow <= endRow){
19014             for(var i = startRow; i <= endRow; i++){
19015                 this.selectRow(i, true);
19016             }
19017         }else{
19018             for(var i = startRow; i >= endRow; i--){
19019                 this.selectRow(i, true);
19020             }
19021         }
19022     },
19023
19024     /**
19025      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19026      * @param {Number} startRow The index of the first row in the range
19027      * @param {Number} endRow The index of the last row in the range
19028      */
19029     deselectRange : function(startRow, endRow, preventViewNotify){
19030         if(this.locked) return;
19031         for(var i = startRow; i <= endRow; i++){
19032             this.deselectRow(i, preventViewNotify);
19033         }
19034     },
19035
19036     /**
19037      * Selects a row.
19038      * @param {Number} row The index of the row to select
19039      * @param {Boolean} keepExisting (optional) True to keep existing selections
19040      */
19041     selectRow : function(index, keepExisting, preventViewNotify){
19042         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19043         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19044             if(!keepExisting || this.singleSelect){
19045                 this.clearSelections();
19046             }
19047             var r = this.grid.dataSource.getAt(index);
19048             this.selections.add(r);
19049             this.last = this.lastActive = index;
19050             if(!preventViewNotify){
19051                 this.grid.getView().onRowSelect(index);
19052             }
19053             this.fireEvent("rowselect", this, index, r);
19054             this.fireEvent("selectionchange", this);
19055         }
19056     },
19057
19058     /**
19059      * Deselects a row.
19060      * @param {Number} row The index of the row to deselect
19061      */
19062     deselectRow : function(index, preventViewNotify){
19063         if(this.locked) return;
19064         if(this.last == index){
19065             this.last = false;
19066         }
19067         if(this.lastActive == index){
19068             this.lastActive = false;
19069         }
19070         var r = this.grid.dataSource.getAt(index);
19071         this.selections.remove(r);
19072         if(!preventViewNotify){
19073             this.grid.getView().onRowDeselect(index);
19074         }
19075         this.fireEvent("rowdeselect", this, index);
19076         this.fireEvent("selectionchange", this);
19077     },
19078
19079     // private
19080     restoreLast : function(){
19081         if(this._last){
19082             this.last = this._last;
19083         }
19084     },
19085
19086     // private
19087     acceptsNav : function(row, col, cm){
19088         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19089     },
19090
19091     // private
19092     onEditorKey : function(field, e){
19093         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19094         if(k == e.TAB){
19095             e.stopEvent();
19096             ed.completeEdit();
19097             if(e.shiftKey){
19098                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19099             }else{
19100                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19101             }
19102         }else if(k == e.ENTER && !e.ctrlKey){
19103             e.stopEvent();
19104             ed.completeEdit();
19105             if(e.shiftKey){
19106                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19107             }else{
19108                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19109             }
19110         }else if(k == e.ESC){
19111             ed.cancelEdit();
19112         }
19113         if(newCell){
19114             g.startEditing(newCell[0], newCell[1]);
19115         }
19116     }
19117 });/*
19118  * Based on:
19119  * Ext JS Library 1.1.1
19120  * Copyright(c) 2006-2007, Ext JS, LLC.
19121  *
19122  * Originally Released Under LGPL - original licence link has changed is not relivant.
19123  *
19124  * Fork - LGPL
19125  * <script type="text/javascript">
19126  */
19127  
19128 /**
19129  * @class Roo.bootstrap.PagingToolbar
19130  * @extends Roo.Row
19131  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19132  * @constructor
19133  * Create a new PagingToolbar
19134  * @param {Object} config The config object
19135  */
19136 Roo.bootstrap.PagingToolbar = function(config)
19137 {
19138     // old args format still supported... - xtype is prefered..
19139         // created from xtype...
19140     var ds = config.dataSource;
19141     this.toolbarItems = [];
19142     if (config.items) {
19143         this.toolbarItems = config.items;
19144 //        config.items = [];
19145     }
19146     
19147     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19148     this.ds = ds;
19149     this.cursor = 0;
19150     if (ds) { 
19151         this.bind(ds);
19152     }
19153     
19154     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19155     
19156 };
19157
19158 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19159     /**
19160      * @cfg {Roo.data.Store} dataSource
19161      * The underlying data store providing the paged data
19162      */
19163     /**
19164      * @cfg {String/HTMLElement/Element} container
19165      * container The id or element that will contain the toolbar
19166      */
19167     /**
19168      * @cfg {Boolean} displayInfo
19169      * True to display the displayMsg (defaults to false)
19170      */
19171     /**
19172      * @cfg {Number} pageSize
19173      * The number of records to display per page (defaults to 20)
19174      */
19175     pageSize: 20,
19176     /**
19177      * @cfg {String} displayMsg
19178      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19179      */
19180     displayMsg : 'Displaying {0} - {1} of {2}',
19181     /**
19182      * @cfg {String} emptyMsg
19183      * The message to display when no records are found (defaults to "No data to display")
19184      */
19185     emptyMsg : 'No data to display',
19186     /**
19187      * Customizable piece of the default paging text (defaults to "Page")
19188      * @type String
19189      */
19190     beforePageText : "Page",
19191     /**
19192      * Customizable piece of the default paging text (defaults to "of %0")
19193      * @type String
19194      */
19195     afterPageText : "of {0}",
19196     /**
19197      * Customizable piece of the default paging text (defaults to "First Page")
19198      * @type String
19199      */
19200     firstText : "First Page",
19201     /**
19202      * Customizable piece of the default paging text (defaults to "Previous Page")
19203      * @type String
19204      */
19205     prevText : "Previous Page",
19206     /**
19207      * Customizable piece of the default paging text (defaults to "Next Page")
19208      * @type String
19209      */
19210     nextText : "Next Page",
19211     /**
19212      * Customizable piece of the default paging text (defaults to "Last Page")
19213      * @type String
19214      */
19215     lastText : "Last Page",
19216     /**
19217      * Customizable piece of the default paging text (defaults to "Refresh")
19218      * @type String
19219      */
19220     refreshText : "Refresh",
19221
19222     buttons : false,
19223     // private
19224     onRender : function(ct, position) 
19225     {
19226         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19227         this.navgroup.parentId = this.id;
19228         this.navgroup.onRender(this.el, null);
19229         // add the buttons to the navgroup
19230         
19231         if(this.displayInfo){
19232             Roo.log(this.el.select('ul.navbar-nav',true).first());
19233             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19234             this.displayEl = this.el.select('.x-paging-info', true).first();
19235 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19236 //            this.displayEl = navel.el.select('span',true).first();
19237         }
19238         
19239         var _this = this;
19240         
19241         if(this.buttons){
19242             Roo.each(_this.buttons, function(e){
19243                Roo.factory(e).onRender(_this.el, null);
19244             });
19245         }
19246             
19247         Roo.each(_this.toolbarItems, function(e) {
19248             _this.navgroup.addItem(e);
19249         });
19250         
19251         this.first = this.navgroup.addItem({
19252             tooltip: this.firstText,
19253             cls: "prev",
19254             icon : 'fa fa-backward',
19255             disabled: true,
19256             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19257         });
19258         
19259         this.prev =  this.navgroup.addItem({
19260             tooltip: this.prevText,
19261             cls: "prev",
19262             icon : 'fa fa-step-backward',
19263             disabled: true,
19264             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19265         });
19266     //this.addSeparator();
19267         
19268         
19269         var field = this.navgroup.addItem( {
19270             tagtype : 'span',
19271             cls : 'x-paging-position',
19272             
19273             html : this.beforePageText  +
19274                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19275                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19276          } ); //?? escaped?
19277         
19278         this.field = field.el.select('input', true).first();
19279         this.field.on("keydown", this.onPagingKeydown, this);
19280         this.field.on("focus", function(){this.dom.select();});
19281     
19282     
19283         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19284         //this.field.setHeight(18);
19285         //this.addSeparator();
19286         this.next = this.navgroup.addItem({
19287             tooltip: this.nextText,
19288             cls: "next",
19289             html : ' <i class="fa fa-step-forward">',
19290             disabled: true,
19291             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19292         });
19293         this.last = this.navgroup.addItem({
19294             tooltip: this.lastText,
19295             icon : 'fa fa-forward',
19296             cls: "next",
19297             disabled: true,
19298             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19299         });
19300     //this.addSeparator();
19301         this.loading = this.navgroup.addItem({
19302             tooltip: this.refreshText,
19303             icon: 'fa fa-refresh',
19304             
19305             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19306         });
19307
19308     },
19309
19310     // private
19311     updateInfo : function(){
19312         if(this.displayEl){
19313             var count = this.ds.getCount();
19314             var msg = count == 0 ?
19315                 this.emptyMsg :
19316                 String.format(
19317                     this.displayMsg,
19318                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19319                 );
19320             this.displayEl.update(msg);
19321         }
19322     },
19323
19324     // private
19325     onLoad : function(ds, r, o){
19326        this.cursor = o.params ? o.params.start : 0;
19327        var d = this.getPageData(),
19328             ap = d.activePage,
19329             ps = d.pages;
19330         
19331        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19332        this.field.dom.value = ap;
19333        this.first.setDisabled(ap == 1);
19334        this.prev.setDisabled(ap == 1);
19335        this.next.setDisabled(ap == ps);
19336        this.last.setDisabled(ap == ps);
19337        this.loading.enable();
19338        this.updateInfo();
19339     },
19340
19341     // private
19342     getPageData : function(){
19343         var total = this.ds.getTotalCount();
19344         return {
19345             total : total,
19346             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19347             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19348         };
19349     },
19350
19351     // private
19352     onLoadError : function(){
19353         this.loading.enable();
19354     },
19355
19356     // private
19357     onPagingKeydown : function(e){
19358         var k = e.getKey();
19359         var d = this.getPageData();
19360         if(k == e.RETURN){
19361             var v = this.field.dom.value, pageNum;
19362             if(!v || isNaN(pageNum = parseInt(v, 10))){
19363                 this.field.dom.value = d.activePage;
19364                 return;
19365             }
19366             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19367             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19368             e.stopEvent();
19369         }
19370         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))
19371         {
19372           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19373           this.field.dom.value = pageNum;
19374           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19375           e.stopEvent();
19376         }
19377         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19378         {
19379           var v = this.field.dom.value, pageNum; 
19380           var increment = (e.shiftKey) ? 10 : 1;
19381           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19382             increment *= -1;
19383           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19384             this.field.dom.value = d.activePage;
19385             return;
19386           }
19387           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19388           {
19389             this.field.dom.value = parseInt(v, 10) + increment;
19390             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19391             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19392           }
19393           e.stopEvent();
19394         }
19395     },
19396
19397     // private
19398     beforeLoad : function(){
19399         if(this.loading){
19400             this.loading.disable();
19401         }
19402     },
19403
19404     // private
19405     onClick : function(which){
19406         var ds = this.ds;
19407         if (!ds) {
19408             return;
19409         }
19410         switch(which){
19411             case "first":
19412                 ds.load({params:{start: 0, limit: this.pageSize}});
19413             break;
19414             case "prev":
19415                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19416             break;
19417             case "next":
19418                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19419             break;
19420             case "last":
19421                 var total = ds.getTotalCount();
19422                 var extra = total % this.pageSize;
19423                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19424                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19425             break;
19426             case "refresh":
19427                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19428             break;
19429         }
19430     },
19431
19432     /**
19433      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19434      * @param {Roo.data.Store} store The data store to unbind
19435      */
19436     unbind : function(ds){
19437         ds.un("beforeload", this.beforeLoad, this);
19438         ds.un("load", this.onLoad, this);
19439         ds.un("loadexception", this.onLoadError, this);
19440         ds.un("remove", this.updateInfo, this);
19441         ds.un("add", this.updateInfo, this);
19442         this.ds = undefined;
19443     },
19444
19445     /**
19446      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19447      * @param {Roo.data.Store} store The data store to bind
19448      */
19449     bind : function(ds){
19450         ds.on("beforeload", this.beforeLoad, this);
19451         ds.on("load", this.onLoad, this);
19452         ds.on("loadexception", this.onLoadError, this);
19453         ds.on("remove", this.updateInfo, this);
19454         ds.on("add", this.updateInfo, this);
19455         this.ds = ds;
19456     }
19457 });/*
19458  * - LGPL
19459  *
19460  * element
19461  * 
19462  */
19463
19464 /**
19465  * @class Roo.bootstrap.MessageBar
19466  * @extends Roo.bootstrap.Component
19467  * Bootstrap MessageBar class
19468  * @cfg {String} html contents of the MessageBar
19469  * @cfg {String} weight (info | success | warning | danger) default info
19470  * @cfg {String} beforeClass insert the bar before the given class
19471  * @cfg {Boolean} closable (true | false) default false
19472  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19473  * 
19474  * @constructor
19475  * Create a new Element
19476  * @param {Object} config The config object
19477  */
19478
19479 Roo.bootstrap.MessageBar = function(config){
19480     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19481 };
19482
19483 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19484     
19485     html: '',
19486     weight: 'info',
19487     closable: false,
19488     fixed: false,
19489     beforeClass: 'bootstrap-sticky-wrap',
19490     
19491     getAutoCreate : function(){
19492         
19493         var cfg = {
19494             tag: 'div',
19495             cls: 'alert alert-dismissable alert-' + this.weight,
19496             cn: [
19497                 {
19498                     tag: 'span',
19499                     cls: 'message',
19500                     html: this.html || ''
19501                 }
19502             ]
19503         }
19504         
19505         if(this.fixed){
19506             cfg.cls += ' alert-messages-fixed';
19507         }
19508         
19509         if(this.closable){
19510             cfg.cn.push({
19511                 tag: 'button',
19512                 cls: 'close',
19513                 html: 'x'
19514             });
19515         }
19516         
19517         return cfg;
19518     },
19519     
19520     onRender : function(ct, position)
19521     {
19522         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19523         
19524         if(!this.el){
19525             var cfg = Roo.apply({},  this.getAutoCreate());
19526             cfg.id = Roo.id();
19527             
19528             if (this.cls) {
19529                 cfg.cls += ' ' + this.cls;
19530             }
19531             if (this.style) {
19532                 cfg.style = this.style;
19533             }
19534             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19535             
19536             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19537         }
19538         
19539         this.el.select('>button.close').on('click', this.hide, this);
19540         
19541     },
19542     
19543     show : function()
19544     {
19545         if (!this.rendered) {
19546             this.render();
19547         }
19548         
19549         this.el.show();
19550         
19551         this.fireEvent('show', this);
19552         
19553     },
19554     
19555     hide : function()
19556     {
19557         if (!this.rendered) {
19558             this.render();
19559         }
19560         
19561         this.el.hide();
19562         
19563         this.fireEvent('hide', this);
19564     },
19565     
19566     update : function()
19567     {
19568 //        var e = this.el.dom.firstChild;
19569 //        
19570 //        if(this.closable){
19571 //            e = e.nextSibling;
19572 //        }
19573 //        
19574 //        e.data = this.html || '';
19575
19576         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19577     }
19578    
19579 });
19580
19581  
19582
19583      /*
19584  * - LGPL
19585  *
19586  * Graph
19587  * 
19588  */
19589
19590
19591 /**
19592  * @class Roo.bootstrap.Graph
19593  * @extends Roo.bootstrap.Component
19594  * Bootstrap Graph class
19595 > Prameters
19596  -sm {number} sm 4
19597  -md {number} md 5
19598  @cfg {String} graphtype  bar | vbar | pie
19599  @cfg {number} g_x coodinator | centre x (pie)
19600  @cfg {number} g_y coodinator | centre y (pie)
19601  @cfg {number} g_r radius (pie)
19602  @cfg {number} g_height height of the chart (respected by all elements in the set)
19603  @cfg {number} g_width width of the chart (respected by all elements in the set)
19604  @cfg {Object} title The title of the chart
19605     
19606  -{Array}  values
19607  -opts (object) options for the chart 
19608      o {
19609      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19610      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19611      o vgutter (number)
19612      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.
19613      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19614      o to
19615      o stretch (boolean)
19616      o }
19617  -opts (object) options for the pie
19618      o{
19619      o cut
19620      o startAngle (number)
19621      o endAngle (number)
19622      } 
19623  *
19624  * @constructor
19625  * Create a new Input
19626  * @param {Object} config The config object
19627  */
19628
19629 Roo.bootstrap.Graph = function(config){
19630     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19631     
19632     this.addEvents({
19633         // img events
19634         /**
19635          * @event click
19636          * The img click event for the img.
19637          * @param {Roo.EventObject} e
19638          */
19639         "click" : true
19640     });
19641 };
19642
19643 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19644     
19645     sm: 4,
19646     md: 5,
19647     graphtype: 'bar',
19648     g_height: 250,
19649     g_width: 400,
19650     g_x: 50,
19651     g_y: 50,
19652     g_r: 30,
19653     opts:{
19654         //g_colors: this.colors,
19655         g_type: 'soft',
19656         g_gutter: '20%'
19657
19658     },
19659     title : false,
19660
19661     getAutoCreate : function(){
19662         
19663         var cfg = {
19664             tag: 'div',
19665             html : null
19666         }
19667         
19668         
19669         return  cfg;
19670     },
19671
19672     onRender : function(ct,position){
19673         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19674         this.raphael = Raphael(this.el.dom);
19675         
19676                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19677                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19678                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19679                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19680                 /*
19681                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19682                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19683                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19684                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19685                 
19686                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19687                 r.barchart(330, 10, 300, 220, data1);
19688                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19689                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19690                 */
19691                 
19692                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19693                 // r.barchart(30, 30, 560, 250,  xdata, {
19694                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19695                 //     axis : "0 0 1 1",
19696                 //     axisxlabels :  xdata
19697                 //     //yvalues : cols,
19698                    
19699                 // });
19700 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19701 //        
19702 //        this.load(null,xdata,{
19703 //                axis : "0 0 1 1",
19704 //                axisxlabels :  xdata
19705 //                });
19706
19707     },
19708
19709     load : function(graphtype,xdata,opts){
19710         this.raphael.clear();
19711         if(!graphtype) {
19712             graphtype = this.graphtype;
19713         }
19714         if(!opts){
19715             opts = this.opts;
19716         }
19717         var r = this.raphael,
19718             fin = function () {
19719                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19720             },
19721             fout = function () {
19722                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19723             },
19724             pfin = function() {
19725                 this.sector.stop();
19726                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19727
19728                 if (this.label) {
19729                     this.label[0].stop();
19730                     this.label[0].attr({ r: 7.5 });
19731                     this.label[1].attr({ "font-weight": 800 });
19732                 }
19733             },
19734             pfout = function() {
19735                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19736
19737                 if (this.label) {
19738                     this.label[0].animate({ r: 5 }, 500, "bounce");
19739                     this.label[1].attr({ "font-weight": 400 });
19740                 }
19741             };
19742
19743         switch(graphtype){
19744             case 'bar':
19745                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19746                 break;
19747             case 'hbar':
19748                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19749                 break;
19750             case 'pie':
19751 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19752 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19753 //            
19754                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19755                 
19756                 break;
19757
19758         }
19759         
19760         if(this.title){
19761             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19762         }
19763         
19764     },
19765     
19766     setTitle: function(o)
19767     {
19768         this.title = o;
19769     },
19770     
19771     initEvents: function() {
19772         
19773         if(!this.href){
19774             this.el.on('click', this.onClick, this);
19775         }
19776     },
19777     
19778     onClick : function(e)
19779     {
19780         Roo.log('img onclick');
19781         this.fireEvent('click', this, e);
19782     }
19783    
19784 });
19785
19786  
19787 /*
19788  * - LGPL
19789  *
19790  * numberBox
19791  * 
19792  */
19793 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19794
19795 /**
19796  * @class Roo.bootstrap.dash.NumberBox
19797  * @extends Roo.bootstrap.Component
19798  * Bootstrap NumberBox class
19799  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19800  * @cfg {String} headline Box headline
19801  * @cfg {String} content Box content
19802  * @cfg {String} icon Box icon
19803  * @cfg {String} footer Footer text
19804  * @cfg {String} fhref Footer href
19805  * 
19806  * @constructor
19807  * Create a new NumberBox
19808  * @param {Object} config The config object
19809  */
19810
19811
19812 Roo.bootstrap.dash.NumberBox = function(config){
19813     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19814     
19815 };
19816
19817 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19818     
19819     bgcolor : 'aqua',
19820     headline : '',
19821     content : '',
19822     icon : '',
19823     footer : '',
19824     fhref : '',
19825     ficon : '',
19826     
19827     getAutoCreate : function(){
19828         
19829         var cfg = {
19830             tag : 'div',
19831             cls : 'small-box bg-' + this.bgcolor,
19832             cn : [
19833                 {
19834                     tag : 'div',
19835                     cls : 'inner',
19836                     cn :[
19837                         {
19838                             tag : 'h3',
19839                             cls : 'roo-headline',
19840                             html : this.headline
19841                         },
19842                         {
19843                             tag : 'p',
19844                             cls : 'roo-content',
19845                             html : this.content
19846                         }
19847                     ]
19848                 }
19849             ]
19850         }
19851         
19852         if(this.icon){
19853             cfg.cn.push({
19854                 tag : 'div',
19855                 cls : 'icon',
19856                 cn :[
19857                     {
19858                         tag : 'i',
19859                         cls : 'ion ' + this.icon
19860                     }
19861                 ]
19862             });
19863         }
19864         
19865         if(this.footer){
19866             var footer = {
19867                 tag : 'a',
19868                 cls : 'small-box-footer',
19869                 href : this.fhref || '#',
19870                 html : this.footer
19871             };
19872             
19873             cfg.cn.push(footer);
19874             
19875         }
19876         
19877         return  cfg;
19878     },
19879
19880     onRender : function(ct,position){
19881         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19882
19883
19884        
19885                 
19886     },
19887
19888     setHeadline: function (value)
19889     {
19890         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19891     },
19892     
19893     setFooter: function (value, href)
19894     {
19895         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19896         
19897         if(href){
19898             this.el.select('a.small-box-footer',true).first().attr('href', href);
19899         }
19900         
19901     },
19902
19903     setContent: function (value)
19904     {
19905         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19906     },
19907
19908     initEvents: function() 
19909     {   
19910         
19911     }
19912     
19913 });
19914
19915  
19916 /*
19917  * - LGPL
19918  *
19919  * TabBox
19920  * 
19921  */
19922 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19923
19924 /**
19925  * @class Roo.bootstrap.dash.TabBox
19926  * @extends Roo.bootstrap.Component
19927  * Bootstrap TabBox class
19928  * @cfg {String} title Title of the TabBox
19929  * @cfg {String} icon Icon of the TabBox
19930  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19931  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19932  * 
19933  * @constructor
19934  * Create a new TabBox
19935  * @param {Object} config The config object
19936  */
19937
19938
19939 Roo.bootstrap.dash.TabBox = function(config){
19940     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19941     this.addEvents({
19942         // raw events
19943         /**
19944          * @event addpane
19945          * When a pane is added
19946          * @param {Roo.bootstrap.dash.TabPane} pane
19947          */
19948         "addpane" : true,
19949         /**
19950          * @event activatepane
19951          * When a pane is activated
19952          * @param {Roo.bootstrap.dash.TabPane} pane
19953          */
19954         "activatepane" : true
19955         
19956          
19957     });
19958     
19959     this.panes = [];
19960 };
19961
19962 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19963
19964     title : '',
19965     icon : false,
19966     showtabs : true,
19967     tabScrollable : false,
19968     
19969     getChildContainer : function()
19970     {
19971         return this.el.select('.tab-content', true).first();
19972     },
19973     
19974     getAutoCreate : function(){
19975         
19976         var header = {
19977             tag: 'li',
19978             cls: 'pull-left header',
19979             html: this.title,
19980             cn : []
19981         };
19982         
19983         if(this.icon){
19984             header.cn.push({
19985                 tag: 'i',
19986                 cls: 'fa ' + this.icon
19987             });
19988         }
19989         
19990         var h = {
19991             tag: 'ul',
19992             cls: 'nav nav-tabs pull-right',
19993             cn: [
19994                 header
19995             ]
19996         };
19997         
19998         if(this.tabScrollable){
19999             h = {
20000                 tag: 'div',
20001                 cls: 'tab-header',
20002                 cn: [
20003                     {
20004                         tag: 'ul',
20005                         cls: 'nav nav-tabs pull-right',
20006                         cn: [
20007                             header
20008                         ]
20009                     }
20010                 ]
20011             }
20012         }
20013         
20014         var cfg = {
20015             tag: 'div',
20016             cls: 'nav-tabs-custom',
20017             cn: [
20018                 h,
20019                 {
20020                     tag: 'div',
20021                     cls: 'tab-content no-padding',
20022                     cn: []
20023                 }
20024             ]
20025         }
20026
20027         return  cfg;
20028     },
20029     initEvents : function()
20030     {
20031         //Roo.log('add add pane handler');
20032         this.on('addpane', this.onAddPane, this);
20033     },
20034      /**
20035      * Updates the box title
20036      * @param {String} html to set the title to.
20037      */
20038     setTitle : function(value)
20039     {
20040         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20041     },
20042     onAddPane : function(pane)
20043     {
20044         this.panes.push(pane);
20045         //Roo.log('addpane');
20046         //Roo.log(pane);
20047         // tabs are rendere left to right..
20048         if(!this.showtabs){
20049             return;
20050         }
20051         
20052         var ctr = this.el.select('.nav-tabs', true).first();
20053          
20054          
20055         var existing = ctr.select('.nav-tab',true);
20056         var qty = existing.getCount();;
20057         
20058         
20059         var tab = ctr.createChild({
20060             tag : 'li',
20061             cls : 'nav-tab' + (qty ? '' : ' active'),
20062             cn : [
20063                 {
20064                     tag : 'a',
20065                     href:'#',
20066                     html : pane.title
20067                 }
20068             ]
20069         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20070         pane.tab = tab;
20071         
20072         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20073         if (!qty) {
20074             pane.el.addClass('active');
20075         }
20076         
20077                 
20078     },
20079     onTabClick : function(ev,un,ob,pane)
20080     {
20081         //Roo.log('tab - prev default');
20082         ev.preventDefault();
20083         
20084         
20085         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20086         pane.tab.addClass('active');
20087         //Roo.log(pane.title);
20088         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20089         // technically we should have a deactivate event.. but maybe add later.
20090         // and it should not de-activate the selected tab...
20091         this.fireEvent('activatepane', pane);
20092         pane.el.addClass('active');
20093         pane.fireEvent('activate');
20094         
20095         
20096     },
20097     
20098     getActivePane : function()
20099     {
20100         var r = false;
20101         Roo.each(this.panes, function(p) {
20102             if(p.el.hasClass('active')){
20103                 r = p;
20104                 return false;
20105             }
20106             
20107             return;
20108         });
20109         
20110         return r;
20111     }
20112     
20113     
20114 });
20115
20116  
20117 /*
20118  * - LGPL
20119  *
20120  * Tab pane
20121  * 
20122  */
20123 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20124 /**
20125  * @class Roo.bootstrap.TabPane
20126  * @extends Roo.bootstrap.Component
20127  * Bootstrap TabPane class
20128  * @cfg {Boolean} active (false | true) Default false
20129  * @cfg {String} title title of panel
20130
20131  * 
20132  * @constructor
20133  * Create a new TabPane
20134  * @param {Object} config The config object
20135  */
20136
20137 Roo.bootstrap.dash.TabPane = function(config){
20138     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20139     
20140     this.addEvents({
20141         // raw events
20142         /**
20143          * @event activate
20144          * When a pane is activated
20145          * @param {Roo.bootstrap.dash.TabPane} pane
20146          */
20147         "activate" : true
20148          
20149     });
20150 };
20151
20152 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20153     
20154     active : false,
20155     title : '',
20156     
20157     // the tabBox that this is attached to.
20158     tab : false,
20159      
20160     getAutoCreate : function() 
20161     {
20162         var cfg = {
20163             tag: 'div',
20164             cls: 'tab-pane'
20165         }
20166         
20167         if(this.active){
20168             cfg.cls += ' active';
20169         }
20170         
20171         return cfg;
20172     },
20173     initEvents  : function()
20174     {
20175         //Roo.log('trigger add pane handler');
20176         this.parent().fireEvent('addpane', this)
20177     },
20178     
20179      /**
20180      * Updates the tab title 
20181      * @param {String} html to set the title to.
20182      */
20183     setTitle: function(str)
20184     {
20185         if (!this.tab) {
20186             return;
20187         }
20188         this.title = str;
20189         this.tab.select('a', true).first().dom.innerHTML = str;
20190         
20191     }
20192     
20193     
20194     
20195 });
20196
20197  
20198
20199
20200  /*
20201  * - LGPL
20202  *
20203  * menu
20204  * 
20205  */
20206 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20207
20208 /**
20209  * @class Roo.bootstrap.menu.Menu
20210  * @extends Roo.bootstrap.Component
20211  * Bootstrap Menu class - container for Menu
20212  * @cfg {String} html Text of the menu
20213  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20214  * @cfg {String} icon Font awesome icon
20215  * @cfg {String} pos Menu align to (top | bottom) default bottom
20216  * 
20217  * 
20218  * @constructor
20219  * Create a new Menu
20220  * @param {Object} config The config object
20221  */
20222
20223
20224 Roo.bootstrap.menu.Menu = function(config){
20225     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20226     
20227     this.addEvents({
20228         /**
20229          * @event beforeshow
20230          * Fires before this menu is displayed
20231          * @param {Roo.bootstrap.menu.Menu} this
20232          */
20233         beforeshow : true,
20234         /**
20235          * @event beforehide
20236          * Fires before this menu is hidden
20237          * @param {Roo.bootstrap.menu.Menu} this
20238          */
20239         beforehide : true,
20240         /**
20241          * @event show
20242          * Fires after this menu is displayed
20243          * @param {Roo.bootstrap.menu.Menu} this
20244          */
20245         show : true,
20246         /**
20247          * @event hide
20248          * Fires after this menu is hidden
20249          * @param {Roo.bootstrap.menu.Menu} this
20250          */
20251         hide : true,
20252         /**
20253          * @event click
20254          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20255          * @param {Roo.bootstrap.menu.Menu} this
20256          * @param {Roo.EventObject} e
20257          */
20258         click : true
20259     });
20260     
20261 };
20262
20263 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20264     
20265     submenu : false,
20266     html : '',
20267     weight : 'default',
20268     icon : false,
20269     pos : 'bottom',
20270     
20271     
20272     getChildContainer : function() {
20273         if(this.isSubMenu){
20274             return this.el;
20275         }
20276         
20277         return this.el.select('ul.dropdown-menu', true).first();  
20278     },
20279     
20280     getAutoCreate : function()
20281     {
20282         var text = [
20283             {
20284                 tag : 'span',
20285                 cls : 'roo-menu-text',
20286                 html : this.html
20287             }
20288         ];
20289         
20290         if(this.icon){
20291             text.unshift({
20292                 tag : 'i',
20293                 cls : 'fa ' + this.icon
20294             })
20295         }
20296         
20297         
20298         var cfg = {
20299             tag : 'div',
20300             cls : 'btn-group',
20301             cn : [
20302                 {
20303                     tag : 'button',
20304                     cls : 'dropdown-button btn btn-' + this.weight,
20305                     cn : text
20306                 },
20307                 {
20308                     tag : 'button',
20309                     cls : 'dropdown-toggle btn btn-' + this.weight,
20310                     cn : [
20311                         {
20312                             tag : 'span',
20313                             cls : 'caret'
20314                         }
20315                     ]
20316                 },
20317                 {
20318                     tag : 'ul',
20319                     cls : 'dropdown-menu'
20320                 }
20321             ]
20322             
20323         };
20324         
20325         if(this.pos == 'top'){
20326             cfg.cls += ' dropup';
20327         }
20328         
20329         if(this.isSubMenu){
20330             cfg = {
20331                 tag : 'ul',
20332                 cls : 'dropdown-menu'
20333             }
20334         }
20335         
20336         return cfg;
20337     },
20338     
20339     onRender : function(ct, position)
20340     {
20341         this.isSubMenu = ct.hasClass('dropdown-submenu');
20342         
20343         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20344     },
20345     
20346     initEvents : function() 
20347     {
20348         if(this.isSubMenu){
20349             return;
20350         }
20351         
20352         this.hidden = true;
20353         
20354         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20355         this.triggerEl.on('click', this.onTriggerPress, this);
20356         
20357         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20358         this.buttonEl.on('click', this.onClick, this);
20359         
20360     },
20361     
20362     list : function()
20363     {
20364         if(this.isSubMenu){
20365             return this.el;
20366         }
20367         
20368         return this.el.select('ul.dropdown-menu', true).first();
20369     },
20370     
20371     onClick : function(e)
20372     {
20373         this.fireEvent("click", this, e);
20374     },
20375     
20376     onTriggerPress  : function(e)
20377     {   
20378         if (this.isVisible()) {
20379             this.hide();
20380         } else {
20381             this.show();
20382         }
20383     },
20384     
20385     isVisible : function(){
20386         return !this.hidden;
20387     },
20388     
20389     show : function()
20390     {
20391         this.fireEvent("beforeshow", this);
20392         
20393         this.hidden = false;
20394         this.el.addClass('open');
20395         
20396         Roo.get(document).on("mouseup", this.onMouseUp, this);
20397         
20398         this.fireEvent("show", this);
20399         
20400         
20401     },
20402     
20403     hide : function()
20404     {
20405         this.fireEvent("beforehide", this);
20406         
20407         this.hidden = true;
20408         this.el.removeClass('open');
20409         
20410         Roo.get(document).un("mouseup", this.onMouseUp);
20411         
20412         this.fireEvent("hide", this);
20413     },
20414     
20415     onMouseUp : function()
20416     {
20417         this.hide();
20418     }
20419     
20420 });
20421
20422  
20423  /*
20424  * - LGPL
20425  *
20426  * menu item
20427  * 
20428  */
20429 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20430
20431 /**
20432  * @class Roo.bootstrap.menu.Item
20433  * @extends Roo.bootstrap.Component
20434  * Bootstrap MenuItem class
20435  * @cfg {Boolean} submenu (true | false) default false
20436  * @cfg {String} html text of the item
20437  * @cfg {String} href the link
20438  * @cfg {Boolean} disable (true | false) default false
20439  * @cfg {Boolean} preventDefault (true | false) default true
20440  * @cfg {String} icon Font awesome icon
20441  * @cfg {String} pos Submenu align to (left | right) default right 
20442  * 
20443  * 
20444  * @constructor
20445  * Create a new Item
20446  * @param {Object} config The config object
20447  */
20448
20449
20450 Roo.bootstrap.menu.Item = function(config){
20451     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20452     this.addEvents({
20453         /**
20454          * @event mouseover
20455          * Fires when the mouse is hovering over this menu
20456          * @param {Roo.bootstrap.menu.Item} this
20457          * @param {Roo.EventObject} e
20458          */
20459         mouseover : true,
20460         /**
20461          * @event mouseout
20462          * Fires when the mouse exits this menu
20463          * @param {Roo.bootstrap.menu.Item} this
20464          * @param {Roo.EventObject} e
20465          */
20466         mouseout : true,
20467         // raw events
20468         /**
20469          * @event click
20470          * The raw click event for the entire grid.
20471          * @param {Roo.EventObject} e
20472          */
20473         click : true
20474     });
20475 };
20476
20477 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20478     
20479     submenu : false,
20480     href : '',
20481     html : '',
20482     preventDefault: true,
20483     disable : false,
20484     icon : false,
20485     pos : 'right',
20486     
20487     getAutoCreate : function()
20488     {
20489         var text = [
20490             {
20491                 tag : 'span',
20492                 cls : 'roo-menu-item-text',
20493                 html : this.html
20494             }
20495         ];
20496         
20497         if(this.icon){
20498             text.unshift({
20499                 tag : 'i',
20500                 cls : 'fa ' + this.icon
20501             })
20502         }
20503         
20504         var cfg = {
20505             tag : 'li',
20506             cn : [
20507                 {
20508                     tag : 'a',
20509                     href : this.href || '#',
20510                     cn : text
20511                 }
20512             ]
20513         };
20514         
20515         if(this.disable){
20516             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20517         }
20518         
20519         if(this.submenu){
20520             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20521             
20522             if(this.pos == 'left'){
20523                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20524             }
20525         }
20526         
20527         return cfg;
20528     },
20529     
20530     initEvents : function() 
20531     {
20532         this.el.on('mouseover', this.onMouseOver, this);
20533         this.el.on('mouseout', this.onMouseOut, this);
20534         
20535         this.el.select('a', true).first().on('click', this.onClick, this);
20536         
20537     },
20538     
20539     onClick : function(e)
20540     {
20541         if(this.preventDefault){
20542             e.preventDefault();
20543         }
20544         
20545         this.fireEvent("click", this, e);
20546     },
20547     
20548     onMouseOver : function(e)
20549     {
20550         if(this.submenu && this.pos == 'left'){
20551             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20552         }
20553         
20554         this.fireEvent("mouseover", this, e);
20555     },
20556     
20557     onMouseOut : function(e)
20558     {
20559         this.fireEvent("mouseout", this, e);
20560     }
20561 });
20562
20563  
20564
20565  /*
20566  * - LGPL
20567  *
20568  * menu separator
20569  * 
20570  */
20571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20572
20573 /**
20574  * @class Roo.bootstrap.menu.Separator
20575  * @extends Roo.bootstrap.Component
20576  * Bootstrap Separator class
20577  * 
20578  * @constructor
20579  * Create a new Separator
20580  * @param {Object} config The config object
20581  */
20582
20583
20584 Roo.bootstrap.menu.Separator = function(config){
20585     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20586 };
20587
20588 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20589     
20590     getAutoCreate : function(){
20591         var cfg = {
20592             tag : 'li',
20593             cls: 'divider'
20594         };
20595         
20596         return cfg;
20597     }
20598    
20599 });
20600
20601  
20602
20603  /*
20604  * - LGPL
20605  *
20606  * Tooltip
20607  * 
20608  */
20609
20610 /**
20611  * @class Roo.bootstrap.Tooltip
20612  * Bootstrap Tooltip class
20613  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20614  * to determine which dom element triggers the tooltip.
20615  * 
20616  * It needs to add support for additional attributes like tooltip-position
20617  * 
20618  * @constructor
20619  * Create a new Toolti
20620  * @param {Object} config The config object
20621  */
20622
20623 Roo.bootstrap.Tooltip = function(config){
20624     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20625 };
20626
20627 Roo.apply(Roo.bootstrap.Tooltip, {
20628     /**
20629      * @function init initialize tooltip monitoring.
20630      * @static
20631      */
20632     currentEl : false,
20633     currentTip : false,
20634     currentRegion : false,
20635     
20636     //  init : delay?
20637     
20638     init : function()
20639     {
20640         Roo.get(document).on('mouseover', this.enter ,this);
20641         Roo.get(document).on('mouseout', this.leave, this);
20642          
20643         
20644         this.currentTip = new Roo.bootstrap.Tooltip();
20645     },
20646     
20647     enter : function(ev)
20648     {
20649         var dom = ev.getTarget();
20650         //Roo.log(['enter',dom]);
20651         var el = Roo.fly(dom);
20652         if (this.currentEl) {
20653             //Roo.log(dom);
20654             //Roo.log(this.currentEl);
20655             //Roo.log(this.currentEl.contains(dom));
20656             if (this.currentEl == el) {
20657                 return;
20658             }
20659             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20660                 return;
20661             }
20662
20663         }
20664         
20665         
20666         
20667         if (this.currentTip.el) {
20668             this.currentTip.el.hide(); // force hiding...
20669         }    
20670         //Roo.log(el);
20671         if (!el.attr('tooltip')) { // parents who have tip?
20672             return;
20673         }
20674         this.currentEl = el;
20675         this.currentTip.bind(el);
20676         this.currentRegion = Roo.lib.Region.getRegion(dom);
20677         this.currentTip.enter();
20678         
20679     },
20680     leave : function(ev)
20681     {
20682         var dom = ev.getTarget();
20683         //Roo.log(['leave',dom]);
20684         if (!this.currentEl) {
20685             return;
20686         }
20687         
20688         
20689         if (dom != this.currentEl.dom) {
20690             return;
20691         }
20692         var xy = ev.getXY();
20693         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
20694             return;
20695         }
20696         // only activate leave if mouse cursor is outside... bounding box..
20697         
20698         
20699         
20700         
20701         if (this.currentTip) {
20702             this.currentTip.leave();
20703         }
20704         //Roo.log('clear currentEl');
20705         this.currentEl = false;
20706         
20707         
20708     },
20709     alignment : {
20710         'left' : ['r-l', [-2,0], 'right'],
20711         'right' : ['l-r', [2,0], 'left'],
20712         'bottom' : ['t-b', [0,2], 'top'],
20713         'top' : [ 'b-t', [0,-2], 'bottom']
20714     }
20715     
20716 });
20717
20718
20719 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
20720     
20721     
20722     bindEl : false,
20723     
20724     delay : null, // can be { show : 300 , hide: 500}
20725     
20726     timeout : null,
20727     
20728     hoverState : null, //???
20729     
20730     placement : 'bottom', 
20731     
20732     getAutoCreate : function(){
20733     
20734         var cfg = {
20735            cls : 'tooltip',
20736            role : 'tooltip',
20737            cn : [
20738                 {
20739                     cls : 'tooltip-arrow'
20740                 },
20741                 {
20742                     cls : 'tooltip-inner'
20743                 }
20744            ]
20745         };
20746         
20747         return cfg;
20748     },
20749     bind : function(el)
20750     {
20751         this.bindEl = el;
20752     },
20753       
20754     
20755     enter : function () {
20756        
20757         if (this.timeout != null) {
20758             clearTimeout(this.timeout);
20759         }
20760         
20761         this.hoverState = 'in'
20762          //Roo.log("enter - show");
20763         if (!this.delay || !this.delay.show) {
20764             this.show();
20765             return;
20766         }
20767         var _t = this;
20768         this.timeout = setTimeout(function () {
20769             if (_t.hoverState == 'in') {
20770                 _t.show();
20771             }
20772         }, this.delay.show);
20773     },
20774     leave : function()
20775     {
20776         clearTimeout(this.timeout);
20777     
20778         this.hoverState = 'out'
20779          if (!this.delay || !this.delay.hide) {
20780             this.hide();
20781             return 
20782         }
20783        
20784         var _t = this;
20785         this.timeout = setTimeout(function () {
20786             //Roo.log("leave - timeout");
20787             
20788             if (_t.hoverState == 'out') {
20789                 _t.hide();
20790                 Roo.bootstrap.Tooltip.currentEl = false;
20791             }
20792         }, delay)
20793     },
20794     
20795     show : function ()
20796     {
20797         if (!this.el) {
20798             this.render(document.body);
20799         }
20800         // set content.
20801         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
20802         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
20803         
20804         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
20805         
20806         var placement = typeof this.placement == 'function' ?
20807             this.placement.call(this, this.el, on_el) :
20808             this.placement;
20809             
20810         var autoToken = /\s?auto?\s?/i;
20811         var autoPlace = autoToken.test(placement);
20812         if (autoPlace) {
20813             placement = placement.replace(autoToken, '') || 'top';
20814         }
20815         
20816         //this.el.detach()
20817         //this.el.setXY([0,0]);
20818         this.el.show();
20819         //this.el.dom.style.display='block';
20820         this.el.addClass(placement);
20821         
20822         //this.el.appendTo(on_el);
20823         
20824         var p = this.getPosition();
20825         var box = this.el.getBox();
20826         
20827         if (autoPlace) {
20828             // fixme..
20829         }
20830         var align = Roo.bootstrap.Tooltip.alignment[placement]
20831         this.el.alignTo(this.bindEl, align[0],align[1]);
20832         //var arrow = this.el.select('.arrow',true).first();
20833         //arrow.set(align[2], 
20834         
20835         this.el.addClass('in fade');
20836         this.hoverState = null;
20837         
20838         if (this.el.hasClass('fade')) {
20839             // fade it?
20840         }
20841         
20842     },
20843     hide : function()
20844     {
20845          
20846         if (!this.el) {
20847             return;
20848         }
20849         //this.el.setXY([0,0]);
20850         this.el.removeClass('in');
20851         //this.el.hide();
20852         
20853     }
20854     
20855 });
20856  
20857
20858