roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     tooltip : null,
43     /**
44      * Initialize Events for the element
45      */
46     initEvents : function()
47     {
48         Roo.log("------------ component init events ----------");
49         Roo.log(this.tooltip);
50         if (this.tooltip) {
51             this.getTooltipEl().attr('tooltip', this.tooltip);
52         }
53     },
54     
55     xattr : false,
56     
57     parentId : false,
58     
59     can_build_overlaid : true,
60     
61     dataId : false,
62     
63     name : false,
64     
65     parent: function() {
66         // returns the parent component..
67         return Roo.ComponentMgr.get(this.parentId)
68         
69         
70     },
71     
72     // private
73     onRender : function(ct, position)
74     {
75        // Roo.log("Call onRender: " + this.xtype);
76         
77         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
78         
79         if(this.el){
80             if (this.el.attr('xtype')) {
81                 this.el.attr('xtypex', this.el.attr('xtype'));
82                 this.el.dom.removeAttribute('xtype');
83                 
84                 this.initEvents();
85             }
86             
87             return;
88         }
89         
90          
91         
92         var cfg = Roo.apply({},  this.getAutoCreate());
93         cfg.id = Roo.id();
94         
95         // fill in the extra attributes 
96         if (this.xattr && typeof(this.xattr) =='object') {
97             for (var i in this.xattr) {
98                 cfg[i] = this.xattr[i];
99             }
100         }
101         
102         if(this.dataId){
103             cfg.dataId = this.dataId;
104         }
105         
106         if (this.cls) {
107             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
108         }
109         
110         if (this.style) { // fixme needs to support more complex style data.
111             cfg.style = this.style;
112         }
113         
114         if(this.name){
115             cfg.name = this.name;
116         }
117          
118         
119         this.el = ct.createChild(cfg, position);
120         
121         if(this.tabIndex !== undefined){
122             this.el.dom.setAttribute('tabIndex', this.tabIndex);
123         }
124         this.initEvents();
125         
126         
127     },
128     /**
129      * Fetch the element to add children to
130      * @return {Roo.Element} defaults to this.el
131      */
132     getChildContainer : function()
133     {
134         return this.el;
135     },
136     /**
137      * Fetch the element to display the tooltip on.
138      * @return {Roo.Element} defaults to this.el
139      */
140     getTooltipEl : function()
141     {
142         return this.el;
143     },
144         
145     addxtype  : function(tree,cntr)
146     {
147         var cn = this;
148         
149         cn = Roo.factory(tree);
150            
151         cn.parentType = this.xtype; //??
152         cn.parentId = this.id;
153         
154         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
155         
156         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
157         
158         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
159         
160         var build_from_html =  Roo.XComponent.build_from_html;
161           
162         var is_body  = (tree.xtype == 'Body') ;
163           
164         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
165           
166         var self_cntr_el = Roo.get(this[cntr](false));
167         
168         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
169             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
170                 return this.addxtypeChild(tree,cntr);
171             }
172             
173             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
174                 
175             if(echild){
176                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
177             }
178             
179             Roo.log('skipping render');
180             return cn;
181             
182         }
183         
184         var ret = false;
185         
186         while (true) {
187             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
188             
189             if (!echild) {
190                 break;
191             }
192             
193             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
194                 break;
195             }
196             
197             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
198         }
199         return ret;
200     },
201     
202     addxtypeChild : function (tree, cntr)
203     {
204         Roo.log('addxtypeChild:' + cntr);
205         var cn = this;
206         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
207         
208         
209         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
210                     (typeof(tree['flexy:foreach']) != 'undefined');
211           
212         
213         
214          skip_children = false;
215         // render the element if it's not BODY.
216         if (tree.xtype != 'Body') {
217            
218             cn = Roo.factory(tree);
219            
220             cn.parentType = this.xtype; //??
221             cn.parentId = this.id;
222             
223             var build_from_html =  Roo.XComponent.build_from_html;
224             
225             
226             // does the container contain child eleemnts with 'xtype' attributes.
227             // that match this xtype..
228             // note - when we render we create these as well..
229             // so we should check to see if body has xtype set.
230             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
231                
232                 var self_cntr_el = Roo.get(this[cntr](false));
233                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
234                 
235                 
236                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
237                 // and are not displayed -this causes this to use up the wrong element when matching.
238                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
239                 
240                 
241                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
242                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
243                   
244                   
245                   
246                     cn.el = echild;
247                   //  Roo.log("GOT");
248                     //echild.dom.removeAttribute('xtype');
249                 } else {
250                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
251                     Roo.log(self_cntr_el);
252                     Roo.log(echild);
253                     Roo.log(cn);
254                 }
255             }
256            
257             
258            
259             // if object has flexy:if - then it may or may not be rendered.
260             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
261                 // skip a flexy if element.
262                 Roo.log('skipping render');
263                 Roo.log(tree);
264                 if (!cn.el) {
265                     Roo.log('skipping all children');
266                     skip_children = true;
267                 }
268                 
269              } else {
270                  
271                 // actually if flexy:foreach is found, we really want to create 
272                 // multiple copies here...
273                 //Roo.log('render');
274                 //Roo.log(this[cntr]());
275                 cn.render(this[cntr](true));
276              }
277             // then add the element..
278         }
279         
280         
281         // handle the kids..
282         
283         var nitems = [];
284         /*
285         if (typeof (tree.menu) != 'undefined') {
286             tree.menu.parentType = cn.xtype;
287             tree.menu.triggerEl = cn.el;
288             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
289             
290         }
291         */
292         if (!tree.items || !tree.items.length) {
293             cn.items = nitems;
294             return cn;
295         }
296         var items = tree.items;
297         delete tree.items;
298         
299         //Roo.log(items.length);
300             // add the items..
301         if (!skip_children) {    
302             for(var i =0;i < items.length;i++) {
303                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
304             }
305         }
306         
307         cn.items = nitems;
308         
309         return cn;
310     }
311     
312     
313     
314     
315 });
316
317  /*
318  * - LGPL
319  *
320  * Body
321  * 
322  */
323
324 /**
325  * @class Roo.bootstrap.Body
326  * @extends Roo.bootstrap.Component
327  * Bootstrap Body class
328  * 
329  * @constructor
330  * Create a new body
331  * @param {Object} config The config object
332  */
333
334 Roo.bootstrap.Body = function(config){
335     Roo.bootstrap.Body.superclass.constructor.call(this, config);
336     this.el = Roo.get(document.body);
337     if (this.cls && this.cls.length) {
338         Roo.get(document.body).addClass(this.cls);
339     }
340 };
341
342 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
343       
344         autoCreate : {
345         cls: 'container'
346     },
347     onRender : function(ct, position)
348     {
349        /* Roo.log("Roo.bootstrap.Body - onRender");
350         if (this.cls && this.cls.length) {
351             Roo.get(document.body).addClass(this.cls);
352         }
353         // style??? xttr???
354         */
355     }
356     
357     
358  
359    
360 });
361
362  /*
363  * - LGPL
364  *
365  * button group
366  * 
367  */
368
369
370 /**
371  * @class Roo.bootstrap.ButtonGroup
372  * @extends Roo.bootstrap.Component
373  * Bootstrap ButtonGroup class
374  * @cfg {String} size lg | sm | xs (default empty normal)
375  * @cfg {String} align vertical | justified  (default none)
376  * @cfg {String} direction up | down (default down)
377  * @cfg {Boolean} toolbar false | true
378  * @cfg {Boolean} btn true | false
379  * 
380  * 
381  * @constructor
382  * Create a new Input
383  * @param {Object} config The config object
384  */
385
386 Roo.bootstrap.ButtonGroup = function(config){
387     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
388 };
389
390 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
391     
392     size: '',
393     align: '',
394     direction: '',
395     toolbar: false,
396     btn: true,
397
398     getAutoCreate : function(){
399         var cfg = {
400             cls: 'btn-group',
401             html : null
402         }
403         
404         cfg.html = this.html || cfg.html;
405         
406         if (this.toolbar) {
407             cfg = {
408                 cls: 'btn-toolbar',
409                 html: null
410             }
411             
412             return cfg;
413         }
414         
415         if (['vertical','justified'].indexOf(this.align)!==-1) {
416             cfg.cls = 'btn-group-' + this.align;
417             
418             if (this.align == 'justified') {
419                 console.log(this.items);
420             }
421         }
422         
423         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
424             cfg.cls += ' btn-group-' + this.size;
425         }
426         
427         if (this.direction == 'up') {
428             cfg.cls += ' dropup' ;
429         }
430         
431         return cfg;
432     }
433    
434 });
435
436  /*
437  * - LGPL
438  *
439  * button
440  * 
441  */
442
443 /**
444  * @class Roo.bootstrap.Button
445  * @extends Roo.bootstrap.Component
446  * Bootstrap Button class
447  * @cfg {String} html The button content
448  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
449  * @cfg {String} size empty | lg | sm | xs
450  * @cfg {String} tag empty | a | input | submit
451  * @cfg {String} href empty or href
452  * @cfg {Boolean} disabled false | true
453  * @cfg {Boolean} isClose false | true
454  * @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
455  * @cfg {String} badge text for badge
456  * @cfg {String} theme default (or empty) | glow
457  * @cfg {Boolean} inverse false | true
458  * @cfg {Boolean} toggle false | true
459  * @cfg {String} ontext text for on toggle state
460  * @cfg {String} offtext text for off toggle state
461  * @cfg {Boolean} defaulton true | false
462  * @cfg {Boolean} preventDefault (true | false) default true
463  * @cfg {Boolean} removeClass true | false remove the standard class..
464  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
465  * 
466  * @constructor
467  * Create a new button
468  * @param {Object} config The config object
469  */
470
471
472 Roo.bootstrap.Button = function(config){
473     Roo.bootstrap.Button.superclass.constructor.call(this, config);
474     this.addEvents({
475         // raw events
476         /**
477          * @event click
478          * When a butotn is pressed
479          * @param {Roo.EventObject} e
480          */
481         "click" : true,
482          /**
483          * @event toggle
484          * After the button has been toggles
485          * @param {Roo.EventObject} e
486          * @param {boolean} pressed (also available as button.pressed)
487          */
488         "toggle" : true
489     });
490 };
491
492 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
493     html: false,
494     active: false,
495     weight: '',
496     size: '',
497     tag: 'button',
498     href: '',
499     disabled: false,
500     isClose: false,
501     glyphicon: '',
502     badge: '',
503     theme: 'default',
504     inverse: false,
505     
506     toggle: false,
507     ontext: 'ON',
508     offtext: 'OFF',
509     defaulton: true,
510     preventDefault: true,
511     removeClass: false,
512     name: false,
513     target: false,
514     
515     
516     pressed : null,
517      
518     
519     getAutoCreate : function(){
520         
521         var cfg = {
522             tag : 'button',
523             cls : 'roo-button',
524             html: ''
525         };
526         
527         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
528             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
529             this.tag = 'button';
530         } else {
531             cfg.tag = this.tag;
532         }
533         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
534         
535         if (this.toggle == true) {
536             cfg={
537                 tag: 'div',
538                 cls: 'slider-frame roo-button',
539                 cn: [
540                     {
541                         tag: 'span',
542                         'data-on-text':'ON',
543                         'data-off-text':'OFF',
544                         cls: 'slider-button',
545                         html: this.offtext
546                     }
547                 ]
548             };
549             
550             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
551                 cfg.cls += ' '+this.weight;
552             }
553             
554             return cfg;
555         }
556         
557         if (this.isClose) {
558             cfg.cls += ' close';
559             
560             cfg["aria-hidden"] = true;
561             
562             cfg.html = "&times;";
563             
564             return cfg;
565         }
566         
567          
568         if (this.theme==='default') {
569             cfg.cls = 'btn roo-button';
570             
571             //if (this.parentType != 'Navbar') {
572             this.weight = this.weight.length ?  this.weight : 'default';
573             //}
574             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
575                 
576                 cfg.cls += ' btn-' + this.weight;
577             }
578         } else if (this.theme==='glow') {
579             
580             cfg.tag = 'a';
581             cfg.cls = 'btn-glow roo-button';
582             
583             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
584                 
585                 cfg.cls += ' ' + this.weight;
586             }
587         }
588    
589         
590         if (this.inverse) {
591             this.cls += ' inverse';
592         }
593         
594         
595         if (this.active) {
596             cfg.cls += ' active';
597         }
598         
599         if (this.disabled) {
600             cfg.disabled = 'disabled';
601         }
602         
603         if (this.items) {
604             Roo.log('changing to ul' );
605             cfg.tag = 'ul';
606             this.glyphicon = 'caret';
607         }
608         
609         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
610          
611         //gsRoo.log(this.parentType);
612         if (this.parentType === 'Navbar' && !this.parent().bar) {
613             Roo.log('changing to li?');
614             
615             cfg.tag = 'li';
616             
617             cfg.cls = '';
618             cfg.cn =  [{
619                 tag : 'a',
620                 cls : 'roo-button',
621                 html : this.html,
622                 href : this.href || '#'
623             }];
624             if (this.menu) {
625                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
626                 cfg.cls += ' dropdown';
627             }   
628             
629             delete cfg.html;
630             
631         }
632         
633        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
634         
635         if (this.glyphicon) {
636             cfg.html = ' ' + cfg.html;
637             
638             cfg.cn = [
639                 {
640                     tag: 'span',
641                     cls: 'glyphicon glyphicon-' + this.glyphicon
642                 }
643             ];
644         }
645         
646         if (this.badge) {
647             cfg.html += ' ';
648             
649             cfg.tag = 'a';
650             
651 //            cfg.cls='btn roo-button';
652             
653             cfg.href=this.href;
654             
655             var value = cfg.html;
656             
657             if(this.glyphicon){
658                 value = {
659                             tag: 'span',
660                             cls: 'glyphicon glyphicon-' + this.glyphicon,
661                             html: this.html
662                         };
663                 
664             }
665             
666             cfg.cn = [
667                 value,
668                 {
669                     tag: 'span',
670                     cls: 'badge',
671                     html: this.badge
672                 }
673             ];
674             
675             cfg.html='';
676         }
677         
678         if (this.menu) {
679             cfg.cls += ' dropdown';
680             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
681         }
682         
683         if (cfg.tag !== 'a' && this.href !== '') {
684             throw "Tag must be a to set href.";
685         } else if (this.href.length > 0) {
686             cfg.href = this.href;
687         }
688         
689         if(this.removeClass){
690             cfg.cls = '';
691         }
692         
693         if(this.target){
694             cfg.target = this.target;
695         }
696         
697         return cfg;
698     },
699     initEvents: function() {
700        // Roo.log('init events?');
701 //        Roo.log(this.el.dom);
702         // add the menu...
703         
704         if (typeof (this.menu) != 'undefined') {
705             this.menu.parentType = this.xtype;
706             this.menu.triggerEl = this.el;
707             this.addxtype(Roo.apply({}, this.menu));
708         }
709
710
711        if (this.el.hasClass('roo-button')) {
712             this.el.on('click', this.onClick, this);
713        } else {
714             this.el.select('.roo-button').on('click', this.onClick, this);
715        }
716        
717        if(this.removeClass){
718            this.el.on('click', this.onClick, this);
719        }
720        
721        this.el.enableDisplayMode();
722         
723     },
724     onClick : function(e)
725     {
726         if (this.disabled) {
727             return;
728         }
729         
730         Roo.log('button on click ');
731         if(this.preventDefault){
732             e.preventDefault();
733         }
734         if (this.pressed === true || this.pressed === false) {
735             this.pressed = !this.pressed;
736             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
737             this.fireEvent('toggle', this, e, this.pressed);
738         }
739         
740         
741         this.fireEvent('click', this, e);
742     },
743     
744     /**
745      * Enables this button
746      */
747     enable : function()
748     {
749         this.disabled = false;
750         this.el.removeClass('disabled');
751     },
752     
753     /**
754      * Disable this button
755      */
756     disable : function()
757     {
758         this.disabled = true;
759         this.el.addClass('disabled');
760     },
761      /**
762      * sets the active state on/off, 
763      * @param {Boolean} state (optional) Force a particular state
764      */
765     setActive : function(v) {
766         
767         this.el[v ? 'addClass' : 'removeClass']('active');
768     },
769      /**
770      * toggles the current active state 
771      */
772     toggleActive : function()
773     {
774        var active = this.el.hasClass('active');
775        this.setActive(!active);
776        
777         
778     },
779     setText : function(str)
780     {
781         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
782     },
783     getText : function()
784     {
785         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
786     },
787     hide: function() {
788        
789      
790         this.el.hide();   
791     },
792     show: function() {
793        
794         this.el.show();   
795     }
796     
797     
798 });
799
800  /*
801  * - LGPL
802  *
803  * column
804  * 
805  */
806
807 /**
808  * @class Roo.bootstrap.Column
809  * @extends Roo.bootstrap.Component
810  * Bootstrap Column class
811  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
812  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
813  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
814  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
815  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
816  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
817  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
818  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
819  *
820  * 
821  * @cfg {Boolean} hidden (true|false) hide the element
822  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
823  * @cfg {String} fa (ban|check|...) font awesome icon
824  * @cfg {Number} fasize (1|2|....) font awsome size
825
826  * @cfg {String} icon (info-sign|check|...) glyphicon name
827
828  * @cfg {String} html content of column.
829  * 
830  * @constructor
831  * Create a new Column
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Column = function(config){
836     Roo.bootstrap.Column.superclass.constructor.call(this, config);
837 };
838
839 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
840     
841     xs: false,
842     sm: false,
843     md: false,
844     lg: false,
845     xsoff: false,
846     smoff: false,
847     mdoff: false,
848     lgoff: false,
849     html: '',
850     offset: 0,
851     alert: false,
852     fa: false,
853     icon : false,
854     hidden : false,
855     fasize : 1,
856     
857     getAutoCreate : function(){
858         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
859         
860         cfg = {
861             tag: 'div',
862             cls: 'column'
863         };
864         
865         var settings=this;
866         ['xs','sm','md','lg'].map(function(size){
867             //Roo.log( size + ':' + settings[size]);
868             
869             if (settings[size+'off'] !== false) {
870                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
871             }
872             
873             if (settings[size] === false) {
874                 return;
875             }
876             Roo.log(settings[size]);
877             if (!settings[size]) { // 0 = hidden
878                 cfg.cls += ' hidden-' + size;
879                 return;
880             }
881             cfg.cls += ' col-' + size + '-' + settings[size];
882             
883         });
884         
885         if (this.hidden) {
886             cfg.cls += ' hidden';
887         }
888         
889         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
890             cfg.cls +=' alert alert-' + this.alert;
891         }
892         
893         
894         if (this.html.length) {
895             cfg.html = this.html;
896         }
897         if (this.fa) {
898             var fasize = '';
899             if (this.fasize > 1) {
900                 fasize = ' fa-' + this.fasize + 'x';
901             }
902             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
903             
904             
905         }
906         if (this.icon) {
907             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
908         }
909         
910         return cfg;
911     }
912    
913 });
914
915  
916
917  /*
918  * - LGPL
919  *
920  * page container.
921  * 
922  */
923
924
925 /**
926  * @class Roo.bootstrap.Container
927  * @extends Roo.bootstrap.Component
928  * Bootstrap Container class
929  * @cfg {Boolean} jumbotron is it a jumbotron element
930  * @cfg {String} html content of element
931  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
932  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
933  * @cfg {String} header content of header (for panel)
934  * @cfg {String} footer content of footer (for panel)
935  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
936  * @cfg {String} tag (header|aside|section) type of HTML tag.
937  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
938  * @cfg {String} fa (ban|check|...) font awesome icon
939  * @cfg {String} icon (info-sign|check|...) glyphicon name
940  * @cfg {Boolean} hidden (true|false) hide the element
941
942  *     
943  * @constructor
944  * Create a new Container
945  * @param {Object} config The config object
946  */
947
948 Roo.bootstrap.Container = function(config){
949     Roo.bootstrap.Container.superclass.constructor.call(this, config);
950 };
951
952 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
953     
954     jumbotron : false,
955     well: '',
956     panel : '',
957     header: '',
958     footer : '',
959     sticky: '',
960     tag : false,
961     alert : false,
962     fa: false,
963     icon : false,
964   
965      
966     getChildContainer : function() {
967         
968         if(!this.el){
969             return false;
970         }
971         
972         if (this.panel.length) {
973             return this.el.select('.panel-body',true).first();
974         }
975         
976         return this.el;
977     },
978     
979     
980     getAutoCreate : function(){
981         
982         var cfg = {
983             tag : this.tag || 'div',
984             html : '',
985             cls : ''
986         };
987         if (this.jumbotron) {
988             cfg.cls = 'jumbotron';
989         }
990         
991         
992         
993         // - this is applied by the parent..
994         //if (this.cls) {
995         //    cfg.cls = this.cls + '';
996         //}
997         
998         if (this.sticky.length) {
999             
1000             var bd = Roo.get(document.body);
1001             if (!bd.hasClass('bootstrap-sticky')) {
1002                 bd.addClass('bootstrap-sticky');
1003                 Roo.select('html',true).setStyle('height', '100%');
1004             }
1005              
1006             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1007         }
1008         
1009         
1010         if (this.well.length) {
1011             switch (this.well) {
1012                 case 'lg':
1013                 case 'sm':
1014                     cfg.cls +=' well well-' +this.well;
1015                     break;
1016                 default:
1017                     cfg.cls +=' well';
1018                     break;
1019             }
1020         }
1021         
1022         if (this.hidden) {
1023             cfg.cls += ' hidden';
1024         }
1025         
1026         
1027         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028             cfg.cls +=' alert alert-' + this.alert;
1029         }
1030         
1031         var body = cfg;
1032         
1033         if (this.panel.length) {
1034             cfg.cls += ' panel panel-' + this.panel;
1035             cfg.cn = [];
1036             if (this.header.length) {
1037                 cfg.cn.push({
1038                     
1039                     cls : 'panel-heading',
1040                     cn : [{
1041                         tag: 'h3',
1042                         cls : 'panel-title',
1043                         html : this.header
1044                     }]
1045                     
1046                 });
1047             }
1048             body = false;
1049             cfg.cn.push({
1050                 cls : 'panel-body',
1051                 html : this.html
1052             });
1053             
1054             
1055             if (this.footer.length) {
1056                 cfg.cn.push({
1057                     cls : 'panel-footer',
1058                     html : this.footer
1059                     
1060                 });
1061             }
1062             
1063         }
1064         
1065         if (body) {
1066             body.html = this.html || cfg.html;
1067             // prefix with the icons..
1068             if (this.fa) {
1069                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1070             }
1071             if (this.icon) {
1072                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1073             }
1074             
1075             
1076         }
1077         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1078             cfg.cls =  'container';
1079         }
1080         
1081         return cfg;
1082     },
1083     
1084     titleEl : function()
1085     {
1086         if(!this.el || !this.panel.length || !this.header.length){
1087             return;
1088         }
1089         
1090         return this.el.select('.panel-title',true).first();
1091     },
1092     
1093     setTitle : function(v)
1094     {
1095         var titleEl = this.titleEl();
1096         
1097         if(!titleEl){
1098             return;
1099         }
1100         
1101         titleEl.dom.innerHTML = v;
1102     },
1103     
1104     getTitle : function()
1105     {
1106         
1107         var titleEl = this.titleEl();
1108         
1109         if(!titleEl){
1110             return '';
1111         }
1112         
1113         return titleEl.dom.innerHTML;
1114     }
1115    
1116 });
1117
1118  /*
1119  * - LGPL
1120  *
1121  * image
1122  * 
1123  */
1124
1125
1126 /**
1127  * @class Roo.bootstrap.Img
1128  * @extends Roo.bootstrap.Component
1129  * Bootstrap Img class
1130  * @cfg {Boolean} imgResponsive false | true
1131  * @cfg {String} border rounded | circle | thumbnail
1132  * @cfg {String} src image source
1133  * @cfg {String} alt image alternative text
1134  * @cfg {String} href a tag href
1135  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1136  * 
1137  * @constructor
1138  * Create a new Input
1139  * @param {Object} config The config object
1140  */
1141
1142 Roo.bootstrap.Img = function(config){
1143     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1144     
1145     this.addEvents({
1146         // img events
1147         /**
1148          * @event click
1149          * The img click event for the img.
1150          * @param {Roo.EventObject} e
1151          */
1152         "click" : true
1153     });
1154 };
1155
1156 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1157     
1158     imgResponsive: true,
1159     border: '',
1160     src: '',
1161     href: false,
1162     target: false,
1163
1164     getAutoCreate : function(){
1165         
1166         var cfg = {
1167             tag: 'img',
1168             cls: (this.imgResponsive) ? 'img-responsive' : '',
1169             html : null
1170         }
1171         
1172         cfg.html = this.html || cfg.html;
1173         
1174         cfg.src = this.src || cfg.src;
1175         
1176         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1177             cfg.cls += ' img-' + this.border;
1178         }
1179         
1180         if(this.alt){
1181             cfg.alt = this.alt;
1182         }
1183         
1184         if(this.href){
1185             var a = {
1186                 tag: 'a',
1187                 href: this.href,
1188                 cn: [
1189                     cfg
1190                 ]
1191             }
1192             
1193             if(this.target){
1194                 a.target = this.target;
1195             }
1196             
1197         }
1198         
1199         
1200         return (this.href) ? a : cfg;
1201     },
1202     
1203     initEvents: function() {
1204         
1205         if(!this.href){
1206             this.el.on('click', this.onClick, this);
1207         }
1208     },
1209     
1210     onClick : function(e)
1211     {
1212         Roo.log('img onclick');
1213         this.fireEvent('click', this, e);
1214     }
1215    
1216 });
1217
1218  /*
1219  * - LGPL
1220  *
1221  * image
1222  * 
1223  */
1224
1225
1226 /**
1227  * @class Roo.bootstrap.Link
1228  * @extends Roo.bootstrap.Component
1229  * Bootstrap Link Class
1230  * @cfg {String} alt image alternative text
1231  * @cfg {String} href a tag href
1232  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1233  * @cfg {String} html the content of the link.
1234  * @cfg {String} anchor name for the anchor link
1235
1236  * @cfg {Boolean} preventDefault (true | false) default false
1237
1238  * 
1239  * @constructor
1240  * Create a new Input
1241  * @param {Object} config The config object
1242  */
1243
1244 Roo.bootstrap.Link = function(config){
1245     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1246     
1247     this.addEvents({
1248         // img events
1249         /**
1250          * @event click
1251          * The img click event for the img.
1252          * @param {Roo.EventObject} e
1253          */
1254         "click" : true
1255     });
1256 };
1257
1258 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1259     
1260     href: false,
1261     target: false,
1262     preventDefault: false,
1263     anchor : false,
1264     alt : false,
1265
1266     getAutoCreate : function()
1267     {
1268         
1269         var cfg = {
1270             tag: 'a'
1271         };
1272         // anchor's do not require html/href...
1273         if (this.anchor === false) {
1274             cfg.html = this.html || 'html-missing';
1275             cfg.href = this.href || '#';
1276         } else {
1277             cfg.name = this.anchor;
1278             if (this.html !== false) {
1279                 cfg.html = this.html;
1280             }
1281             if (this.href !== false) {
1282                 cfg.href = this.href;
1283             }
1284         }
1285         
1286         if(this.alt !== false){
1287             cfg.alt = this.alt;
1288         }
1289         
1290         
1291         if(this.target !== false) {
1292             cfg.target = this.target;
1293         }
1294         
1295         return cfg;
1296     },
1297     
1298     initEvents: function() {
1299         
1300         if(!this.href || this.preventDefault){
1301             this.el.on('click', this.onClick, this);
1302         }
1303     },
1304     
1305     onClick : function(e)
1306     {
1307         if(this.preventDefault){
1308             e.preventDefault();
1309         }
1310         //Roo.log('img onclick');
1311         this.fireEvent('click', this, e);
1312     }
1313    
1314 });
1315
1316  /*
1317  * - LGPL
1318  *
1319  * header
1320  * 
1321  */
1322
1323 /**
1324  * @class Roo.bootstrap.Header
1325  * @extends Roo.bootstrap.Component
1326  * Bootstrap Header class
1327  * @cfg {String} html content of header
1328  * @cfg {Number} level (1|2|3|4|5|6) default 1
1329  * 
1330  * @constructor
1331  * Create a new Header
1332  * @param {Object} config The config object
1333  */
1334
1335
1336 Roo.bootstrap.Header  = function(config){
1337     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1338 };
1339
1340 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1341     
1342     //href : false,
1343     html : false,
1344     level : 1,
1345     
1346     
1347     
1348     getAutoCreate : function(){
1349         
1350         var cfg = {
1351             tag: 'h' + (1 *this.level),
1352             html: this.html || 'fill in html'
1353         } ;
1354         
1355         return cfg;
1356     }
1357    
1358 });
1359
1360  
1361
1362  /*
1363  * Based on:
1364  * Ext JS Library 1.1.1
1365  * Copyright(c) 2006-2007, Ext JS, LLC.
1366  *
1367  * Originally Released Under LGPL - original licence link has changed is not relivant.
1368  *
1369  * Fork - LGPL
1370  * <script type="text/javascript">
1371  */
1372  
1373 /**
1374  * @class Roo.bootstrap.MenuMgr
1375  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1376  * @singleton
1377  */
1378 Roo.bootstrap.MenuMgr = function(){
1379    var menus, active, groups = {}, attached = false, lastShow = new Date();
1380
1381    // private - called when first menu is created
1382    function init(){
1383        menus = {};
1384        active = new Roo.util.MixedCollection();
1385        Roo.get(document).addKeyListener(27, function(){
1386            if(active.length > 0){
1387                hideAll();
1388            }
1389        });
1390    }
1391
1392    // private
1393    function hideAll(){
1394        if(active && active.length > 0){
1395            var c = active.clone();
1396            c.each(function(m){
1397                m.hide();
1398            });
1399        }
1400    }
1401
1402    // private
1403    function onHide(m){
1404        active.remove(m);
1405        if(active.length < 1){
1406            Roo.get(document).un("mouseup", onMouseDown);
1407             
1408            attached = false;
1409        }
1410    }
1411
1412    // private
1413    function onShow(m){
1414        var last = active.last();
1415        lastShow = new Date();
1416        active.add(m);
1417        if(!attached){
1418           Roo.get(document).on("mouseup", onMouseDown);
1419            
1420            attached = true;
1421        }
1422        if(m.parentMenu){
1423           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1424           m.parentMenu.activeChild = m;
1425        }else if(last && last.isVisible()){
1426           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1427        }
1428    }
1429
1430    // private
1431    function onBeforeHide(m){
1432        if(m.activeChild){
1433            m.activeChild.hide();
1434        }
1435        if(m.autoHideTimer){
1436            clearTimeout(m.autoHideTimer);
1437            delete m.autoHideTimer;
1438        }
1439    }
1440
1441    // private
1442    function onBeforeShow(m){
1443        var pm = m.parentMenu;
1444        if(!pm && !m.allowOtherMenus){
1445            hideAll();
1446        }else if(pm && pm.activeChild && active != m){
1447            pm.activeChild.hide();
1448        }
1449    }
1450
1451    // private
1452    function onMouseDown(e){
1453         Roo.log("on MouseDown");
1454         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1455            hideAll();
1456         }
1457         
1458         
1459    }
1460
1461    // private
1462    function onBeforeCheck(mi, state){
1463        if(state){
1464            var g = groups[mi.group];
1465            for(var i = 0, l = g.length; i < l; i++){
1466                if(g[i] != mi){
1467                    g[i].setChecked(false);
1468                }
1469            }
1470        }
1471    }
1472
1473    return {
1474
1475        /**
1476         * Hides all menus that are currently visible
1477         */
1478        hideAll : function(){
1479             hideAll();  
1480        },
1481
1482        // private
1483        register : function(menu){
1484            if(!menus){
1485                init();
1486            }
1487            menus[menu.id] = menu;
1488            menu.on("beforehide", onBeforeHide);
1489            menu.on("hide", onHide);
1490            menu.on("beforeshow", onBeforeShow);
1491            menu.on("show", onShow);
1492            var g = menu.group;
1493            if(g && menu.events["checkchange"]){
1494                if(!groups[g]){
1495                    groups[g] = [];
1496                }
1497                groups[g].push(menu);
1498                menu.on("checkchange", onCheck);
1499            }
1500        },
1501
1502         /**
1503          * Returns a {@link Roo.menu.Menu} object
1504          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1505          * be used to generate and return a new Menu instance.
1506          */
1507        get : function(menu){
1508            if(typeof menu == "string"){ // menu id
1509                return menus[menu];
1510            }else if(menu.events){  // menu instance
1511                return menu;
1512            }
1513            /*else if(typeof menu.length == 'number'){ // array of menu items?
1514                return new Roo.bootstrap.Menu({items:menu});
1515            }else{ // otherwise, must be a config
1516                return new Roo.bootstrap.Menu(menu);
1517            }
1518            */
1519            return false;
1520        },
1521
1522        // private
1523        unregister : function(menu){
1524            delete menus[menu.id];
1525            menu.un("beforehide", onBeforeHide);
1526            menu.un("hide", onHide);
1527            menu.un("beforeshow", onBeforeShow);
1528            menu.un("show", onShow);
1529            var g = menu.group;
1530            if(g && menu.events["checkchange"]){
1531                groups[g].remove(menu);
1532                menu.un("checkchange", onCheck);
1533            }
1534        },
1535
1536        // private
1537        registerCheckable : function(menuItem){
1538            var g = menuItem.group;
1539            if(g){
1540                if(!groups[g]){
1541                    groups[g] = [];
1542                }
1543                groups[g].push(menuItem);
1544                menuItem.on("beforecheckchange", onBeforeCheck);
1545            }
1546        },
1547
1548        // private
1549        unregisterCheckable : function(menuItem){
1550            var g = menuItem.group;
1551            if(g){
1552                groups[g].remove(menuItem);
1553                menuItem.un("beforecheckchange", onBeforeCheck);
1554            }
1555        }
1556    };
1557 }();/*
1558  * - LGPL
1559  *
1560  * menu
1561  * 
1562  */
1563
1564 /**
1565  * @class Roo.bootstrap.Menu
1566  * @extends Roo.bootstrap.Component
1567  * Bootstrap Menu class - container for MenuItems
1568  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1569  * 
1570  * @constructor
1571  * Create a new Menu
1572  * @param {Object} config The config object
1573  */
1574
1575
1576 Roo.bootstrap.Menu = function(config){
1577     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1578     if (this.registerMenu) {
1579         Roo.bootstrap.MenuMgr.register(this);
1580     }
1581     this.addEvents({
1582         /**
1583          * @event beforeshow
1584          * Fires before this menu is displayed
1585          * @param {Roo.menu.Menu} this
1586          */
1587         beforeshow : true,
1588         /**
1589          * @event beforehide
1590          * Fires before this menu is hidden
1591          * @param {Roo.menu.Menu} this
1592          */
1593         beforehide : true,
1594         /**
1595          * @event show
1596          * Fires after this menu is displayed
1597          * @param {Roo.menu.Menu} this
1598          */
1599         show : true,
1600         /**
1601          * @event hide
1602          * Fires after this menu is hidden
1603          * @param {Roo.menu.Menu} this
1604          */
1605         hide : true,
1606         /**
1607          * @event click
1608          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1609          * @param {Roo.menu.Menu} this
1610          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1611          * @param {Roo.EventObject} e
1612          */
1613         click : true,
1614         /**
1615          * @event mouseover
1616          * Fires when the mouse is hovering over this menu
1617          * @param {Roo.menu.Menu} this
1618          * @param {Roo.EventObject} e
1619          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1620          */
1621         mouseover : true,
1622         /**
1623          * @event mouseout
1624          * Fires when the mouse exits this menu
1625          * @param {Roo.menu.Menu} this
1626          * @param {Roo.EventObject} e
1627          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1628          */
1629         mouseout : true,
1630         /**
1631          * @event itemclick
1632          * Fires when a menu item contained in this menu is clicked
1633          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1634          * @param {Roo.EventObject} e
1635          */
1636         itemclick: true
1637     });
1638     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1639 };
1640
1641 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1642     
1643    /// html : false,
1644     //align : '',
1645     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1646     type: false,
1647     /**
1648      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1649      */
1650     registerMenu : true,
1651     
1652     menuItems :false, // stores the menu items..
1653     
1654     hidden:true,
1655     
1656     parentMenu : false,
1657     
1658     getChildContainer : function() {
1659         return this.el;  
1660     },
1661     
1662     getAutoCreate : function(){
1663          
1664         //if (['right'].indexOf(this.align)!==-1) {
1665         //    cfg.cn[1].cls += ' pull-right'
1666         //}
1667         
1668         
1669         var cfg = {
1670             tag : 'ul',
1671             cls : 'dropdown-menu' ,
1672             style : 'z-index:1000'
1673             
1674         }
1675         
1676         if (this.type === 'submenu') {
1677             cfg.cls = 'submenu active';
1678         }
1679         if (this.type === 'treeview') {
1680             cfg.cls = 'treeview-menu';
1681         }
1682         
1683         return cfg;
1684     },
1685     initEvents : function() {
1686         
1687        // Roo.log("ADD event");
1688        // Roo.log(this.triggerEl.dom);
1689         this.triggerEl.on('click', this.onTriggerPress, this);
1690         this.triggerEl.addClass('dropdown-toggle');
1691         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1692
1693         this.el.on("mouseover", this.onMouseOver, this);
1694         this.el.on("mouseout", this.onMouseOut, this);
1695         
1696         
1697     },
1698     findTargetItem : function(e){
1699         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1700         if(!t){
1701             return false;
1702         }
1703         //Roo.log(t);         Roo.log(t.id);
1704         if(t && t.id){
1705             //Roo.log(this.menuitems);
1706             return this.menuitems.get(t.id);
1707             
1708             //return this.items.get(t.menuItemId);
1709         }
1710         
1711         return false;
1712     },
1713     onClick : function(e){
1714         Roo.log("menu.onClick");
1715         var t = this.findTargetItem(e);
1716         if(!t || t.isContainer){
1717             return;
1718         }
1719         Roo.log(e);
1720         /*
1721         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1722             if(t == this.activeItem && t.shouldDeactivate(e)){
1723                 this.activeItem.deactivate();
1724                 delete this.activeItem;
1725                 return;
1726             }
1727             if(t.canActivate){
1728                 this.setActiveItem(t, true);
1729             }
1730             return;
1731             
1732             
1733         }
1734         */
1735        
1736         Roo.log('pass click event');
1737         
1738         t.onClick(e);
1739         
1740         this.fireEvent("click", this, t, e);
1741         
1742         this.hide();
1743     },
1744      onMouseOver : function(e){
1745         var t  = this.findTargetItem(e);
1746         //Roo.log(t);
1747         //if(t){
1748         //    if(t.canActivate && !t.disabled){
1749         //        this.setActiveItem(t, true);
1750         //    }
1751         //}
1752         
1753         this.fireEvent("mouseover", this, e, t);
1754     },
1755     isVisible : function(){
1756         return !this.hidden;
1757     },
1758      onMouseOut : function(e){
1759         var t  = this.findTargetItem(e);
1760         
1761         //if(t ){
1762         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1763         //        this.activeItem.deactivate();
1764         //        delete this.activeItem;
1765         //    }
1766         //}
1767         this.fireEvent("mouseout", this, e, t);
1768     },
1769     
1770     
1771     /**
1772      * Displays this menu relative to another element
1773      * @param {String/HTMLElement/Roo.Element} element The element to align to
1774      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1775      * the element (defaults to this.defaultAlign)
1776      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1777      */
1778     show : function(el, pos, parentMenu){
1779         this.parentMenu = parentMenu;
1780         if(!this.el){
1781             this.render();
1782         }
1783         this.fireEvent("beforeshow", this);
1784         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1785     },
1786      /**
1787      * Displays this menu at a specific xy position
1788      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1789      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1790      */
1791     showAt : function(xy, parentMenu, /* private: */_e){
1792         this.parentMenu = parentMenu;
1793         if(!this.el){
1794             this.render();
1795         }
1796         if(_e !== false){
1797             this.fireEvent("beforeshow", this);
1798             
1799             //xy = this.el.adjustForConstraints(xy);
1800         }
1801         //this.el.setXY(xy);
1802         //this.el.show();
1803         this.hideMenuItems();
1804         this.hidden = false;
1805         this.triggerEl.addClass('open');
1806         this.focus();
1807         this.fireEvent("show", this);
1808     },
1809     
1810     focus : function(){
1811         return;
1812         if(!this.hidden){
1813             this.doFocus.defer(50, this);
1814         }
1815     },
1816
1817     doFocus : function(){
1818         if(!this.hidden){
1819             this.focusEl.focus();
1820         }
1821     },
1822
1823     /**
1824      * Hides this menu and optionally all parent menus
1825      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1826      */
1827     hide : function(deep){
1828         
1829         this.hideMenuItems();
1830         if(this.el && this.isVisible()){
1831             this.fireEvent("beforehide", this);
1832             if(this.activeItem){
1833                 this.activeItem.deactivate();
1834                 this.activeItem = null;
1835             }
1836             this.triggerEl.removeClass('open');;
1837             this.hidden = true;
1838             this.fireEvent("hide", this);
1839         }
1840         if(deep === true && this.parentMenu){
1841             this.parentMenu.hide(true);
1842         }
1843     },
1844     
1845     onTriggerPress  : function(e)
1846     {
1847         
1848         Roo.log('trigger press');
1849         //Roo.log(e.getTarget());
1850        // Roo.log(this.triggerEl.dom);
1851         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1852             return;
1853         }
1854         if (this.isVisible()) {
1855             Roo.log('hide');
1856             this.hide();
1857         } else {
1858             this.show(this.triggerEl, false, false);
1859         }
1860         
1861         
1862     },
1863     
1864          
1865        
1866     
1867     hideMenuItems : function()
1868     {
1869         //$(backdrop).remove()
1870         Roo.select('.open',true).each(function(aa) {
1871             
1872             aa.removeClass('open');
1873           //var parent = getParent($(this))
1874           //var relatedTarget = { relatedTarget: this }
1875           
1876            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1877           //if (e.isDefaultPrevented()) return
1878            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1879         })
1880     },
1881     addxtypeChild : function (tree, cntr) {
1882         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1883           
1884         this.menuitems.add(comp);
1885         return comp;
1886
1887     },
1888     getEl : function()
1889     {
1890         Roo.log(this.el);
1891         return this.el;
1892     }
1893 });
1894
1895  
1896  /*
1897  * - LGPL
1898  *
1899  * menu item
1900  * 
1901  */
1902
1903
1904 /**
1905  * @class Roo.bootstrap.MenuItem
1906  * @extends Roo.bootstrap.Component
1907  * Bootstrap MenuItem class
1908  * @cfg {String} html the menu label
1909  * @cfg {String} href the link
1910  * @cfg {Boolean} preventDefault (true | false) default true
1911  * @cfg {Boolean} isContainer (true | false) default false
1912  * 
1913  * 
1914  * @constructor
1915  * Create a new MenuItem
1916  * @param {Object} config The config object
1917  */
1918
1919
1920 Roo.bootstrap.MenuItem = function(config){
1921     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event click
1926          * The raw click event for the entire grid.
1927          * @param {Roo.EventObject} e
1928          */
1929         "click" : true
1930     });
1931 };
1932
1933 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1934     
1935     href : false,
1936     html : false,
1937     preventDefault: true,
1938     isContainer : false,
1939     
1940     getAutoCreate : function(){
1941         
1942         if(this.isContainer){
1943             return {
1944                 tag: 'li',
1945                 cls: 'dropdown-menu-item'
1946             };
1947         }
1948         
1949         var cfg= {
1950             tag: 'li',
1951             cls: 'dropdown-menu-item',
1952             cn: [
1953                     {
1954                         tag : 'a',
1955                         href : '#',
1956                         html : 'Link'
1957                     }
1958                 ]
1959         };
1960         if (this.parent().type == 'treeview') {
1961             cfg.cls = 'treeview-menu';
1962         }
1963         
1964         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1965         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1966         return cfg;
1967     },
1968     
1969     initEvents: function() {
1970         
1971         //this.el.select('a').on('click', this.onClick, this);
1972         
1973     },
1974     onClick : function(e)
1975     {
1976         Roo.log('item on click ');
1977         //if(this.preventDefault){
1978         //    e.preventDefault();
1979         //}
1980         //this.parent().hideMenuItems();
1981         
1982         this.fireEvent('click', this, e);
1983     },
1984     getEl : function()
1985     {
1986         return this.el;
1987     }
1988 });
1989
1990  
1991
1992  /*
1993  * - LGPL
1994  *
1995  * menu separator
1996  * 
1997  */
1998
1999
2000 /**
2001  * @class Roo.bootstrap.MenuSeparator
2002  * @extends Roo.bootstrap.Component
2003  * Bootstrap MenuSeparator class
2004  * 
2005  * @constructor
2006  * Create a new MenuItem
2007  * @param {Object} config The config object
2008  */
2009
2010
2011 Roo.bootstrap.MenuSeparator = function(config){
2012     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2013 };
2014
2015 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2016     
2017     getAutoCreate : function(){
2018         var cfg = {
2019             cls: 'divider',
2020             tag : 'li'
2021         };
2022         
2023         return cfg;
2024     }
2025    
2026 });
2027
2028  
2029
2030  
2031 /*
2032 <div class="modal fade">
2033   <div class="modal-dialog">
2034     <div class="modal-content">
2035       <div class="modal-header">
2036         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2037         <h4 class="modal-title">Modal title</h4>
2038       </div>
2039       <div class="modal-body">
2040         <p>One fine body&hellip;</p>
2041       </div>
2042       <div class="modal-footer">
2043         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2044         <button type="button" class="btn btn-primary">Save changes</button>
2045       </div>
2046     </div><!-- /.modal-content -->
2047   </div><!-- /.modal-dialog -->
2048 </div><!-- /.modal -->
2049 */
2050 /*
2051  * - LGPL
2052  *
2053  * page contgainer.
2054  * 
2055  */
2056
2057 /**
2058  * @class Roo.bootstrap.Modal
2059  * @extends Roo.bootstrap.Component
2060  * Bootstrap Modal class
2061  * @cfg {String} title Title of dialog
2062  * @cfg {Boolean} specificTitle (true|false) default false
2063  * @cfg {Array} buttons Array of buttons or standard button set..
2064  * @cfg {String} buttonPosition (left|right|center) default right
2065  * @cfg {Boolean} animate (true | false) default true
2066  * 
2067  * @constructor
2068  * Create a new Modal Dialog
2069  * @param {Object} config The config object
2070  */
2071
2072 Roo.bootstrap.Modal = function(config){
2073     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2074     this.addEvents({
2075         // raw events
2076         /**
2077          * @event btnclick
2078          * The raw btnclick event for the button
2079          * @param {Roo.EventObject} e
2080          */
2081         "btnclick" : true
2082     });
2083     this.buttons = this.buttons || [];
2084 };
2085
2086 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2087     
2088     title : 'test dialog',
2089    
2090     buttons : false,
2091     
2092     // set on load...
2093     body:  false,
2094     
2095     specificTitle: false,
2096     
2097     buttonPosition: 'right',
2098     
2099     animate : true,
2100     
2101     onRender : function(ct, position)
2102     {
2103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2104      
2105         if(!this.el){
2106             var cfg = Roo.apply({},  this.getAutoCreate());
2107             cfg.id = Roo.id();
2108             //if(!cfg.name){
2109             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2110             //}
2111             //if (!cfg.name.length) {
2112             //    delete cfg.name;
2113            // }
2114             if (this.cls) {
2115                 cfg.cls += ' ' + this.cls;
2116             }
2117             if (this.style) {
2118                 cfg.style = this.style;
2119             }
2120             this.el = Roo.get(document.body).createChild(cfg, position);
2121         }
2122         //var type = this.el.dom.type;
2123         
2124         if(this.tabIndex !== undefined){
2125             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2126         }
2127         
2128         
2129         
2130         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2131         this.maskEl.enableDisplayMode("block");
2132         this.maskEl.hide();
2133         //this.el.addClass("x-dlg-modal");
2134     
2135         if (this.buttons.length) {
2136             Roo.each(this.buttons, function(bb) {
2137                 b = Roo.apply({}, bb);
2138                 b.xns = b.xns || Roo.bootstrap;
2139                 b.xtype = b.xtype || 'Button';
2140                 if (typeof(b.listeners) == 'undefined') {
2141                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2142                 }
2143                 
2144                 var btn = Roo.factory(b);
2145                 
2146                 btn.onRender(this.el.select('.modal-footer div').first());
2147                 
2148             },this);
2149         }
2150         // render the children.
2151         var nitems = [];
2152         
2153         if(typeof(this.items) != 'undefined'){
2154             var items = this.items;
2155             delete this.items;
2156
2157             for(var i =0;i < items.length;i++) {
2158                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2159             }
2160         }
2161         
2162         this.items = nitems;
2163         
2164         this.body = this.el.select('.modal-body',true).first();
2165         this.close = this.el.select('.modal-header .close', true).first();
2166         this.footer = this.el.select('.modal-footer',true).first();
2167         this.initEvents();
2168         //this.el.addClass([this.fieldClass, this.cls]);
2169         
2170     },
2171     getAutoCreate : function(){
2172         
2173         
2174         var bdy = {
2175                 cls : 'modal-body',
2176                 html : this.html || ''
2177         };
2178         
2179         var title = {
2180             tag: 'h4',
2181             cls : 'modal-title',
2182             html : this.title
2183         };
2184         
2185         if(this.specificTitle){
2186             title = this.title;
2187         };
2188         
2189         var modal = {
2190             cls: "modal",
2191             style : 'display: none',
2192             cn : [
2193                 {
2194                     cls: "modal-dialog",
2195                     cn : [
2196                         {
2197                             cls : "modal-content",
2198                             cn : [
2199                                 {
2200                                     cls : 'modal-header',
2201                                     cn : [
2202                                         {
2203                                             tag: 'button',
2204                                             cls : 'close',
2205                                             html : '&times'
2206                                         },
2207                                         title
2208                                     ]
2209                                 },
2210                                 bdy,
2211                                 {
2212                                     cls : 'modal-footer',
2213                                     cn : [
2214                                         {
2215                                             tag: 'div',
2216                                             cls: 'btn-' + this.buttonPosition
2217                                         }
2218                                     ]
2219                                     
2220                                 }
2221                                 
2222                                 
2223                             ]
2224                             
2225                         }
2226                     ]
2227                         
2228                 }
2229             ]
2230         };
2231         
2232         if(this.animate){
2233             modal.cls += ' fade';
2234         }
2235         
2236         return modal;
2237           
2238     },
2239     getChildContainer : function() {
2240          
2241          return this.el.select('.modal-body',true).first();
2242         
2243     },
2244     getButtonContainer : function() {
2245          return this.el.select('.modal-footer div',true).first();
2246         
2247     },
2248     initEvents : function()
2249     {
2250         this.el.select('.modal-header .close').on('click', this.hide, this);
2251 //        
2252 //        this.addxtype(this);
2253     },
2254     show : function() {
2255         
2256         if (!this.rendered) {
2257             this.render();
2258         }
2259         
2260         this.el.setStyle('display', 'block');
2261         
2262         if(this.animate){
2263             var _this = this;
2264             (function(){ _this.el.addClass('in'); }).defer(50);
2265         }else{
2266             this.el.addClass('in');
2267         }
2268         
2269         Roo.get(document.body).addClass("x-body-masked");
2270         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2271         this.maskEl.show();
2272         this.el.setStyle('zIndex', '10001');
2273         this.fireEvent('show', this);
2274         
2275         
2276     },
2277     hide : function()
2278     {
2279         this.maskEl.hide();
2280         Roo.get(document.body).removeClass("x-body-masked");
2281         this.el.removeClass('in');
2282         
2283         if(this.animate){
2284             var _this = this;
2285             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2286         }else{
2287             this.el.setStyle('display', 'none');
2288         }
2289         
2290         this.fireEvent('hide', this);
2291     },
2292     
2293     addButton : function(str, cb)
2294     {
2295          
2296         
2297         var b = Roo.apply({}, { html : str } );
2298         b.xns = b.xns || Roo.bootstrap;
2299         b.xtype = b.xtype || 'Button';
2300         if (typeof(b.listeners) == 'undefined') {
2301             b.listeners = { click : cb.createDelegate(this)  };
2302         }
2303         
2304         var btn = Roo.factory(b);
2305            
2306         btn.onRender(this.el.select('.modal-footer div').first());
2307         
2308         return btn;   
2309        
2310     },
2311     
2312     setDefaultButton : function(btn)
2313     {
2314         //this.el.select('.modal-footer').()
2315     },
2316     resizeTo: function(w,h)
2317     {
2318         // skip..
2319     },
2320     setContentSize  : function(w, h)
2321     {
2322         
2323     },
2324     onButtonClick: function(btn,e)
2325     {
2326         //Roo.log([a,b,c]);
2327         this.fireEvent('btnclick', btn.name, e);
2328     },
2329     setTitle: function(str) {
2330         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2331         
2332     }
2333 });
2334
2335
2336 Roo.apply(Roo.bootstrap.Modal,  {
2337     /**
2338          * Button config that displays a single OK button
2339          * @type Object
2340          */
2341         OK :  [{
2342             name : 'ok',
2343             weight : 'primary',
2344             html : 'OK'
2345         }], 
2346         /**
2347          * Button config that displays Yes and No buttons
2348          * @type Object
2349          */
2350         YESNO : [
2351             {
2352                 name  : 'no',
2353                 html : 'No'
2354             },
2355             {
2356                 name  :'yes',
2357                 weight : 'primary',
2358                 html : 'Yes'
2359             }
2360         ],
2361         
2362         /**
2363          * Button config that displays OK and Cancel buttons
2364          * @type Object
2365          */
2366         OKCANCEL : [
2367             {
2368                name : 'cancel',
2369                 html : 'Cancel'
2370             },
2371             {
2372                 name : 'ok',
2373                 weight : 'primary',
2374                 html : 'OK'
2375             }
2376         ],
2377         /**
2378          * Button config that displays Yes, No and Cancel buttons
2379          * @type Object
2380          */
2381         YESNOCANCEL : [
2382             {
2383                 name : 'yes',
2384                 weight : 'primary',
2385                 html : 'Yes'
2386             },
2387             {
2388                 name : 'no',
2389                 html : 'No'
2390             },
2391             {
2392                 name : 'cancel',
2393                 html : 'Cancel'
2394             }
2395         ]
2396 });
2397  /*
2398  * - LGPL
2399  *
2400  * messagebox - can be used as a replace
2401  * 
2402  */
2403 /**
2404  * @class Roo.MessageBox
2405  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2406  * Example usage:
2407  *<pre><code>
2408 // Basic alert:
2409 Roo.Msg.alert('Status', 'Changes saved successfully.');
2410
2411 // Prompt for user data:
2412 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2413     if (btn == 'ok'){
2414         // process text value...
2415     }
2416 });
2417
2418 // Show a dialog using config options:
2419 Roo.Msg.show({
2420    title:'Save Changes?',
2421    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2422    buttons: Roo.Msg.YESNOCANCEL,
2423    fn: processResult,
2424    animEl: 'elId'
2425 });
2426 </code></pre>
2427  * @singleton
2428  */
2429 Roo.bootstrap.MessageBox = function(){
2430     var dlg, opt, mask, waitTimer;
2431     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2432     var buttons, activeTextEl, bwidth;
2433
2434     
2435     // private
2436     var handleButton = function(button){
2437         dlg.hide();
2438         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2439     };
2440
2441     // private
2442     var handleHide = function(){
2443         if(opt && opt.cls){
2444             dlg.el.removeClass(opt.cls);
2445         }
2446         //if(waitTimer){
2447         //    Roo.TaskMgr.stop(waitTimer);
2448         //    waitTimer = null;
2449         //}
2450     };
2451
2452     // private
2453     var updateButtons = function(b){
2454         var width = 0;
2455         if(!b){
2456             buttons["ok"].hide();
2457             buttons["cancel"].hide();
2458             buttons["yes"].hide();
2459             buttons["no"].hide();
2460             //dlg.footer.dom.style.display = 'none';
2461             return width;
2462         }
2463         dlg.footer.dom.style.display = '';
2464         for(var k in buttons){
2465             if(typeof buttons[k] != "function"){
2466                 if(b[k]){
2467                     buttons[k].show();
2468                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2469                     width += buttons[k].el.getWidth()+15;
2470                 }else{
2471                     buttons[k].hide();
2472                 }
2473             }
2474         }
2475         return width;
2476     };
2477
2478     // private
2479     var handleEsc = function(d, k, e){
2480         if(opt && opt.closable !== false){
2481             dlg.hide();
2482         }
2483         if(e){
2484             e.stopEvent();
2485         }
2486     };
2487
2488     return {
2489         /**
2490          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2491          * @return {Roo.BasicDialog} The BasicDialog element
2492          */
2493         getDialog : function(){
2494            if(!dlg){
2495                 dlg = new Roo.bootstrap.Modal( {
2496                     //draggable: true,
2497                     //resizable:false,
2498                     //constraintoviewport:false,
2499                     //fixedcenter:true,
2500                     //collapsible : false,
2501                     //shim:true,
2502                     //modal: true,
2503                   //  width:400,
2504                   //  height:100,
2505                     //buttonAlign:"center",
2506                     closeClick : function(){
2507                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2508                             handleButton("no");
2509                         }else{
2510                             handleButton("cancel");
2511                         }
2512                     }
2513                 });
2514                 dlg.render();
2515                 dlg.on("hide", handleHide);
2516                 mask = dlg.mask;
2517                 //dlg.addKeyListener(27, handleEsc);
2518                 buttons = {};
2519                 this.buttons = buttons;
2520                 var bt = this.buttonText;
2521                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2522                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2523                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2524                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2525                 Roo.log(buttons)
2526                 bodyEl = dlg.body.createChild({
2527
2528                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2529                         '<textarea class="roo-mb-textarea"></textarea>' +
2530                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2531                 });
2532                 msgEl = bodyEl.dom.firstChild;
2533                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2534                 textboxEl.enableDisplayMode();
2535                 textboxEl.addKeyListener([10,13], function(){
2536                     if(dlg.isVisible() && opt && opt.buttons){
2537                         if(opt.buttons.ok){
2538                             handleButton("ok");
2539                         }else if(opt.buttons.yes){
2540                             handleButton("yes");
2541                         }
2542                     }
2543                 });
2544                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2545                 textareaEl.enableDisplayMode();
2546                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2547                 progressEl.enableDisplayMode();
2548                 var pf = progressEl.dom.firstChild;
2549                 if (pf) {
2550                     pp = Roo.get(pf.firstChild);
2551                     pp.setHeight(pf.offsetHeight);
2552                 }
2553                 
2554             }
2555             return dlg;
2556         },
2557
2558         /**
2559          * Updates the message box body text
2560          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2561          * the XHTML-compliant non-breaking space character '&amp;#160;')
2562          * @return {Roo.MessageBox} This message box
2563          */
2564         updateText : function(text){
2565             if(!dlg.isVisible() && !opt.width){
2566                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2567             }
2568             msgEl.innerHTML = text || '&#160;';
2569       
2570             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2571             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2572             var w = Math.max(
2573                     Math.min(opt.width || cw , this.maxWidth), 
2574                     Math.max(opt.minWidth || this.minWidth, bwidth)
2575             );
2576             if(opt.prompt){
2577                 activeTextEl.setWidth(w);
2578             }
2579             if(dlg.isVisible()){
2580                 dlg.fixedcenter = false;
2581             }
2582             // to big, make it scroll. = But as usual stupid IE does not support
2583             // !important..
2584             
2585             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2586                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2587                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2588             } else {
2589                 bodyEl.dom.style.height = '';
2590                 bodyEl.dom.style.overflowY = '';
2591             }
2592             if (cw > w) {
2593                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2594             } else {
2595                 bodyEl.dom.style.overflowX = '';
2596             }
2597             
2598             dlg.setContentSize(w, bodyEl.getHeight());
2599             if(dlg.isVisible()){
2600                 dlg.fixedcenter = true;
2601             }
2602             return this;
2603         },
2604
2605         /**
2606          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2607          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2608          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2609          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2610          * @return {Roo.MessageBox} This message box
2611          */
2612         updateProgress : function(value, text){
2613             if(text){
2614                 this.updateText(text);
2615             }
2616             if (pp) { // weird bug on my firefox - for some reason this is not defined
2617                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2618             }
2619             return this;
2620         },        
2621
2622         /**
2623          * Returns true if the message box is currently displayed
2624          * @return {Boolean} True if the message box is visible, else false
2625          */
2626         isVisible : function(){
2627             return dlg && dlg.isVisible();  
2628         },
2629
2630         /**
2631          * Hides the message box if it is displayed
2632          */
2633         hide : function(){
2634             if(this.isVisible()){
2635                 dlg.hide();
2636             }  
2637         },
2638
2639         /**
2640          * Displays a new message box, or reinitializes an existing message box, based on the config options
2641          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2642          * The following config object properties are supported:
2643          * <pre>
2644 Property    Type             Description
2645 ----------  ---------------  ------------------------------------------------------------------------------------
2646 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2647                                    closes (defaults to undefined)
2648 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2649                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2650 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2651                                    progress and wait dialogs will ignore this property and always hide the
2652                                    close button as they can only be closed programmatically.
2653 cls               String           A custom CSS class to apply to the message box element
2654 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2655                                    displayed (defaults to 75)
2656 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2657                                    function will be btn (the name of the button that was clicked, if applicable,
2658                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2659                                    Progress and wait dialogs will ignore this option since they do not respond to
2660                                    user actions and can only be closed programmatically, so any required function
2661                                    should be called by the same code after it closes the dialog.
2662 icon              String           A CSS class that provides a background image to be used as an icon for
2663                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2664 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2665 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2666 modal             Boolean          False to allow user interaction with the page while the message box is
2667                                    displayed (defaults to true)
2668 msg               String           A string that will replace the existing message box body text (defaults
2669                                    to the XHTML-compliant non-breaking space character '&#160;')
2670 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2671 progress          Boolean          True to display a progress bar (defaults to false)
2672 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2673 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2674 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2675 title             String           The title text
2676 value             String           The string value to set into the active textbox element if displayed
2677 wait              Boolean          True to display a progress bar (defaults to false)
2678 width             Number           The width of the dialog in pixels
2679 </pre>
2680          *
2681          * Example usage:
2682          * <pre><code>
2683 Roo.Msg.show({
2684    title: 'Address',
2685    msg: 'Please enter your address:',
2686    width: 300,
2687    buttons: Roo.MessageBox.OKCANCEL,
2688    multiline: true,
2689    fn: saveAddress,
2690    animEl: 'addAddressBtn'
2691 });
2692 </code></pre>
2693          * @param {Object} config Configuration options
2694          * @return {Roo.MessageBox} This message box
2695          */
2696         show : function(options)
2697         {
2698             
2699             // this causes nightmares if you show one dialog after another
2700             // especially on callbacks..
2701              
2702             if(this.isVisible()){
2703                 
2704                 this.hide();
2705                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2706                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2707                 Roo.log("New Dialog Message:" +  options.msg )
2708                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2709                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2710                 
2711             }
2712             var d = this.getDialog();
2713             opt = options;
2714             d.setTitle(opt.title || "&#160;");
2715             d.close.setDisplayed(opt.closable !== false);
2716             activeTextEl = textboxEl;
2717             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2718             if(opt.prompt){
2719                 if(opt.multiline){
2720                     textboxEl.hide();
2721                     textareaEl.show();
2722                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2723                         opt.multiline : this.defaultTextHeight);
2724                     activeTextEl = textareaEl;
2725                 }else{
2726                     textboxEl.show();
2727                     textareaEl.hide();
2728                 }
2729             }else{
2730                 textboxEl.hide();
2731                 textareaEl.hide();
2732             }
2733             progressEl.setDisplayed(opt.progress === true);
2734             this.updateProgress(0);
2735             activeTextEl.dom.value = opt.value || "";
2736             if(opt.prompt){
2737                 dlg.setDefaultButton(activeTextEl);
2738             }else{
2739                 var bs = opt.buttons;
2740                 var db = null;
2741                 if(bs && bs.ok){
2742                     db = buttons["ok"];
2743                 }else if(bs && bs.yes){
2744                     db = buttons["yes"];
2745                 }
2746                 dlg.setDefaultButton(db);
2747             }
2748             bwidth = updateButtons(opt.buttons);
2749             this.updateText(opt.msg);
2750             if(opt.cls){
2751                 d.el.addClass(opt.cls);
2752             }
2753             d.proxyDrag = opt.proxyDrag === true;
2754             d.modal = opt.modal !== false;
2755             d.mask = opt.modal !== false ? mask : false;
2756             if(!d.isVisible()){
2757                 // force it to the end of the z-index stack so it gets a cursor in FF
2758                 document.body.appendChild(dlg.el.dom);
2759                 d.animateTarget = null;
2760                 d.show(options.animEl);
2761             }
2762             return this;
2763         },
2764
2765         /**
2766          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2767          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2768          * and closing the message box when the process is complete.
2769          * @param {String} title The title bar text
2770          * @param {String} msg The message box body text
2771          * @return {Roo.MessageBox} This message box
2772          */
2773         progress : function(title, msg){
2774             this.show({
2775                 title : title,
2776                 msg : msg,
2777                 buttons: false,
2778                 progress:true,
2779                 closable:false,
2780                 minWidth: this.minProgressWidth,
2781                 modal : true
2782             });
2783             return this;
2784         },
2785
2786         /**
2787          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2788          * If a callback function is passed it will be called after the user clicks the button, and the
2789          * id of the button that was clicked will be passed as the only parameter to the callback
2790          * (could also be the top-right close button).
2791          * @param {String} title The title bar text
2792          * @param {String} msg The message box body text
2793          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2794          * @param {Object} scope (optional) The scope of the callback function
2795          * @return {Roo.MessageBox} This message box
2796          */
2797         alert : function(title, msg, fn, scope){
2798             this.show({
2799                 title : title,
2800                 msg : msg,
2801                 buttons: this.OK,
2802                 fn: fn,
2803                 scope : scope,
2804                 modal : true
2805             });
2806             return this;
2807         },
2808
2809         /**
2810          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2811          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2812          * You are responsible for closing the message box when the process is complete.
2813          * @param {String} msg The message box body text
2814          * @param {String} title (optional) The title bar text
2815          * @return {Roo.MessageBox} This message box
2816          */
2817         wait : function(msg, title){
2818             this.show({
2819                 title : title,
2820                 msg : msg,
2821                 buttons: false,
2822                 closable:false,
2823                 progress:true,
2824                 modal:true,
2825                 width:300,
2826                 wait:true
2827             });
2828             waitTimer = Roo.TaskMgr.start({
2829                 run: function(i){
2830                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2831                 },
2832                 interval: 1000
2833             });
2834             return this;
2835         },
2836
2837         /**
2838          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2839          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2840          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2841          * @param {String} title The title bar text
2842          * @param {String} msg The message box body text
2843          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2844          * @param {Object} scope (optional) The scope of the callback function
2845          * @return {Roo.MessageBox} This message box
2846          */
2847         confirm : function(title, msg, fn, scope){
2848             this.show({
2849                 title : title,
2850                 msg : msg,
2851                 buttons: this.YESNO,
2852                 fn: fn,
2853                 scope : scope,
2854                 modal : true
2855             });
2856             return this;
2857         },
2858
2859         /**
2860          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2861          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2862          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2863          * (could also be the top-right close button) and the text that was entered will be passed as the two
2864          * parameters to the callback.
2865          * @param {String} title The title bar text
2866          * @param {String} msg The message box body text
2867          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2868          * @param {Object} scope (optional) The scope of the callback function
2869          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2870          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2871          * @return {Roo.MessageBox} This message box
2872          */
2873         prompt : function(title, msg, fn, scope, multiline){
2874             this.show({
2875                 title : title,
2876                 msg : msg,
2877                 buttons: this.OKCANCEL,
2878                 fn: fn,
2879                 minWidth:250,
2880                 scope : scope,
2881                 prompt:true,
2882                 multiline: multiline,
2883                 modal : true
2884             });
2885             return this;
2886         },
2887
2888         /**
2889          * Button config that displays a single OK button
2890          * @type Object
2891          */
2892         OK : {ok:true},
2893         /**
2894          * Button config that displays Yes and No buttons
2895          * @type Object
2896          */
2897         YESNO : {yes:true, no:true},
2898         /**
2899          * Button config that displays OK and Cancel buttons
2900          * @type Object
2901          */
2902         OKCANCEL : {ok:true, cancel:true},
2903         /**
2904          * Button config that displays Yes, No and Cancel buttons
2905          * @type Object
2906          */
2907         YESNOCANCEL : {yes:true, no:true, cancel:true},
2908
2909         /**
2910          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2911          * @type Number
2912          */
2913         defaultTextHeight : 75,
2914         /**
2915          * The maximum width in pixels of the message box (defaults to 600)
2916          * @type Number
2917          */
2918         maxWidth : 600,
2919         /**
2920          * The minimum width in pixels of the message box (defaults to 100)
2921          * @type Number
2922          */
2923         minWidth : 100,
2924         /**
2925          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2926          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2927          * @type Number
2928          */
2929         minProgressWidth : 250,
2930         /**
2931          * An object containing the default button text strings that can be overriden for localized language support.
2932          * Supported properties are: ok, cancel, yes and no.
2933          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2934          * @type Object
2935          */
2936         buttonText : {
2937             ok : "OK",
2938             cancel : "Cancel",
2939             yes : "Yes",
2940             no : "No"
2941         }
2942     };
2943 }();
2944
2945 /**
2946  * Shorthand for {@link Roo.MessageBox}
2947  */
2948 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2949 Roo.Msg = Roo.Msg || Roo.MessageBox;
2950 /*
2951  * - LGPL
2952  *
2953  * navbar
2954  * 
2955  */
2956
2957 /**
2958  * @class Roo.bootstrap.Navbar
2959  * @extends Roo.bootstrap.Component
2960  * Bootstrap Navbar class
2961
2962  * @constructor
2963  * Create a new Navbar
2964  * @param {Object} config The config object
2965  */
2966
2967
2968 Roo.bootstrap.Navbar = function(config){
2969     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2970     
2971 };
2972
2973 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2974     
2975     
2976    
2977     // private
2978     navItems : false,
2979     loadMask : false,
2980     
2981     
2982     getAutoCreate : function(){
2983         
2984         
2985         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2986         
2987     },
2988     
2989     initEvents :function ()
2990     {
2991         //Roo.log(this.el.select('.navbar-toggle',true));
2992         this.el.select('.navbar-toggle',true).on('click', function() {
2993            // Roo.log('click');
2994             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2995         }, this);
2996         
2997         var mark = {
2998             tag: "div",
2999             cls:"x-dlg-mask"
3000         }
3001         
3002         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3003         
3004         var size = this.el.getSize();
3005         this.maskEl.setSize(size.width, size.height);
3006         this.maskEl.enableDisplayMode("block");
3007         this.maskEl.hide();
3008         
3009         if(this.loadMask){
3010             this.maskEl.show();
3011         }
3012     },
3013     
3014     
3015     getChildContainer : function()
3016     {
3017         if (this.el.select('.collapse').getCount()) {
3018             return this.el.select('.collapse',true).first();
3019         }
3020         
3021         return this.el;
3022     },
3023     
3024     mask : function()
3025     {
3026         this.maskEl.show();
3027     },
3028     
3029     unmask : function()
3030     {
3031         this.maskEl.hide();
3032     } 
3033     
3034     
3035     
3036     
3037 });
3038
3039
3040
3041  
3042
3043  /*
3044  * - LGPL
3045  *
3046  * navbar
3047  * 
3048  */
3049
3050 /**
3051  * @class Roo.bootstrap.NavSimplebar
3052  * @extends Roo.bootstrap.Navbar
3053  * Bootstrap Sidebar class
3054  *
3055  * @cfg {Boolean} inverse is inverted color
3056  * 
3057  * @cfg {String} type (nav | pills | tabs)
3058  * @cfg {Boolean} arrangement stacked | justified
3059  * @cfg {String} align (left | right) alignment
3060  * 
3061  * @cfg {Boolean} main (true|false) main nav bar? default false
3062  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3063  * 
3064  * @cfg {String} tag (header|footer|nav|div) default is nav 
3065
3066  * 
3067  * 
3068  * 
3069  * @constructor
3070  * Create a new Sidebar
3071  * @param {Object} config The config object
3072  */
3073
3074
3075 Roo.bootstrap.NavSimplebar = function(config){
3076     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3077 };
3078
3079 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3080     
3081     inverse: false,
3082     
3083     type: false,
3084     arrangement: '',
3085     align : false,
3086     
3087     
3088     
3089     main : false,
3090     
3091     
3092     tag : false,
3093     
3094     
3095     getAutoCreate : function(){
3096         
3097         
3098         var cfg = {
3099             tag : this.tag || 'div',
3100             cls : 'navbar'
3101         };
3102           
3103         
3104         cfg.cn = [
3105             {
3106                 cls: 'nav',
3107                 tag : 'ul'
3108             }
3109         ];
3110         
3111          
3112         this.type = this.type || 'nav';
3113         if (['tabs','pills'].indexOf(this.type)!==-1) {
3114             cfg.cn[0].cls += ' nav-' + this.type
3115         
3116         
3117         } else {
3118             if (this.type!=='nav') {
3119                 Roo.log('nav type must be nav/tabs/pills')
3120             }
3121             cfg.cn[0].cls += ' navbar-nav'
3122         }
3123         
3124         
3125         
3126         
3127         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3128             cfg.cn[0].cls += ' nav-' + this.arrangement;
3129         }
3130         
3131         
3132         if (this.align === 'right') {
3133             cfg.cn[0].cls += ' navbar-right';
3134         }
3135         
3136         if (this.inverse) {
3137             cfg.cls += ' navbar-inverse';
3138             
3139         }
3140         
3141         
3142         return cfg;
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  
3156        /*
3157  * - LGPL
3158  *
3159  * navbar
3160  * 
3161  */
3162
3163 /**
3164  * @class Roo.bootstrap.NavHeaderbar
3165  * @extends Roo.bootstrap.NavSimplebar
3166  * Bootstrap Sidebar class
3167  *
3168  * @cfg {String} brand what is brand
3169  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3170  * @cfg {String} brand_href href of the brand
3171  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3172  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3173  * 
3174  * @constructor
3175  * Create a new Sidebar
3176  * @param {Object} config The config object
3177  */
3178
3179
3180 Roo.bootstrap.NavHeaderbar = function(config){
3181     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3182 };
3183
3184 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3185     
3186     position: '',
3187     brand: '',
3188     brand_href: false,
3189     srButton : true,
3190     autohide : false,
3191     
3192     getAutoCreate : function(){
3193         
3194         var   cfg = {
3195             tag: this.nav || 'nav',
3196             cls: 'navbar',
3197             role: 'navigation',
3198             cn: []
3199         };
3200         
3201         if(this.srButton){
3202             cfg.cn.push({
3203                 tag: 'div',
3204                 cls: 'navbar-header',
3205                 cn: [
3206                     {
3207                         tag: 'button',
3208                         type: 'button',
3209                         cls: 'navbar-toggle',
3210                         'data-toggle': 'collapse',
3211                         cn: [
3212                             {
3213                                 tag: 'span',
3214                                 cls: 'sr-only',
3215                                 html: 'Toggle navigation'
3216                             },
3217                             {
3218                                 tag: 'span',
3219                                 cls: 'icon-bar'
3220                             },
3221                             {
3222                                 tag: 'span',
3223                                 cls: 'icon-bar'
3224                             },
3225                             {
3226                                 tag: 'span',
3227                                 cls: 'icon-bar'
3228                             }
3229                         ]
3230                     }
3231                 ]
3232             });
3233         }
3234         
3235         cfg.cn.push({
3236             tag: 'div',
3237             cls: 'collapse navbar-collapse',
3238             cn : []
3239         });
3240         
3241         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3242         
3243         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3244             cfg.cls += ' navbar-' + this.position;
3245             
3246             // tag can override this..
3247             
3248             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3249         }
3250         
3251         if (this.brand !== '') {
3252             cfg.cn[0].cn.push({
3253                 tag: 'a',
3254                 href: this.brand_href ? this.brand_href : '#',
3255                 cls: 'navbar-brand',
3256                 cn: [
3257                 this.brand
3258                 ]
3259             });
3260         }
3261         
3262         if(this.main){
3263             cfg.cls += ' main-nav';
3264         }
3265         
3266         
3267         return cfg;
3268
3269         
3270     },
3271     initEvents : function()
3272     {
3273         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3274         
3275         if (this.autohide) {
3276             
3277             var prevScroll = 0;
3278             var ft = this.el;
3279             
3280             Roo.get(document).on('scroll',function(e) {
3281                 var ns = Roo.get(document).getScroll().top;
3282                 var os = prevScroll;
3283                 prevScroll = ns;
3284                 
3285                 if(ns > os){
3286                     ft.removeClass('slideDown');
3287                     ft.addClass('slideUp');
3288                     return;
3289                 }
3290                 ft.removeClass('slideUp');
3291                 ft.addClass('slideDown');
3292                  
3293               
3294           },this);
3295         }
3296     }    
3297           
3298       
3299     
3300     
3301 });
3302
3303
3304
3305  
3306
3307  /*
3308  * - LGPL
3309  *
3310  * navbar
3311  * 
3312  */
3313
3314 /**
3315  * @class Roo.bootstrap.NavSidebar
3316  * @extends Roo.bootstrap.Navbar
3317  * Bootstrap Sidebar class
3318  * 
3319  * @constructor
3320  * Create a new Sidebar
3321  * @param {Object} config The config object
3322  */
3323
3324
3325 Roo.bootstrap.NavSidebar = function(config){
3326     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3327 };
3328
3329 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3330     
3331     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3332     
3333     getAutoCreate : function(){
3334         
3335         
3336         return  {
3337             tag: 'div',
3338             cls: 'sidebar sidebar-nav'
3339         };
3340     
3341         
3342     }
3343     
3344     
3345     
3346 });
3347
3348
3349
3350  
3351
3352  /*
3353  * - LGPL
3354  *
3355  * nav group
3356  * 
3357  */
3358
3359 /**
3360  * @class Roo.bootstrap.NavGroup
3361  * @extends Roo.bootstrap.Component
3362  * Bootstrap NavGroup class
3363  * @cfg {String} align left | right
3364  * @cfg {Boolean} inverse false | true
3365  * @cfg {String} type (nav|pills|tab) default nav
3366  * @cfg {String} navId - reference Id for navbar.
3367
3368  * 
3369  * @constructor
3370  * Create a new nav group
3371  * @param {Object} config The config object
3372  */
3373
3374 Roo.bootstrap.NavGroup = function(config){
3375     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3376     this.navItems = [];
3377    
3378     Roo.bootstrap.NavGroup.register(this);
3379      this.addEvents({
3380         /**
3381              * @event changed
3382              * Fires when the active item changes
3383              * @param {Roo.bootstrap.NavGroup} this
3384              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3385              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3386          */
3387         'changed': true
3388      });
3389     
3390 };
3391
3392 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3393     
3394     align: '',
3395     inverse: false,
3396     form: false,
3397     type: 'nav',
3398     navId : '',
3399     // private
3400     
3401     navItems : false, 
3402     
3403     getAutoCreate : function()
3404     {
3405         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3406         
3407         cfg = {
3408             tag : 'ul',
3409             cls: 'nav' 
3410         }
3411         
3412         if (['tabs','pills'].indexOf(this.type)!==-1) {
3413             cfg.cls += ' nav-' + this.type
3414         } else {
3415             if (this.type!=='nav') {
3416                 Roo.log('nav type must be nav/tabs/pills')
3417             }
3418             cfg.cls += ' navbar-nav'
3419         }
3420         
3421         if (this.parent().sidebar) {
3422             cfg = {
3423                 tag: 'ul',
3424                 cls: 'dashboard-menu sidebar-menu'
3425             }
3426             
3427             return cfg;
3428         }
3429         
3430         if (this.form === true) {
3431             cfg = {
3432                 tag: 'form',
3433                 cls: 'navbar-form'
3434             }
3435             
3436             if (this.align === 'right') {
3437                 cfg.cls += ' navbar-right';
3438             } else {
3439                 cfg.cls += ' navbar-left';
3440             }
3441         }
3442         
3443         if (this.align === 'right') {
3444             cfg.cls += ' navbar-right';
3445         }
3446         
3447         if (this.inverse) {
3448             cfg.cls += ' navbar-inverse';
3449             
3450         }
3451         
3452         
3453         return cfg;
3454     },
3455     /**
3456     * sets the active Navigation item
3457     * @param {Roo.bootstrap.NavItem} the new current navitem
3458     */
3459     setActiveItem : function(item)
3460     {
3461         var prev = false;
3462         Roo.each(this.navItems, function(v){
3463             if (v == item) {
3464                 return ;
3465             }
3466             if (v.isActive()) {
3467                 v.setActive(false, true);
3468                 prev = v;
3469                 
3470             }
3471             
3472         });
3473
3474         item.setActive(true, true);
3475         this.fireEvent('changed', this, item, prev);
3476         
3477         
3478     },
3479     /**
3480     * gets the active Navigation item
3481     * @return {Roo.bootstrap.NavItem} the current navitem
3482     */
3483     getActive : function()
3484     {
3485         
3486         var prev = false;
3487         Roo.each(this.navItems, function(v){
3488             
3489             if (v.isActive()) {
3490                 prev = v;
3491                 
3492             }
3493             
3494         });
3495         return prev;
3496     },
3497     
3498     indexOfNav : function()
3499     {
3500         
3501         var prev = false;
3502         Roo.each(this.navItems, function(v,i){
3503             
3504             if (v.isActive()) {
3505                 prev = i;
3506                 
3507             }
3508             
3509         });
3510         return prev;
3511     },
3512     /**
3513     * adds a Navigation item
3514     * @param {Roo.bootstrap.NavItem} the navitem to add
3515     */
3516     addItem : function(cfg)
3517     {
3518         var cn = new Roo.bootstrap.NavItem(cfg);
3519         this.register(cn);
3520         cn.parentId = this.id;
3521         cn.onRender(this.el, null);
3522         return cn;
3523     },
3524     /**
3525     * register a Navigation item
3526     * @param {Roo.bootstrap.NavItem} the navitem to add
3527     */
3528     register : function(item)
3529     {
3530         this.navItems.push( item);
3531         item.navId = this.navId;
3532     
3533     },
3534     
3535     /**
3536     * clear all the Navigation item
3537     */
3538    
3539     clearAll : function()
3540     {
3541         this.navItems = [];
3542         this.el.dom.innerHTML = '';
3543     },
3544     
3545     getNavItem: function(tabId)
3546     {
3547         var ret = false;
3548         Roo.each(this.navItems, function(e) {
3549             if (e.tabId == tabId) {
3550                ret =  e;
3551                return false;
3552             }
3553             return true;
3554             
3555         });
3556         return ret;
3557     },
3558     
3559     setActiveNext : function()
3560     {
3561         var i = this.indexOfNav(this.getActive());
3562         if (i > this.navItems.length) {
3563             return;
3564         }
3565         this.setActiveItem(this.navItems[i+1]);
3566     },
3567     setActivePrev : function()
3568     {
3569         var i = this.indexOfNav(this.getActive());
3570         if (i  < 1) {
3571             return;
3572         }
3573         this.setActiveItem(this.navItems[i-1]);
3574     },
3575     clearWasActive : function(except) {
3576         Roo.each(this.navItems, function(e) {
3577             if (e.tabId != except.tabId && e.was_active) {
3578                e.was_active = false;
3579                return false;
3580             }
3581             return true;
3582             
3583         });
3584     },
3585     getWasActive : function ()
3586     {
3587         var r = false;
3588         Roo.each(this.navItems, function(e) {
3589             if (e.was_active) {
3590                r = e;
3591                return false;
3592             }
3593             return true;
3594             
3595         });
3596         return r;
3597     }
3598     
3599     
3600 });
3601
3602  
3603 Roo.apply(Roo.bootstrap.NavGroup, {
3604     
3605     groups: {},
3606      /**
3607     * register a Navigation Group
3608     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3609     */
3610     register : function(navgrp)
3611     {
3612         this.groups[navgrp.navId] = navgrp;
3613         
3614     },
3615     /**
3616     * fetch a Navigation Group based on the navigation ID
3617     * @param {string} the navgroup to add
3618     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3619     */
3620     get: function(navId) {
3621         if (typeof(this.groups[navId]) == 'undefined') {
3622             return false;
3623             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3624         }
3625         return this.groups[navId] ;
3626     }
3627     
3628     
3629     
3630 });
3631
3632  /*
3633  * - LGPL
3634  *
3635  * row
3636  * 
3637  */
3638
3639 /**
3640  * @class Roo.bootstrap.NavItem
3641  * @extends Roo.bootstrap.Component
3642  * Bootstrap Navbar.NavItem class
3643  * @cfg {String} href  link to
3644  * @cfg {String} html content of button
3645  * @cfg {String} badge text inside badge
3646  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3647  * @cfg {String} glyphicon name of glyphicon
3648  * @cfg {String} icon name of font awesome icon
3649  * @cfg {Boolean} active Is item active
3650  * @cfg {Boolean} disabled Is item disabled
3651  
3652  * @cfg {Boolean} preventDefault (true | false) default false
3653  * @cfg {String} tabId the tab that this item activates.
3654  * @cfg {String} tagtype (a|span) render as a href or span?
3655   
3656  * @constructor
3657  * Create a new Navbar Item
3658  * @param {Object} config The config object
3659  */
3660 Roo.bootstrap.NavItem = function(config){
3661     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3662     this.addEvents({
3663         // raw events
3664         /**
3665          * @event click
3666          * The raw click event for the entire grid.
3667          * @param {Roo.EventObject} e
3668          */
3669         "click" : true,
3670          /**
3671             * @event changed
3672             * Fires when the active item active state changes
3673             * @param {Roo.bootstrap.NavItem} this
3674             * @param {boolean} state the new state
3675              
3676          */
3677         'changed': true
3678     });
3679    
3680 };
3681
3682 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3683     
3684     href: false,
3685     html: '',
3686     badge: '',
3687     icon: false,
3688     glyphicon: false,
3689     active: false,
3690     preventDefault : false,
3691     tabId : false,
3692     tagtype : 'a',
3693     disabled : false,
3694     
3695     was_active : false,
3696     
3697     getAutoCreate : function(){
3698          
3699         var cfg = {
3700             tag: 'li',
3701             cls: 'nav-item'
3702             
3703         }
3704         if (this.active) {
3705             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3706         }
3707         if (this.disabled) {
3708             cfg.cls += ' disabled';
3709         }
3710         
3711         if (this.href || this.html || this.glyphicon || this.icon) {
3712             cfg.cn = [
3713                 {
3714                     tag: this.tagtype,
3715                     href : this.href || "#",
3716                     html: this.html || ''
3717                 }
3718             ];
3719             
3720             if (this.icon) {
3721                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3722             }
3723
3724             if(this.glyphicon) {
3725                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3726             }
3727             
3728             if (this.menu) {
3729                 
3730                 cfg.cn[0].html += " <span class='caret'></span>";
3731              
3732             }
3733             
3734             if (this.badge !== '') {
3735                  
3736                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3737             }
3738         }
3739         
3740         
3741         
3742         return cfg;
3743     },
3744     initEvents: function() 
3745     {
3746         if (typeof (this.menu) != 'undefined') {
3747             this.menu.parentType = this.xtype;
3748             this.menu.triggerEl = this.el;
3749             this.addxtype(Roo.apply({}, this.menu));
3750         }
3751         
3752         this.el.select('a',true).on('click', this.onClick, this);
3753         
3754         if(this.tagtype == 'span'){
3755             this.el.select('span',true).on('click', this.onClick, this);
3756         }
3757        
3758         // at this point parent should be available..
3759         this.parent().register(this);
3760     },
3761     
3762     onClick : function(e)
3763     {
3764          
3765         if(this.preventDefault){
3766             e.preventDefault();
3767         }
3768         if (this.disabled) {
3769             return;
3770         }
3771         
3772         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3773         if (tg && tg.transition) {
3774             Roo.log("waiting for the transitionend");
3775             return;
3776         }
3777         
3778         Roo.log("fire event clicked");
3779         if(this.fireEvent('click', this, e) === false){
3780             return;
3781         };
3782         
3783         if(this.tagtype == 'span'){
3784             return;
3785         }
3786         
3787         var p = this.parent();
3788         if (['tabs','pills'].indexOf(p.type)!==-1) {
3789             if (typeof(p.setActiveItem) !== 'undefined') {
3790                 p.setActiveItem(this);
3791             }
3792         }
3793         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3794         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3795             // remove the collapsed menu expand...
3796             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3797         }
3798         
3799     },
3800     
3801     isActive: function () {
3802         return this.active
3803     },
3804     setActive : function(state, fire, is_was_active)
3805     {
3806         if (this.active && !state & this.navId) {
3807             this.was_active = true;
3808             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3809             if (nv) {
3810                 nv.clearWasActive(this);
3811             }
3812             
3813         }
3814         this.active = state;
3815         
3816         if (!state ) {
3817             this.el.removeClass('active');
3818         } else if (!this.el.hasClass('active')) {
3819             this.el.addClass('active');
3820         }
3821         if (fire) {
3822             this.fireEvent('changed', this, state);
3823         }
3824         
3825         // show a panel if it's registered and related..
3826         
3827         if (!this.navId || !this.tabId || !state || is_was_active) {
3828             return;
3829         }
3830         
3831         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3832         if (!tg) {
3833             return;
3834         }
3835         var pan = tg.getPanelByName(this.tabId);
3836         if (!pan) {
3837             return;
3838         }
3839         // if we can not flip to new panel - go back to old nav highlight..
3840         if (false == tg.showPanel(pan)) {
3841             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3842             if (nv) {
3843                 var onav = nv.getWasActive();
3844                 if (onav) {
3845                     onav.setActive(true, false, true);
3846                 }
3847             }
3848             
3849         }
3850         
3851         
3852         
3853     },
3854      // this should not be here...
3855     setDisabled : function(state)
3856     {
3857         this.disabled = state;
3858         if (!state ) {
3859             this.el.removeClass('disabled');
3860         } else if (!this.el.hasClass('disabled')) {
3861             this.el.addClass('disabled');
3862         }
3863         
3864     }
3865 });
3866  
3867
3868  /*
3869  * - LGPL
3870  *
3871  * sidebar item
3872  *
3873  *  li
3874  *    <span> icon </span>
3875  *    <span> text </span>
3876  *    <span>badge </span>
3877  */
3878
3879 /**
3880  * @class Roo.bootstrap.NavSidebarItem
3881  * @extends Roo.bootstrap.NavItem
3882  * Bootstrap Navbar.NavSidebarItem class
3883  * @constructor
3884  * Create a new Navbar Button
3885  * @param {Object} config The config object
3886  */
3887 Roo.bootstrap.NavSidebarItem = function(config){
3888     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3889     this.addEvents({
3890         // raw events
3891         /**
3892          * @event click
3893          * The raw click event for the entire grid.
3894          * @param {Roo.EventObject} e
3895          */
3896         "click" : true,
3897          /**
3898             * @event changed
3899             * Fires when the active item active state changes
3900             * @param {Roo.bootstrap.NavSidebarItem} this
3901             * @param {boolean} state the new state
3902              
3903          */
3904         'changed': true
3905     });
3906    
3907 };
3908
3909 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3910     
3911     
3912     getAutoCreate : function(){
3913         
3914         
3915         var a = {
3916                 tag: 'a',
3917                 href : this.href || '#',
3918                 cls: '',
3919                 html : '',
3920                 cn : []
3921         };
3922         var cfg = {
3923             tag: 'li',
3924             cls: '',
3925             cn: [ a ]
3926         }
3927         var span = {
3928             tag: 'span',
3929             html : this.html || ''
3930         }
3931         
3932         
3933         if (this.active) {
3934             cfg.cls += ' active';
3935         }
3936         
3937         // left icon..
3938         if (this.glyphicon || this.icon) {
3939             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3940             a.cn.push({ tag : 'i', cls : c }) ;
3941         }
3942         // html..
3943         a.cn.push(span);
3944         // then badge..
3945         if (this.badge !== '') {
3946             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3947         }
3948         // fi
3949         if (this.menu) {
3950             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3951             a.cls += 'dropdown-toggle treeview' ;
3952             
3953         }
3954         
3955         
3956         
3957         return cfg;
3958          
3959            
3960     }
3961    
3962      
3963  
3964 });
3965  
3966
3967  /*
3968  * - LGPL
3969  *
3970  * row
3971  * 
3972  */
3973
3974 /**
3975  * @class Roo.bootstrap.Row
3976  * @extends Roo.bootstrap.Component
3977  * Bootstrap Row class (contains columns...)
3978  * 
3979  * @constructor
3980  * Create a new Row
3981  * @param {Object} config The config object
3982  */
3983
3984 Roo.bootstrap.Row = function(config){
3985     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3986 };
3987
3988 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3989     
3990     getAutoCreate : function(){
3991        return {
3992             cls: 'row clearfix'
3993        };
3994     }
3995     
3996     
3997 });
3998
3999  
4000
4001  /*
4002  * - LGPL
4003  *
4004  * element
4005  * 
4006  */
4007
4008 /**
4009  * @class Roo.bootstrap.Element
4010  * @extends Roo.bootstrap.Component
4011  * Bootstrap Element class
4012  * @cfg {String} html contents of the element
4013  * @cfg {String} tag tag of the element
4014  * @cfg {String} cls class of the element
4015  * 
4016  * @constructor
4017  * Create a new Element
4018  * @param {Object} config The config object
4019  */
4020
4021 Roo.bootstrap.Element = function(config){
4022     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4023 };
4024
4025 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4026     
4027     tag: 'div',
4028     cls: '',
4029     html: '',
4030      
4031     
4032     getAutoCreate : function(){
4033         
4034         var cfg = {
4035             tag: this.tag,
4036             cls: this.cls,
4037             html: this.html
4038         }
4039         
4040         
4041         
4042         return cfg;
4043     }
4044    
4045 });
4046
4047  
4048
4049  /*
4050  * - LGPL
4051  *
4052  * pagination
4053  * 
4054  */
4055
4056 /**
4057  * @class Roo.bootstrap.Pagination
4058  * @extends Roo.bootstrap.Component
4059  * Bootstrap Pagination class
4060  * @cfg {String} size xs | sm | md | lg
4061  * @cfg {Boolean} inverse false | true
4062  * 
4063  * @constructor
4064  * Create a new Pagination
4065  * @param {Object} config The config object
4066  */
4067
4068 Roo.bootstrap.Pagination = function(config){
4069     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4070 };
4071
4072 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4073     
4074     cls: false,
4075     size: false,
4076     inverse: false,
4077     
4078     getAutoCreate : function(){
4079         var cfg = {
4080             tag: 'ul',
4081                 cls: 'pagination'
4082         };
4083         if (this.inverse) {
4084             cfg.cls += ' inverse';
4085         }
4086         if (this.html) {
4087             cfg.html=this.html;
4088         }
4089         if (this.cls) {
4090             cfg.cls += " " + this.cls;
4091         }
4092         return cfg;
4093     }
4094    
4095 });
4096
4097  
4098
4099  /*
4100  * - LGPL
4101  *
4102  * Pagination item
4103  * 
4104  */
4105
4106
4107 /**
4108  * @class Roo.bootstrap.PaginationItem
4109  * @extends Roo.bootstrap.Component
4110  * Bootstrap PaginationItem class
4111  * @cfg {String} html text
4112  * @cfg {String} href the link
4113  * @cfg {Boolean} preventDefault (true | false) default true
4114  * @cfg {Boolean} active (true | false) default false
4115  * 
4116  * 
4117  * @constructor
4118  * Create a new PaginationItem
4119  * @param {Object} config The config object
4120  */
4121
4122
4123 Roo.bootstrap.PaginationItem = function(config){
4124     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4125     this.addEvents({
4126         // raw events
4127         /**
4128          * @event click
4129          * The raw click event for the entire grid.
4130          * @param {Roo.EventObject} e
4131          */
4132         "click" : true
4133     });
4134 };
4135
4136 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4137     
4138     href : false,
4139     html : false,
4140     preventDefault: true,
4141     active : false,
4142     cls : false,
4143     
4144     getAutoCreate : function(){
4145         var cfg= {
4146             tag: 'li',
4147             cn: [
4148                 {
4149                     tag : 'a',
4150                     href : this.href ? this.href : '#',
4151                     html : this.html ? this.html : ''
4152                 }
4153             ]
4154         };
4155         
4156         if(this.cls){
4157             cfg.cls = this.cls;
4158         }
4159         
4160         if(this.active){
4161             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4162         }
4163         
4164         return cfg;
4165     },
4166     
4167     initEvents: function() {
4168         
4169         this.el.on('click', this.onClick, this);
4170         
4171     },
4172     onClick : function(e)
4173     {
4174         Roo.log('PaginationItem on click ');
4175         if(this.preventDefault){
4176             e.preventDefault();
4177         }
4178         
4179         this.fireEvent('click', this, e);
4180     }
4181    
4182 });
4183
4184  
4185
4186  /*
4187  * - LGPL
4188  *
4189  * slider
4190  * 
4191  */
4192
4193
4194 /**
4195  * @class Roo.bootstrap.Slider
4196  * @extends Roo.bootstrap.Component
4197  * Bootstrap Slider class
4198  *    
4199  * @constructor
4200  * Create a new Slider
4201  * @param {Object} config The config object
4202  */
4203
4204 Roo.bootstrap.Slider = function(config){
4205     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4206 };
4207
4208 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4209     
4210     getAutoCreate : function(){
4211         
4212         var cfg = {
4213             tag: 'div',
4214             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4215             cn: [
4216                 {
4217                     tag: 'a',
4218                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4219                 }
4220             ]
4221         }
4222         
4223         return cfg;
4224     }
4225    
4226 });
4227
4228  /*
4229  * Based on:
4230  * Ext JS Library 1.1.1
4231  * Copyright(c) 2006-2007, Ext JS, LLC.
4232  *
4233  * Originally Released Under LGPL - original licence link has changed is not relivant.
4234  *
4235  * Fork - LGPL
4236  * <script type="text/javascript">
4237  */
4238  
4239
4240 /**
4241  * @class Roo.grid.ColumnModel
4242  * @extends Roo.util.Observable
4243  * This is the default implementation of a ColumnModel used by the Grid. It defines
4244  * the columns in the grid.
4245  * <br>Usage:<br>
4246  <pre><code>
4247  var colModel = new Roo.grid.ColumnModel([
4248         {header: "Ticker", width: 60, sortable: true, locked: true},
4249         {header: "Company Name", width: 150, sortable: true},
4250         {header: "Market Cap.", width: 100, sortable: true},
4251         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4252         {header: "Employees", width: 100, sortable: true, resizable: false}
4253  ]);
4254  </code></pre>
4255  * <p>
4256  
4257  * The config options listed for this class are options which may appear in each
4258  * individual column definition.
4259  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4260  * @constructor
4261  * @param {Object} config An Array of column config objects. See this class's
4262  * config objects for details.
4263 */
4264 Roo.grid.ColumnModel = function(config){
4265         /**
4266      * The config passed into the constructor
4267      */
4268     this.config = config;
4269     this.lookup = {};
4270
4271     // if no id, create one
4272     // if the column does not have a dataIndex mapping,
4273     // map it to the order it is in the config
4274     for(var i = 0, len = config.length; i < len; i++){
4275         var c = config[i];
4276         if(typeof c.dataIndex == "undefined"){
4277             c.dataIndex = i;
4278         }
4279         if(typeof c.renderer == "string"){
4280             c.renderer = Roo.util.Format[c.renderer];
4281         }
4282         if(typeof c.id == "undefined"){
4283             c.id = Roo.id();
4284         }
4285         if(c.editor && c.editor.xtype){
4286             c.editor  = Roo.factory(c.editor, Roo.grid);
4287         }
4288         if(c.editor && c.editor.isFormField){
4289             c.editor = new Roo.grid.GridEditor(c.editor);
4290         }
4291         this.lookup[c.id] = c;
4292     }
4293
4294     /**
4295      * The width of columns which have no width specified (defaults to 100)
4296      * @type Number
4297      */
4298     this.defaultWidth = 100;
4299
4300     /**
4301      * Default sortable of columns which have no sortable specified (defaults to false)
4302      * @type Boolean
4303      */
4304     this.defaultSortable = false;
4305
4306     this.addEvents({
4307         /**
4308              * @event widthchange
4309              * Fires when the width of a column changes.
4310              * @param {ColumnModel} this
4311              * @param {Number} columnIndex The column index
4312              * @param {Number} newWidth The new width
4313              */
4314             "widthchange": true,
4315         /**
4316              * @event headerchange
4317              * Fires when the text of a header changes.
4318              * @param {ColumnModel} this
4319              * @param {Number} columnIndex The column index
4320              * @param {Number} newText The new header text
4321              */
4322             "headerchange": true,
4323         /**
4324              * @event hiddenchange
4325              * Fires when a column is hidden or "unhidden".
4326              * @param {ColumnModel} this
4327              * @param {Number} columnIndex The column index
4328              * @param {Boolean} hidden true if hidden, false otherwise
4329              */
4330             "hiddenchange": true,
4331             /**
4332          * @event columnmoved
4333          * Fires when a column is moved.
4334          * @param {ColumnModel} this
4335          * @param {Number} oldIndex
4336          * @param {Number} newIndex
4337          */
4338         "columnmoved" : true,
4339         /**
4340          * @event columlockchange
4341          * Fires when a column's locked state is changed
4342          * @param {ColumnModel} this
4343          * @param {Number} colIndex
4344          * @param {Boolean} locked true if locked
4345          */
4346         "columnlockchange" : true
4347     });
4348     Roo.grid.ColumnModel.superclass.constructor.call(this);
4349 };
4350 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4351     /**
4352      * @cfg {String} header The header text to display in the Grid view.
4353      */
4354     /**
4355      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4356      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4357      * specified, the column's index is used as an index into the Record's data Array.
4358      */
4359     /**
4360      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4361      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4362      */
4363     /**
4364      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4365      * Defaults to the value of the {@link #defaultSortable} property.
4366      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4367      */
4368     /**
4369      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4370      */
4371     /**
4372      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4373      */
4374     /**
4375      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4376      */
4377     /**
4378      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4379      */
4380     /**
4381      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4382      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4383      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4384      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4385      */
4386        /**
4387      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4388      */
4389     /**
4390      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4391      */
4392
4393     /**
4394      * Returns the id of the column at the specified index.
4395      * @param {Number} index The column index
4396      * @return {String} the id
4397      */
4398     getColumnId : function(index){
4399         return this.config[index].id;
4400     },
4401
4402     /**
4403      * Returns the column for a specified id.
4404      * @param {String} id The column id
4405      * @return {Object} the column
4406      */
4407     getColumnById : function(id){
4408         return this.lookup[id];
4409     },
4410
4411     
4412     /**
4413      * Returns the column for a specified dataIndex.
4414      * @param {String} dataIndex The column dataIndex
4415      * @return {Object|Boolean} the column or false if not found
4416      */
4417     getColumnByDataIndex: function(dataIndex){
4418         var index = this.findColumnIndex(dataIndex);
4419         return index > -1 ? this.config[index] : false;
4420     },
4421     
4422     /**
4423      * Returns the index for a specified column id.
4424      * @param {String} id The column id
4425      * @return {Number} the index, or -1 if not found
4426      */
4427     getIndexById : function(id){
4428         for(var i = 0, len = this.config.length; i < len; i++){
4429             if(this.config[i].id == id){
4430                 return i;
4431             }
4432         }
4433         return -1;
4434     },
4435     
4436     /**
4437      * Returns the index for a specified column dataIndex.
4438      * @param {String} dataIndex The column dataIndex
4439      * @return {Number} the index, or -1 if not found
4440      */
4441     
4442     findColumnIndex : function(dataIndex){
4443         for(var i = 0, len = this.config.length; i < len; i++){
4444             if(this.config[i].dataIndex == dataIndex){
4445                 return i;
4446             }
4447         }
4448         return -1;
4449     },
4450     
4451     
4452     moveColumn : function(oldIndex, newIndex){
4453         var c = this.config[oldIndex];
4454         this.config.splice(oldIndex, 1);
4455         this.config.splice(newIndex, 0, c);
4456         this.dataMap = null;
4457         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4458     },
4459
4460     isLocked : function(colIndex){
4461         return this.config[colIndex].locked === true;
4462     },
4463
4464     setLocked : function(colIndex, value, suppressEvent){
4465         if(this.isLocked(colIndex) == value){
4466             return;
4467         }
4468         this.config[colIndex].locked = value;
4469         if(!suppressEvent){
4470             this.fireEvent("columnlockchange", this, colIndex, value);
4471         }
4472     },
4473
4474     getTotalLockedWidth : function(){
4475         var totalWidth = 0;
4476         for(var i = 0; i < this.config.length; i++){
4477             if(this.isLocked(i) && !this.isHidden(i)){
4478                 this.totalWidth += this.getColumnWidth(i);
4479             }
4480         }
4481         return totalWidth;
4482     },
4483
4484     getLockedCount : function(){
4485         for(var i = 0, len = this.config.length; i < len; i++){
4486             if(!this.isLocked(i)){
4487                 return i;
4488             }
4489         }
4490     },
4491
4492     /**
4493      * Returns the number of columns.
4494      * @return {Number}
4495      */
4496     getColumnCount : function(visibleOnly){
4497         if(visibleOnly === true){
4498             var c = 0;
4499             for(var i = 0, len = this.config.length; i < len; i++){
4500                 if(!this.isHidden(i)){
4501                     c++;
4502                 }
4503             }
4504             return c;
4505         }
4506         return this.config.length;
4507     },
4508
4509     /**
4510      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4511      * @param {Function} fn
4512      * @param {Object} scope (optional)
4513      * @return {Array} result
4514      */
4515     getColumnsBy : function(fn, scope){
4516         var r = [];
4517         for(var i = 0, len = this.config.length; i < len; i++){
4518             var c = this.config[i];
4519             if(fn.call(scope||this, c, i) === true){
4520                 r[r.length] = c;
4521             }
4522         }
4523         return r;
4524     },
4525
4526     /**
4527      * Returns true if the specified column is sortable.
4528      * @param {Number} col The column index
4529      * @return {Boolean}
4530      */
4531     isSortable : function(col){
4532         if(typeof this.config[col].sortable == "undefined"){
4533             return this.defaultSortable;
4534         }
4535         return this.config[col].sortable;
4536     },
4537
4538     /**
4539      * Returns the rendering (formatting) function defined for the column.
4540      * @param {Number} col The column index.
4541      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4542      */
4543     getRenderer : function(col){
4544         if(!this.config[col].renderer){
4545             return Roo.grid.ColumnModel.defaultRenderer;
4546         }
4547         return this.config[col].renderer;
4548     },
4549
4550     /**
4551      * Sets the rendering (formatting) function for a column.
4552      * @param {Number} col The column index
4553      * @param {Function} fn The function to use to process the cell's raw data
4554      * to return HTML markup for the grid view. The render function is called with
4555      * the following parameters:<ul>
4556      * <li>Data value.</li>
4557      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4558      * <li>css A CSS style string to apply to the table cell.</li>
4559      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4560      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4561      * <li>Row index</li>
4562      * <li>Column index</li>
4563      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4564      */
4565     setRenderer : function(col, fn){
4566         this.config[col].renderer = fn;
4567     },
4568
4569     /**
4570      * Returns the width for the specified column.
4571      * @param {Number} col The column index
4572      * @return {Number}
4573      */
4574     getColumnWidth : function(col){
4575         return this.config[col].width * 1 || this.defaultWidth;
4576     },
4577
4578     /**
4579      * Sets the width for a column.
4580      * @param {Number} col The column index
4581      * @param {Number} width The new width
4582      */
4583     setColumnWidth : function(col, width, suppressEvent){
4584         this.config[col].width = width;
4585         this.totalWidth = null;
4586         if(!suppressEvent){
4587              this.fireEvent("widthchange", this, col, width);
4588         }
4589     },
4590
4591     /**
4592      * Returns the total width of all columns.
4593      * @param {Boolean} includeHidden True to include hidden column widths
4594      * @return {Number}
4595      */
4596     getTotalWidth : function(includeHidden){
4597         if(!this.totalWidth){
4598             this.totalWidth = 0;
4599             for(var i = 0, len = this.config.length; i < len; i++){
4600                 if(includeHidden || !this.isHidden(i)){
4601                     this.totalWidth += this.getColumnWidth(i);
4602                 }
4603             }
4604         }
4605         return this.totalWidth;
4606     },
4607
4608     /**
4609      * Returns the header for the specified column.
4610      * @param {Number} col The column index
4611      * @return {String}
4612      */
4613     getColumnHeader : function(col){
4614         return this.config[col].header;
4615     },
4616
4617     /**
4618      * Sets the header for a column.
4619      * @param {Number} col The column index
4620      * @param {String} header The new header
4621      */
4622     setColumnHeader : function(col, header){
4623         this.config[col].header = header;
4624         this.fireEvent("headerchange", this, col, header);
4625     },
4626
4627     /**
4628      * Returns the tooltip for the specified column.
4629      * @param {Number} col The column index
4630      * @return {String}
4631      */
4632     getColumnTooltip : function(col){
4633             return this.config[col].tooltip;
4634     },
4635     /**
4636      * Sets the tooltip for a column.
4637      * @param {Number} col The column index
4638      * @param {String} tooltip The new tooltip
4639      */
4640     setColumnTooltip : function(col, tooltip){
4641             this.config[col].tooltip = tooltip;
4642     },
4643
4644     /**
4645      * Returns the dataIndex for the specified column.
4646      * @param {Number} col The column index
4647      * @return {Number}
4648      */
4649     getDataIndex : function(col){
4650         return this.config[col].dataIndex;
4651     },
4652
4653     /**
4654      * Sets the dataIndex for a column.
4655      * @param {Number} col The column index
4656      * @param {Number} dataIndex The new dataIndex
4657      */
4658     setDataIndex : function(col, dataIndex){
4659         this.config[col].dataIndex = dataIndex;
4660     },
4661
4662     
4663     
4664     /**
4665      * Returns true if the cell is editable.
4666      * @param {Number} colIndex The column index
4667      * @param {Number} rowIndex The row index
4668      * @return {Boolean}
4669      */
4670     isCellEditable : function(colIndex, rowIndex){
4671         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4672     },
4673
4674     /**
4675      * Returns the editor defined for the cell/column.
4676      * return false or null to disable editing.
4677      * @param {Number} colIndex The column index
4678      * @param {Number} rowIndex The row index
4679      * @return {Object}
4680      */
4681     getCellEditor : function(colIndex, rowIndex){
4682         return this.config[colIndex].editor;
4683     },
4684
4685     /**
4686      * Sets if a column is editable.
4687      * @param {Number} col The column index
4688      * @param {Boolean} editable True if the column is editable
4689      */
4690     setEditable : function(col, editable){
4691         this.config[col].editable = editable;
4692     },
4693
4694
4695     /**
4696      * Returns true if the column is hidden.
4697      * @param {Number} colIndex The column index
4698      * @return {Boolean}
4699      */
4700     isHidden : function(colIndex){
4701         return this.config[colIndex].hidden;
4702     },
4703
4704
4705     /**
4706      * Returns true if the column width cannot be changed
4707      */
4708     isFixed : function(colIndex){
4709         return this.config[colIndex].fixed;
4710     },
4711
4712     /**
4713      * Returns true if the column can be resized
4714      * @return {Boolean}
4715      */
4716     isResizable : function(colIndex){
4717         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4718     },
4719     /**
4720      * Sets if a column is hidden.
4721      * @param {Number} colIndex The column index
4722      * @param {Boolean} hidden True if the column is hidden
4723      */
4724     setHidden : function(colIndex, hidden){
4725         this.config[colIndex].hidden = hidden;
4726         this.totalWidth = null;
4727         this.fireEvent("hiddenchange", this, colIndex, hidden);
4728     },
4729
4730     /**
4731      * Sets the editor for a column.
4732      * @param {Number} col The column index
4733      * @param {Object} editor The editor object
4734      */
4735     setEditor : function(col, editor){
4736         this.config[col].editor = editor;
4737     }
4738 });
4739
4740 Roo.grid.ColumnModel.defaultRenderer = function(value){
4741         if(typeof value == "string" && value.length < 1){
4742             return "&#160;";
4743         }
4744         return value;
4745 };
4746
4747 // Alias for backwards compatibility
4748 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4749 /*
4750  * Based on:
4751  * Ext JS Library 1.1.1
4752  * Copyright(c) 2006-2007, Ext JS, LLC.
4753  *
4754  * Originally Released Under LGPL - original licence link has changed is not relivant.
4755  *
4756  * Fork - LGPL
4757  * <script type="text/javascript">
4758  */
4759  
4760 /**
4761  * @class Roo.LoadMask
4762  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4763  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4764  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4765  * element's UpdateManager load indicator and will be destroyed after the initial load.
4766  * @constructor
4767  * Create a new LoadMask
4768  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4769  * @param {Object} config The config object
4770  */
4771 Roo.LoadMask = function(el, config){
4772     this.el = Roo.get(el);
4773     Roo.apply(this, config);
4774     if(this.store){
4775         this.store.on('beforeload', this.onBeforeLoad, this);
4776         this.store.on('load', this.onLoad, this);
4777         this.store.on('loadexception', this.onLoadException, this);
4778         this.removeMask = false;
4779     }else{
4780         var um = this.el.getUpdateManager();
4781         um.showLoadIndicator = false; // disable the default indicator
4782         um.on('beforeupdate', this.onBeforeLoad, this);
4783         um.on('update', this.onLoad, this);
4784         um.on('failure', this.onLoad, this);
4785         this.removeMask = true;
4786     }
4787 };
4788
4789 Roo.LoadMask.prototype = {
4790     /**
4791      * @cfg {Boolean} removeMask
4792      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4793      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4794      */
4795     /**
4796      * @cfg {String} msg
4797      * The text to display in a centered loading message box (defaults to 'Loading...')
4798      */
4799     msg : 'Loading...',
4800     /**
4801      * @cfg {String} msgCls
4802      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4803      */
4804     msgCls : 'x-mask-loading',
4805
4806     /**
4807      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4808      * @type Boolean
4809      */
4810     disabled: false,
4811
4812     /**
4813      * Disables the mask to prevent it from being displayed
4814      */
4815     disable : function(){
4816        this.disabled = true;
4817     },
4818
4819     /**
4820      * Enables the mask so that it can be displayed
4821      */
4822     enable : function(){
4823         this.disabled = false;
4824     },
4825     
4826     onLoadException : function()
4827     {
4828         Roo.log(arguments);
4829         
4830         if (typeof(arguments[3]) != 'undefined') {
4831             Roo.MessageBox.alert("Error loading",arguments[3]);
4832         } 
4833         /*
4834         try {
4835             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4836                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4837             }   
4838         } catch(e) {
4839             
4840         }
4841         */
4842     
4843         
4844         
4845         this.el.unmask(this.removeMask);
4846     },
4847     // private
4848     onLoad : function()
4849     {
4850         this.el.unmask(this.removeMask);
4851     },
4852
4853     // private
4854     onBeforeLoad : function(){
4855         if(!this.disabled){
4856             this.el.mask(this.msg, this.msgCls);
4857         }
4858     },
4859
4860     // private
4861     destroy : function(){
4862         if(this.store){
4863             this.store.un('beforeload', this.onBeforeLoad, this);
4864             this.store.un('load', this.onLoad, this);
4865             this.store.un('loadexception', this.onLoadException, this);
4866         }else{
4867             var um = this.el.getUpdateManager();
4868             um.un('beforeupdate', this.onBeforeLoad, this);
4869             um.un('update', this.onLoad, this);
4870             um.un('failure', this.onLoad, this);
4871         }
4872     }
4873 };/*
4874  * - LGPL
4875  *
4876  * table
4877  * 
4878  */
4879
4880 /**
4881  * @class Roo.bootstrap.Table
4882  * @extends Roo.bootstrap.Component
4883  * Bootstrap Table class
4884  * @cfg {String} cls table class
4885  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4886  * @cfg {String} bgcolor Specifies the background color for a table
4887  * @cfg {Number} border Specifies whether the table cells should have borders or not
4888  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4889  * @cfg {Number} cellspacing Specifies the space between cells
4890  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4891  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4892  * @cfg {String} sortable Specifies that the table should be sortable
4893  * @cfg {String} summary Specifies a summary of the content of a table
4894  * @cfg {Number} width Specifies the width of a table
4895  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4896  * 
4897  * @cfg {boolean} striped Should the rows be alternative striped
4898  * @cfg {boolean} bordered Add borders to the table
4899  * @cfg {boolean} hover Add hover highlighting
4900  * @cfg {boolean} condensed Format condensed
4901  * @cfg {boolean} responsive Format condensed
4902  * @cfg {Boolean} loadMask (true|false) default false
4903  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4904  * @cfg {Boolean} thead (true|false) generate thead, default true
4905  * @cfg {Boolean} RowSelection (true|false) default false
4906  * @cfg {Boolean} CellSelection (true|false) default false
4907  *
4908  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4909  
4910  * 
4911  * @constructor
4912  * Create a new Table
4913  * @param {Object} config The config object
4914  */
4915
4916 Roo.bootstrap.Table = function(config){
4917     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4918     
4919     if (this.sm) {
4920         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4921         this.sm = this.selModel;
4922         this.sm.xmodule = this.xmodule || false;
4923     }
4924     if (this.cm && typeof(this.cm.config) == 'undefined') {
4925         this.colModel = new Roo.grid.ColumnModel(this.cm);
4926         this.cm = this.colModel;
4927         this.cm.xmodule = this.xmodule || false;
4928     }
4929     if (this.store) {
4930         this.store= Roo.factory(this.store, Roo.data);
4931         this.ds = this.store;
4932         this.ds.xmodule = this.xmodule || false;
4933          
4934     }
4935     if (this.footer && this.store) {
4936         this.footer.dataSource = this.ds;
4937         this.footer = Roo.factory(this.footer);
4938     }
4939     
4940     /** @private */
4941     this.addEvents({
4942         /**
4943          * @event cellclick
4944          * Fires when a cell is clicked
4945          * @param {Roo.bootstrap.Table} this
4946          * @param {Roo.Element} el
4947          * @param {Number} rowIndex
4948          * @param {Number} columnIndex
4949          * @param {Roo.EventObject} e
4950          */
4951         "cellclick" : true,
4952         /**
4953          * @event celldblclick
4954          * Fires when a cell is double clicked
4955          * @param {Roo.bootstrap.Table} this
4956          * @param {Roo.Element} el
4957          * @param {Number} rowIndex
4958          * @param {Number} columnIndex
4959          * @param {Roo.EventObject} e
4960          */
4961         "celldblclick" : true,
4962         /**
4963          * @event rowclick
4964          * Fires when a row is clicked
4965          * @param {Roo.bootstrap.Table} this
4966          * @param {Roo.Element} el
4967          * @param {Number} rowIndex
4968          * @param {Roo.EventObject} e
4969          */
4970         "rowclick" : true,
4971         /**
4972          * @event rowdblclick
4973          * Fires when a row is double clicked
4974          * @param {Roo.bootstrap.Table} this
4975          * @param {Roo.Element} el
4976          * @param {Number} rowIndex
4977          * @param {Roo.EventObject} e
4978          */
4979         "rowdblclick" : true,
4980         /**
4981          * @event mouseover
4982          * Fires when a mouseover occur
4983          * @param {Roo.bootstrap.Table} this
4984          * @param {Roo.Element} el
4985          * @param {Number} rowIndex
4986          * @param {Number} columnIndex
4987          * @param {Roo.EventObject} e
4988          */
4989         "mouseover" : true,
4990         /**
4991          * @event mouseout
4992          * Fires when a mouseout occur
4993          * @param {Roo.bootstrap.Table} this
4994          * @param {Roo.Element} el
4995          * @param {Number} rowIndex
4996          * @param {Number} columnIndex
4997          * @param {Roo.EventObject} e
4998          */
4999         "mouseout" : true,
5000         /**
5001          * @event rowclass
5002          * Fires when a row is rendered, so you can change add a style to it.
5003          * @param {Roo.bootstrap.Table} this
5004          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5005          */
5006         'rowclass' : true
5007         
5008     });
5009 };
5010
5011 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5012     
5013     cls: false,
5014     align: false,
5015     bgcolor: false,
5016     border: false,
5017     cellpadding: false,
5018     cellspacing: false,
5019     frame: false,
5020     rules: false,
5021     sortable: false,
5022     summary: false,
5023     width: false,
5024     striped : false,
5025     bordered: false,
5026     hover:  false,
5027     condensed : false,
5028     responsive : false,
5029     sm : false,
5030     cm : false,
5031     store : false,
5032     loadMask : false,
5033     tfoot : true,
5034     thead : true,
5035     RowSelection : false,
5036     CellSelection : false,
5037     layout : false,
5038     
5039     // Roo.Element - the tbody
5040     mainBody: false, 
5041     
5042     getAutoCreate : function(){
5043         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5044         
5045         cfg = {
5046             tag: 'table',
5047             cls : 'table',
5048             cn : []
5049         }
5050             
5051         if (this.striped) {
5052             cfg.cls += ' table-striped';
5053         }
5054         
5055         if (this.hover) {
5056             cfg.cls += ' table-hover';
5057         }
5058         if (this.bordered) {
5059             cfg.cls += ' table-bordered';
5060         }
5061         if (this.condensed) {
5062             cfg.cls += ' table-condensed';
5063         }
5064         if (this.responsive) {
5065             cfg.cls += ' table-responsive';
5066         }
5067         
5068         if (this.cls) {
5069             cfg.cls+=  ' ' +this.cls;
5070         }
5071         
5072         // this lot should be simplifed...
5073         
5074         if (this.align) {
5075             cfg.align=this.align;
5076         }
5077         if (this.bgcolor) {
5078             cfg.bgcolor=this.bgcolor;
5079         }
5080         if (this.border) {
5081             cfg.border=this.border;
5082         }
5083         if (this.cellpadding) {
5084             cfg.cellpadding=this.cellpadding;
5085         }
5086         if (this.cellspacing) {
5087             cfg.cellspacing=this.cellspacing;
5088         }
5089         if (this.frame) {
5090             cfg.frame=this.frame;
5091         }
5092         if (this.rules) {
5093             cfg.rules=this.rules;
5094         }
5095         if (this.sortable) {
5096             cfg.sortable=this.sortable;
5097         }
5098         if (this.summary) {
5099             cfg.summary=this.summary;
5100         }
5101         if (this.width) {
5102             cfg.width=this.width;
5103         }
5104         if (this.layout) {
5105             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5106         }
5107         
5108         if(this.store || this.cm){
5109             if(this.thead){
5110                 cfg.cn.push(this.renderHeader());
5111             }
5112             
5113             cfg.cn.push(this.renderBody());
5114             
5115             if(this.tfoot){
5116                 cfg.cn.push(this.renderFooter());
5117             }
5118             
5119             cfg.cls+=  ' TableGrid';
5120         }
5121         
5122         return { cn : [ cfg ] };
5123     },
5124     
5125     initEvents : function()
5126     {   
5127         if(!this.store || !this.cm){
5128             return;
5129         }
5130         
5131         //Roo.log('initEvents with ds!!!!');
5132         
5133         this.mainBody = this.el.select('tbody', true).first();
5134         
5135         
5136         var _this = this;
5137         
5138         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5139             e.on('click', _this.sort, _this);
5140         });
5141         
5142         this.el.on("click", this.onClick, this);
5143         this.el.on("dblclick", this.onDblClick, this);
5144         
5145         this.parent().el.setStyle('position', 'relative');
5146         if (this.footer) {
5147             this.footer.parentId = this.id;
5148             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5149         }
5150         
5151         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5152         
5153         this.store.on('load', this.onLoad, this);
5154         this.store.on('beforeload', this.onBeforeLoad, this);
5155         this.store.on('update', this.onUpdate, this);
5156         
5157     },
5158     
5159     onMouseover : function(e, el)
5160     {
5161         var cell = Roo.get(el);
5162         
5163         if(!cell){
5164             return;
5165         }
5166         
5167         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5168             cell = cell.findParent('td', false, true);
5169         }
5170         
5171         var row = cell.findParent('tr', false, true);
5172         var cellIndex = cell.dom.cellIndex;
5173         var rowIndex = row.dom.rowIndex - 1; // start from 0
5174         
5175         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5176         
5177     },
5178     
5179     onMouseout : function(e, el)
5180     {
5181         var cell = Roo.get(el);
5182         
5183         if(!cell){
5184             return;
5185         }
5186         
5187         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5188             cell = cell.findParent('td', false, true);
5189         }
5190         
5191         var row = cell.findParent('tr', false, true);
5192         var cellIndex = cell.dom.cellIndex;
5193         var rowIndex = row.dom.rowIndex - 1; // start from 0
5194         
5195         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5196         
5197     },
5198     
5199     onClick : function(e, el)
5200     {
5201         var cell = Roo.get(el);
5202         
5203         if(!cell || (!this.CellSelection && !this.RowSelection)){
5204             return;
5205         }
5206         
5207         
5208         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5209             cell = cell.findParent('td', false, true);
5210         }
5211         
5212         var row = cell.findParent('tr', false, true);
5213         var cellIndex = cell.dom.cellIndex;
5214         var rowIndex = row.dom.rowIndex - 1;
5215         
5216         if(this.CellSelection){
5217             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5218         }
5219         
5220         if(this.RowSelection){
5221             this.fireEvent('rowclick', this, row, rowIndex, e);
5222         }
5223         
5224         
5225     },
5226     
5227     onDblClick : function(e,el)
5228     {
5229         var cell = Roo.get(el);
5230         
5231         if(!cell || (!this.CellSelection && !this.RowSelection)){
5232             return;
5233         }
5234         
5235         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5236             cell = cell.findParent('td', false, true);
5237         }
5238         
5239         var row = cell.findParent('tr', false, true);
5240         var cellIndex = cell.dom.cellIndex;
5241         var rowIndex = row.dom.rowIndex - 1;
5242         
5243         if(this.CellSelection){
5244             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5245         }
5246         
5247         if(this.RowSelection){
5248             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5249         }
5250     },
5251     
5252     sort : function(e,el)
5253     {
5254         var col = Roo.get(el)
5255         
5256         if(!col.hasClass('sortable')){
5257             return;
5258         }
5259         
5260         var sort = col.attr('sort');
5261         var dir = 'ASC';
5262         
5263         if(col.hasClass('glyphicon-arrow-up')){
5264             dir = 'DESC';
5265         }
5266         
5267         this.store.sortInfo = {field : sort, direction : dir};
5268         
5269         if (this.footer) {
5270             Roo.log("calling footer first");
5271             this.footer.onClick('first');
5272         } else {
5273         
5274             this.store.load({ params : { start : 0 } });
5275         }
5276     },
5277     
5278     renderHeader : function()
5279     {
5280         var header = {
5281             tag: 'thead',
5282             cn : []
5283         };
5284         
5285         var cm = this.cm;
5286         
5287         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5288             
5289             var config = cm.config[i];
5290                     
5291             var c = {
5292                 tag: 'th',
5293                 style : '',
5294                 html: cm.getColumnHeader(i)
5295             };
5296             
5297             if(typeof(config.hidden) != 'undefined' && config.hidden){
5298                 c.style += ' display:none;';
5299             }
5300             
5301             if(typeof(config.dataIndex) != 'undefined'){
5302                 c.sort = config.dataIndex;
5303             }
5304             
5305             if(typeof(config.sortable) != 'undefined' && config.sortable){
5306                 c.cls = 'sortable';
5307             }
5308             
5309             if(typeof(config.align) != 'undefined' && config.align.length){
5310                 c.style += ' text-align:' + config.align + ';';
5311             }
5312             
5313             if(typeof(config.width) != 'undefined'){
5314                 c.style += ' width:' + config.width + 'px;';
5315             }
5316             
5317             header.cn.push(c)
5318         }
5319         
5320         return header;
5321     },
5322     
5323     renderBody : function()
5324     {
5325         var body = {
5326             tag: 'tbody',
5327             cn : [
5328                 {
5329                     tag: 'tr',
5330                     cn : [
5331                         {
5332                             tag : 'td',
5333                             colspan :  this.cm.getColumnCount()
5334                         }
5335                     ]
5336                 }
5337             ]
5338         };
5339         
5340         return body;
5341     },
5342     
5343     renderFooter : function()
5344     {
5345         var footer = {
5346             tag: 'tfoot',
5347             cn : [
5348                 {
5349                     tag: 'tr',
5350                     cn : [
5351                         {
5352                             tag : 'td',
5353                             colspan :  this.cm.getColumnCount()
5354                         }
5355                     ]
5356                 }
5357             ]
5358         };
5359         
5360         return footer;
5361     },
5362     
5363     
5364     
5365     onLoad : function()
5366     {
5367         Roo.log('ds onload');
5368         this.clear();
5369         
5370         var _this = this;
5371         var cm = this.cm;
5372         var ds = this.store;
5373         
5374         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5375             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5376             
5377             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5378                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5379             }
5380             
5381             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5382                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5383             }
5384         });
5385         
5386         var tbody =  this.mainBody;
5387               
5388         if(ds.getCount() > 0){
5389             ds.data.each(function(d,rowIndex){
5390                 var row =  this.renderRow(cm, ds, rowIndex);
5391                 
5392                 tbody.createChild(row);
5393                 
5394                 var _this = this;
5395                 
5396                 if(row.cellObjects.length){
5397                     Roo.each(row.cellObjects, function(r){
5398                         _this.renderCellObject(r);
5399                     })
5400                 }
5401                 
5402             }, this);
5403         }
5404         
5405         Roo.each(this.el.select('tbody td', true).elements, function(e){
5406             e.on('mouseover', _this.onMouseover, _this);
5407         });
5408         
5409         Roo.each(this.el.select('tbody td', true).elements, function(e){
5410             e.on('mouseout', _this.onMouseout, _this);
5411         });
5412
5413         //if(this.loadMask){
5414         //    this.maskEl.hide();
5415         //}
5416     },
5417     
5418     
5419     onUpdate : function(ds,record)
5420     {
5421         this.refreshRow(record);
5422     },
5423     onRemove : function(ds, record, index, isUpdate){
5424         if(isUpdate !== true){
5425             this.fireEvent("beforerowremoved", this, index, record);
5426         }
5427         var bt = this.mainBody.dom;
5428         if(bt.rows[index]){
5429             bt.removeChild(bt.rows[index]);
5430         }
5431         
5432         if(isUpdate !== true){
5433             //this.stripeRows(index);
5434             //this.syncRowHeights(index, index);
5435             //this.layout();
5436             this.fireEvent("rowremoved", this, index, record);
5437         }
5438     },
5439     
5440     
5441     refreshRow : function(record){
5442         var ds = this.store, index;
5443         if(typeof record == 'number'){
5444             index = record;
5445             record = ds.getAt(index);
5446         }else{
5447             index = ds.indexOf(record);
5448         }
5449         this.insertRow(ds, index, true);
5450         this.onRemove(ds, record, index+1, true);
5451         //this.syncRowHeights(index, index);
5452         //this.layout();
5453         this.fireEvent("rowupdated", this, index, record);
5454     },
5455     
5456     insertRow : function(dm, rowIndex, isUpdate){
5457         
5458         if(!isUpdate){
5459             this.fireEvent("beforerowsinserted", this, rowIndex);
5460         }
5461             //var s = this.getScrollState();
5462         var row = this.renderRow(this.cm, this.store, rowIndex);
5463         // insert before rowIndex..
5464         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5465         
5466         var _this = this;
5467                 
5468         if(row.cellObjects.length){
5469             Roo.each(row.cellObjects, function(r){
5470                 _this.renderCellObject(r);
5471             })
5472         }
5473             
5474         if(!isUpdate){
5475             this.fireEvent("rowsinserted", this, rowIndex);
5476             //this.syncRowHeights(firstRow, lastRow);
5477             //this.stripeRows(firstRow);
5478             //this.layout();
5479         }
5480         
5481     },
5482     
5483     
5484     getRowDom : function(rowIndex)
5485     {
5486         // not sure if I need to check this.. but let's do it anyway..
5487         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5488                 this.mainBody.dom.rows[rowIndex] : false
5489     },
5490     // returns the object tree for a tr..
5491   
5492     
5493     renderRow : function(cm, ds, rowIndex) {
5494         
5495         var d = ds.getAt(rowIndex);
5496         
5497         var row = {
5498             tag : 'tr',
5499             cn : []
5500         };
5501             
5502         var cellObjects = [];
5503         
5504         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5505             var config = cm.config[i];
5506             
5507             var renderer = cm.getRenderer(i);
5508             var value = '';
5509             var id = false;
5510             
5511             if(typeof(renderer) !== 'undefined'){
5512                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5513             }
5514             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5515             // and are rendered into the cells after the row is rendered - using the id for the element.
5516             
5517             if(typeof(value) === 'object'){
5518                 id = Roo.id();
5519                 cellObjects.push({
5520                     container : id,
5521                     cfg : value 
5522                 })
5523             }
5524             
5525             var rowcfg = {
5526                 record: d,
5527                 rowIndex : rowIndex,
5528                 colIndex : i,
5529                 rowClass : ''
5530             }
5531
5532             this.fireEvent('rowclass', this, rowcfg);
5533             
5534             var td = {
5535                 tag: 'td',
5536                 cls : rowcfg.rowClass,
5537                 style: '',
5538                 html: (typeof(value) === 'object') ? '' : value
5539             };
5540             
5541             if (id) {
5542                 td.id = id;
5543             }
5544             
5545             if(typeof(config.hidden) != 'undefined' && config.hidden){
5546                 td.style += ' display:none;';
5547             }
5548             
5549             if(typeof(config.align) != 'undefined' && config.align.length){
5550                 td.style += ' text-align:' + config.align + ';';
5551             }
5552             
5553             if(typeof(config.width) != 'undefined'){
5554                 td.style += ' width:' +  config.width + 'px;';
5555             }
5556              
5557             row.cn.push(td);
5558            
5559         }
5560         
5561         row.cellObjects = cellObjects;
5562         
5563         return row;
5564           
5565     },
5566     
5567     
5568     
5569     onBeforeLoad : function()
5570     {
5571         //Roo.log('ds onBeforeLoad');
5572         
5573         //this.clear();
5574         
5575         //if(this.loadMask){
5576         //    this.maskEl.show();
5577         //}
5578     },
5579     
5580     clear : function()
5581     {
5582         this.el.select('tbody', true).first().dom.innerHTML = '';
5583     },
5584     
5585     getSelectionModel : function(){
5586         if(!this.selModel){
5587             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5588         }
5589         return this.selModel;
5590     },
5591     /*
5592      * Render the Roo.bootstrap object from renderder
5593      */
5594     renderCellObject : function(r)
5595     {
5596         var _this = this;
5597         
5598         var t = r.cfg.render(r.container);
5599         
5600         if(r.cfg.cn){
5601             Roo.each(r.cfg.cn, function(c){
5602                 var child = {
5603                     container: t.getChildContainer(),
5604                     cfg: c
5605                 }
5606                 _this.renderCellObject(child);
5607             })
5608         }
5609     }
5610    
5611 });
5612
5613  
5614
5615  /*
5616  * - LGPL
5617  *
5618  * table cell
5619  * 
5620  */
5621
5622 /**
5623  * @class Roo.bootstrap.TableCell
5624  * @extends Roo.bootstrap.Component
5625  * Bootstrap TableCell class
5626  * @cfg {String} html cell contain text
5627  * @cfg {String} cls cell class
5628  * @cfg {String} tag cell tag (td|th) default td
5629  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5630  * @cfg {String} align Aligns the content in a cell
5631  * @cfg {String} axis Categorizes cells
5632  * @cfg {String} bgcolor Specifies the background color of a cell
5633  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5634  * @cfg {Number} colspan Specifies the number of columns a cell should span
5635  * @cfg {String} headers Specifies one or more header cells a cell is related to
5636  * @cfg {Number} height Sets the height of a cell
5637  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5638  * @cfg {Number} rowspan Sets the number of rows a cell should span
5639  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5640  * @cfg {String} valign Vertical aligns the content in a cell
5641  * @cfg {Number} width Specifies the width of a cell
5642  * 
5643  * @constructor
5644  * Create a new TableCell
5645  * @param {Object} config The config object
5646  */
5647
5648 Roo.bootstrap.TableCell = function(config){
5649     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5650 };
5651
5652 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5653     
5654     html: false,
5655     cls: false,
5656     tag: false,
5657     abbr: false,
5658     align: false,
5659     axis: false,
5660     bgcolor: false,
5661     charoff: false,
5662     colspan: false,
5663     headers: false,
5664     height: false,
5665     nowrap: false,
5666     rowspan: false,
5667     scope: false,
5668     valign: false,
5669     width: false,
5670     
5671     
5672     getAutoCreate : function(){
5673         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5674         
5675         cfg = {
5676             tag: 'td'
5677         }
5678         
5679         if(this.tag){
5680             cfg.tag = this.tag;
5681         }
5682         
5683         if (this.html) {
5684             cfg.html=this.html
5685         }
5686         if (this.cls) {
5687             cfg.cls=this.cls
5688         }
5689         if (this.abbr) {
5690             cfg.abbr=this.abbr
5691         }
5692         if (this.align) {
5693             cfg.align=this.align
5694         }
5695         if (this.axis) {
5696             cfg.axis=this.axis
5697         }
5698         if (this.bgcolor) {
5699             cfg.bgcolor=this.bgcolor
5700         }
5701         if (this.charoff) {
5702             cfg.charoff=this.charoff
5703         }
5704         if (this.colspan) {
5705             cfg.colspan=this.colspan
5706         }
5707         if (this.headers) {
5708             cfg.headers=this.headers
5709         }
5710         if (this.height) {
5711             cfg.height=this.height
5712         }
5713         if (this.nowrap) {
5714             cfg.nowrap=this.nowrap
5715         }
5716         if (this.rowspan) {
5717             cfg.rowspan=this.rowspan
5718         }
5719         if (this.scope) {
5720             cfg.scope=this.scope
5721         }
5722         if (this.valign) {
5723             cfg.valign=this.valign
5724         }
5725         if (this.width) {
5726             cfg.width=this.width
5727         }
5728         
5729         
5730         return cfg;
5731     }
5732    
5733 });
5734
5735  
5736
5737  /*
5738  * - LGPL
5739  *
5740  * table row
5741  * 
5742  */
5743
5744 /**
5745  * @class Roo.bootstrap.TableRow
5746  * @extends Roo.bootstrap.Component
5747  * Bootstrap TableRow class
5748  * @cfg {String} cls row class
5749  * @cfg {String} align Aligns the content in a table row
5750  * @cfg {String} bgcolor Specifies a background color for a table row
5751  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5752  * @cfg {String} valign Vertical aligns the content in a table row
5753  * 
5754  * @constructor
5755  * Create a new TableRow
5756  * @param {Object} config The config object
5757  */
5758
5759 Roo.bootstrap.TableRow = function(config){
5760     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5761 };
5762
5763 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5764     
5765     cls: false,
5766     align: false,
5767     bgcolor: false,
5768     charoff: false,
5769     valign: false,
5770     
5771     getAutoCreate : function(){
5772         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5773         
5774         cfg = {
5775             tag: 'tr'
5776         }
5777             
5778         if(this.cls){
5779             cfg.cls = this.cls;
5780         }
5781         if(this.align){
5782             cfg.align = this.align;
5783         }
5784         if(this.bgcolor){
5785             cfg.bgcolor = this.bgcolor;
5786         }
5787         if(this.charoff){
5788             cfg.charoff = this.charoff;
5789         }
5790         if(this.valign){
5791             cfg.valign = this.valign;
5792         }
5793         
5794         return cfg;
5795     }
5796    
5797 });
5798
5799  
5800
5801  /*
5802  * - LGPL
5803  *
5804  * table body
5805  * 
5806  */
5807
5808 /**
5809  * @class Roo.bootstrap.TableBody
5810  * @extends Roo.bootstrap.Component
5811  * Bootstrap TableBody class
5812  * @cfg {String} cls element class
5813  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5814  * @cfg {String} align Aligns the content inside the element
5815  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5816  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5817  * 
5818  * @constructor
5819  * Create a new TableBody
5820  * @param {Object} config The config object
5821  */
5822
5823 Roo.bootstrap.TableBody = function(config){
5824     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5825 };
5826
5827 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5828     
5829     cls: false,
5830     tag: false,
5831     align: false,
5832     charoff: false,
5833     valign: false,
5834     
5835     getAutoCreate : function(){
5836         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5837         
5838         cfg = {
5839             tag: 'tbody'
5840         }
5841             
5842         if (this.cls) {
5843             cfg.cls=this.cls
5844         }
5845         if(this.tag){
5846             cfg.tag = this.tag;
5847         }
5848         
5849         if(this.align){
5850             cfg.align = this.align;
5851         }
5852         if(this.charoff){
5853             cfg.charoff = this.charoff;
5854         }
5855         if(this.valign){
5856             cfg.valign = this.valign;
5857         }
5858         
5859         return cfg;
5860     }
5861     
5862     
5863 //    initEvents : function()
5864 //    {
5865 //        
5866 //        if(!this.store){
5867 //            return;
5868 //        }
5869 //        
5870 //        this.store = Roo.factory(this.store, Roo.data);
5871 //        this.store.on('load', this.onLoad, this);
5872 //        
5873 //        this.store.load();
5874 //        
5875 //    },
5876 //    
5877 //    onLoad: function () 
5878 //    {   
5879 //        this.fireEvent('load', this);
5880 //    }
5881 //    
5882 //   
5883 });
5884
5885  
5886
5887  /*
5888  * Based on:
5889  * Ext JS Library 1.1.1
5890  * Copyright(c) 2006-2007, Ext JS, LLC.
5891  *
5892  * Originally Released Under LGPL - original licence link has changed is not relivant.
5893  *
5894  * Fork - LGPL
5895  * <script type="text/javascript">
5896  */
5897
5898 // as we use this in bootstrap.
5899 Roo.namespace('Roo.form');
5900  /**
5901  * @class Roo.form.Action
5902  * Internal Class used to handle form actions
5903  * @constructor
5904  * @param {Roo.form.BasicForm} el The form element or its id
5905  * @param {Object} config Configuration options
5906  */
5907
5908  
5909  
5910 // define the action interface
5911 Roo.form.Action = function(form, options){
5912     this.form = form;
5913     this.options = options || {};
5914 };
5915 /**
5916  * Client Validation Failed
5917  * @const 
5918  */
5919 Roo.form.Action.CLIENT_INVALID = 'client';
5920 /**
5921  * Server Validation Failed
5922  * @const 
5923  */
5924 Roo.form.Action.SERVER_INVALID = 'server';
5925  /**
5926  * Connect to Server Failed
5927  * @const 
5928  */
5929 Roo.form.Action.CONNECT_FAILURE = 'connect';
5930 /**
5931  * Reading Data from Server Failed
5932  * @const 
5933  */
5934 Roo.form.Action.LOAD_FAILURE = 'load';
5935
5936 Roo.form.Action.prototype = {
5937     type : 'default',
5938     failureType : undefined,
5939     response : undefined,
5940     result : undefined,
5941
5942     // interface method
5943     run : function(options){
5944
5945     },
5946
5947     // interface method
5948     success : function(response){
5949
5950     },
5951
5952     // interface method
5953     handleResponse : function(response){
5954
5955     },
5956
5957     // default connection failure
5958     failure : function(response){
5959         
5960         this.response = response;
5961         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5962         this.form.afterAction(this, false);
5963     },
5964
5965     processResponse : function(response){
5966         this.response = response;
5967         if(!response.responseText){
5968             return true;
5969         }
5970         this.result = this.handleResponse(response);
5971         return this.result;
5972     },
5973
5974     // utility functions used internally
5975     getUrl : function(appendParams){
5976         var url = this.options.url || this.form.url || this.form.el.dom.action;
5977         if(appendParams){
5978             var p = this.getParams();
5979             if(p){
5980                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5981             }
5982         }
5983         return url;
5984     },
5985
5986     getMethod : function(){
5987         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5988     },
5989
5990     getParams : function(){
5991         var bp = this.form.baseParams;
5992         var p = this.options.params;
5993         if(p){
5994             if(typeof p == "object"){
5995                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5996             }else if(typeof p == 'string' && bp){
5997                 p += '&' + Roo.urlEncode(bp);
5998             }
5999         }else if(bp){
6000             p = Roo.urlEncode(bp);
6001         }
6002         return p;
6003     },
6004
6005     createCallback : function(){
6006         return {
6007             success: this.success,
6008             failure: this.failure,
6009             scope: this,
6010             timeout: (this.form.timeout*1000),
6011             upload: this.form.fileUpload ? this.success : undefined
6012         };
6013     }
6014 };
6015
6016 Roo.form.Action.Submit = function(form, options){
6017     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6018 };
6019
6020 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6021     type : 'submit',
6022
6023     haveProgress : false,
6024     uploadComplete : false,
6025     
6026     // uploadProgress indicator.
6027     uploadProgress : function()
6028     {
6029         if (!this.form.progressUrl) {
6030             return;
6031         }
6032         
6033         if (!this.haveProgress) {
6034             Roo.MessageBox.progress("Uploading", "Uploading");
6035         }
6036         if (this.uploadComplete) {
6037            Roo.MessageBox.hide();
6038            return;
6039         }
6040         
6041         this.haveProgress = true;
6042    
6043         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6044         
6045         var c = new Roo.data.Connection();
6046         c.request({
6047             url : this.form.progressUrl,
6048             params: {
6049                 id : uid
6050             },
6051             method: 'GET',
6052             success : function(req){
6053                //console.log(data);
6054                 var rdata = false;
6055                 var edata;
6056                 try  {
6057                    rdata = Roo.decode(req.responseText)
6058                 } catch (e) {
6059                     Roo.log("Invalid data from server..");
6060                     Roo.log(edata);
6061                     return;
6062                 }
6063                 if (!rdata || !rdata.success) {
6064                     Roo.log(rdata);
6065                     Roo.MessageBox.alert(Roo.encode(rdata));
6066                     return;
6067                 }
6068                 var data = rdata.data;
6069                 
6070                 if (this.uploadComplete) {
6071                    Roo.MessageBox.hide();
6072                    return;
6073                 }
6074                    
6075                 if (data){
6076                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6077                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6078                     );
6079                 }
6080                 this.uploadProgress.defer(2000,this);
6081             },
6082        
6083             failure: function(data) {
6084                 Roo.log('progress url failed ');
6085                 Roo.log(data);
6086             },
6087             scope : this
6088         });
6089            
6090     },
6091     
6092     
6093     run : function()
6094     {
6095         // run get Values on the form, so it syncs any secondary forms.
6096         this.form.getValues();
6097         
6098         var o = this.options;
6099         var method = this.getMethod();
6100         var isPost = method == 'POST';
6101         if(o.clientValidation === false || this.form.isValid()){
6102             
6103             if (this.form.progressUrl) {
6104                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6105                     (new Date() * 1) + '' + Math.random());
6106                     
6107             } 
6108             
6109             
6110             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6111                 form:this.form.el.dom,
6112                 url:this.getUrl(!isPost),
6113                 method: method,
6114                 params:isPost ? this.getParams() : null,
6115                 isUpload: this.form.fileUpload
6116             }));
6117             
6118             this.uploadProgress();
6119
6120         }else if (o.clientValidation !== false){ // client validation failed
6121             this.failureType = Roo.form.Action.CLIENT_INVALID;
6122             this.form.afterAction(this, false);
6123         }
6124     },
6125
6126     success : function(response)
6127     {
6128         this.uploadComplete= true;
6129         if (this.haveProgress) {
6130             Roo.MessageBox.hide();
6131         }
6132         
6133         
6134         var result = this.processResponse(response);
6135         if(result === true || result.success){
6136             this.form.afterAction(this, true);
6137             return;
6138         }
6139         if(result.errors){
6140             this.form.markInvalid(result.errors);
6141             this.failureType = Roo.form.Action.SERVER_INVALID;
6142         }
6143         this.form.afterAction(this, false);
6144     },
6145     failure : function(response)
6146     {
6147         this.uploadComplete= true;
6148         if (this.haveProgress) {
6149             Roo.MessageBox.hide();
6150         }
6151         
6152         this.response = response;
6153         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6154         this.form.afterAction(this, false);
6155     },
6156     
6157     handleResponse : function(response){
6158         if(this.form.errorReader){
6159             var rs = this.form.errorReader.read(response);
6160             var errors = [];
6161             if(rs.records){
6162                 for(var i = 0, len = rs.records.length; i < len; i++) {
6163                     var r = rs.records[i];
6164                     errors[i] = r.data;
6165                 }
6166             }
6167             if(errors.length < 1){
6168                 errors = null;
6169             }
6170             return {
6171                 success : rs.success,
6172                 errors : errors
6173             };
6174         }
6175         var ret = false;
6176         try {
6177             ret = Roo.decode(response.responseText);
6178         } catch (e) {
6179             ret = {
6180                 success: false,
6181                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6182                 errors : []
6183             };
6184         }
6185         return ret;
6186         
6187     }
6188 });
6189
6190
6191 Roo.form.Action.Load = function(form, options){
6192     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6193     this.reader = this.form.reader;
6194 };
6195
6196 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6197     type : 'load',
6198
6199     run : function(){
6200         
6201         Roo.Ajax.request(Roo.apply(
6202                 this.createCallback(), {
6203                     method:this.getMethod(),
6204                     url:this.getUrl(false),
6205                     params:this.getParams()
6206         }));
6207     },
6208
6209     success : function(response){
6210         
6211         var result = this.processResponse(response);
6212         if(result === true || !result.success || !result.data){
6213             this.failureType = Roo.form.Action.LOAD_FAILURE;
6214             this.form.afterAction(this, false);
6215             return;
6216         }
6217         this.form.clearInvalid();
6218         this.form.setValues(result.data);
6219         this.form.afterAction(this, true);
6220     },
6221
6222     handleResponse : function(response){
6223         if(this.form.reader){
6224             var rs = this.form.reader.read(response);
6225             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6226             return {
6227                 success : rs.success,
6228                 data : data
6229             };
6230         }
6231         return Roo.decode(response.responseText);
6232     }
6233 });
6234
6235 Roo.form.Action.ACTION_TYPES = {
6236     'load' : Roo.form.Action.Load,
6237     'submit' : Roo.form.Action.Submit
6238 };/*
6239  * - LGPL
6240  *
6241  * form
6242  * 
6243  */
6244
6245 /**
6246  * @class Roo.bootstrap.Form
6247  * @extends Roo.bootstrap.Component
6248  * Bootstrap Form class
6249  * @cfg {String} method  GET | POST (default POST)
6250  * @cfg {String} labelAlign top | left (default top)
6251  * @cfg {String} align left  | right - for navbars
6252  * @cfg {Boolean} loadMask load mask when submit (default true)
6253
6254  * 
6255  * @constructor
6256  * Create a new Form
6257  * @param {Object} config The config object
6258  */
6259
6260
6261 Roo.bootstrap.Form = function(config){
6262     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6263     this.addEvents({
6264         /**
6265          * @event clientvalidation
6266          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6267          * @param {Form} this
6268          * @param {Boolean} valid true if the form has passed client-side validation
6269          */
6270         clientvalidation: true,
6271         /**
6272          * @event beforeaction
6273          * Fires before any action is performed. Return false to cancel the action.
6274          * @param {Form} this
6275          * @param {Action} action The action to be performed
6276          */
6277         beforeaction: true,
6278         /**
6279          * @event actionfailed
6280          * Fires when an action fails.
6281          * @param {Form} this
6282          * @param {Action} action The action that failed
6283          */
6284         actionfailed : true,
6285         /**
6286          * @event actioncomplete
6287          * Fires when an action is completed.
6288          * @param {Form} this
6289          * @param {Action} action The action that completed
6290          */
6291         actioncomplete : true
6292     });
6293     
6294 };
6295
6296 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6297       
6298      /**
6299      * @cfg {String} method
6300      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6301      */
6302     method : 'POST',
6303     /**
6304      * @cfg {String} url
6305      * The URL to use for form actions if one isn't supplied in the action options.
6306      */
6307     /**
6308      * @cfg {Boolean} fileUpload
6309      * Set to true if this form is a file upload.
6310      */
6311      
6312     /**
6313      * @cfg {Object} baseParams
6314      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6315      */
6316       
6317     /**
6318      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6319      */
6320     timeout: 30,
6321     /**
6322      * @cfg {Sting} align (left|right) for navbar forms
6323      */
6324     align : 'left',
6325
6326     // private
6327     activeAction : null,
6328  
6329     /**
6330      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6331      * element by passing it or its id or mask the form itself by passing in true.
6332      * @type Mixed
6333      */
6334     waitMsgTarget : false,
6335     
6336     loadMask : true,
6337     
6338     getAutoCreate : function(){
6339         
6340         var cfg = {
6341             tag: 'form',
6342             method : this.method || 'POST',
6343             id : this.id || Roo.id(),
6344             cls : ''
6345         }
6346         if (this.parent().xtype.match(/^Nav/)) {
6347             cfg.cls = 'navbar-form navbar-' + this.align;
6348             
6349         }
6350         
6351         if (this.labelAlign == 'left' ) {
6352             cfg.cls += ' form-horizontal';
6353         }
6354         
6355         
6356         return cfg;
6357     },
6358     initEvents : function()
6359     {
6360         this.el.on('submit', this.onSubmit, this);
6361         // this was added as random key presses on the form where triggering form submit.
6362         this.el.on('keypress', function(e) {
6363             if (e.getCharCode() != 13) {
6364                 return true;
6365             }
6366             // we might need to allow it for textareas.. and some other items.
6367             // check e.getTarget().
6368             
6369             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6370                 return true;
6371             }
6372         
6373             Roo.log("keypress blocked");
6374             
6375             e.preventDefault();
6376             return false;
6377         });
6378         
6379     },
6380     // private
6381     onSubmit : function(e){
6382         e.stopEvent();
6383     },
6384     
6385      /**
6386      * Returns true if client-side validation on the form is successful.
6387      * @return Boolean
6388      */
6389     isValid : function(){
6390         var items = this.getItems();
6391         var valid = true;
6392         items.each(function(f){
6393            if(!f.validate()){
6394                valid = false;
6395                
6396            }
6397         });
6398         return valid;
6399     },
6400     /**
6401      * Returns true if any fields in this form have changed since their original load.
6402      * @return Boolean
6403      */
6404     isDirty : function(){
6405         var dirty = false;
6406         var items = this.getItems();
6407         items.each(function(f){
6408            if(f.isDirty()){
6409                dirty = true;
6410                return false;
6411            }
6412            return true;
6413         });
6414         return dirty;
6415     },
6416      /**
6417      * Performs a predefined action (submit or load) or custom actions you define on this form.
6418      * @param {String} actionName The name of the action type
6419      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6420      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6421      * accept other config options):
6422      * <pre>
6423 Property          Type             Description
6424 ----------------  ---------------  ----------------------------------------------------------------------------------
6425 url               String           The url for the action (defaults to the form's url)
6426 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6427 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6428 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6429                                    validate the form on the client (defaults to false)
6430      * </pre>
6431      * @return {BasicForm} this
6432      */
6433     doAction : function(action, options){
6434         if(typeof action == 'string'){
6435             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6436         }
6437         if(this.fireEvent('beforeaction', this, action) !== false){
6438             this.beforeAction(action);
6439             action.run.defer(100, action);
6440         }
6441         return this;
6442     },
6443     
6444     // private
6445     beforeAction : function(action){
6446         var o = action.options;
6447         
6448         if(this.loadMask){
6449             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6450         }
6451         // not really supported yet.. ??
6452         
6453         //if(this.waitMsgTarget === true){
6454         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6455         //}else if(this.waitMsgTarget){
6456         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6457         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6458         //}else {
6459         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6460        // }
6461          
6462     },
6463
6464     // private
6465     afterAction : function(action, success){
6466         this.activeAction = null;
6467         var o = action.options;
6468         
6469         //if(this.waitMsgTarget === true){
6470             this.el.unmask();
6471         //}else if(this.waitMsgTarget){
6472         //    this.waitMsgTarget.unmask();
6473         //}else{
6474         //    Roo.MessageBox.updateProgress(1);
6475         //    Roo.MessageBox.hide();
6476        // }
6477         // 
6478         if(success){
6479             if(o.reset){
6480                 this.reset();
6481             }
6482             Roo.callback(o.success, o.scope, [this, action]);
6483             this.fireEvent('actioncomplete', this, action);
6484             
6485         }else{
6486             
6487             // failure condition..
6488             // we have a scenario where updates need confirming.
6489             // eg. if a locking scenario exists..
6490             // we look for { errors : { needs_confirm : true }} in the response.
6491             if (
6492                 (typeof(action.result) != 'undefined')  &&
6493                 (typeof(action.result.errors) != 'undefined')  &&
6494                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6495            ){
6496                 var _t = this;
6497                 Roo.log("not supported yet");
6498                  /*
6499                 
6500                 Roo.MessageBox.confirm(
6501                     "Change requires confirmation",
6502                     action.result.errorMsg,
6503                     function(r) {
6504                         if (r != 'yes') {
6505                             return;
6506                         }
6507                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6508                     }
6509                     
6510                 );
6511                 */
6512                 
6513                 
6514                 return;
6515             }
6516             
6517             Roo.callback(o.failure, o.scope, [this, action]);
6518             // show an error message if no failed handler is set..
6519             if (!this.hasListener('actionfailed')) {
6520                 Roo.log("need to add dialog support");
6521                 /*
6522                 Roo.MessageBox.alert("Error",
6523                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6524                         action.result.errorMsg :
6525                         "Saving Failed, please check your entries or try again"
6526                 );
6527                 */
6528             }
6529             
6530             this.fireEvent('actionfailed', this, action);
6531         }
6532         
6533     },
6534     /**
6535      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6536      * @param {String} id The value to search for
6537      * @return Field
6538      */
6539     findField : function(id){
6540         var items = this.getItems();
6541         var field = items.get(id);
6542         if(!field){
6543              items.each(function(f){
6544                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6545                     field = f;
6546                     return false;
6547                 }
6548                 return true;
6549             });
6550         }
6551         return field || null;
6552     },
6553      /**
6554      * Mark fields in this form invalid in bulk.
6555      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6556      * @return {BasicForm} this
6557      */
6558     markInvalid : function(errors){
6559         if(errors instanceof Array){
6560             for(var i = 0, len = errors.length; i < len; i++){
6561                 var fieldError = errors[i];
6562                 var f = this.findField(fieldError.id);
6563                 if(f){
6564                     f.markInvalid(fieldError.msg);
6565                 }
6566             }
6567         }else{
6568             var field, id;
6569             for(id in errors){
6570                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6571                     field.markInvalid(errors[id]);
6572                 }
6573             }
6574         }
6575         //Roo.each(this.childForms || [], function (f) {
6576         //    f.markInvalid(errors);
6577         //});
6578         
6579         return this;
6580     },
6581
6582     /**
6583      * Set values for fields in this form in bulk.
6584      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6585      * @return {BasicForm} this
6586      */
6587     setValues : function(values){
6588         if(values instanceof Array){ // array of objects
6589             for(var i = 0, len = values.length; i < len; i++){
6590                 var v = values[i];
6591                 var f = this.findField(v.id);
6592                 if(f){
6593                     f.setValue(v.value);
6594                     if(this.trackResetOnLoad){
6595                         f.originalValue = f.getValue();
6596                     }
6597                 }
6598             }
6599         }else{ // object hash
6600             var field, id;
6601             for(id in values){
6602                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6603                     
6604                     if (field.setFromData && 
6605                         field.valueField && 
6606                         field.displayField &&
6607                         // combos' with local stores can 
6608                         // be queried via setValue()
6609                         // to set their value..
6610                         (field.store && !field.store.isLocal)
6611                         ) {
6612                         // it's a combo
6613                         var sd = { };
6614                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6615                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6616                         field.setFromData(sd);
6617                         
6618                     } else {
6619                         field.setValue(values[id]);
6620                     }
6621                     
6622                     
6623                     if(this.trackResetOnLoad){
6624                         field.originalValue = field.getValue();
6625                     }
6626                 }
6627             }
6628         }
6629          
6630         //Roo.each(this.childForms || [], function (f) {
6631         //    f.setValues(values);
6632         //});
6633                 
6634         return this;
6635     },
6636
6637     /**
6638      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6639      * they are returned as an array.
6640      * @param {Boolean} asString
6641      * @return {Object}
6642      */
6643     getValues : function(asString){
6644         //if (this.childForms) {
6645             // copy values from the child forms
6646         //    Roo.each(this.childForms, function (f) {
6647         //        this.setValues(f.getValues());
6648         //    }, this);
6649         //}
6650         
6651         
6652         
6653         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6654         if(asString === true){
6655             return fs;
6656         }
6657         return Roo.urlDecode(fs);
6658     },
6659     
6660     /**
6661      * Returns the fields in this form as an object with key/value pairs. 
6662      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6663      * @return {Object}
6664      */
6665     getFieldValues : function(with_hidden)
6666     {
6667         var items = this.getItems();
6668         var ret = {};
6669         items.each(function(f){
6670             if (!f.getName()) {
6671                 return;
6672             }
6673             var v = f.getValue();
6674             if (f.inputType =='radio') {
6675                 if (typeof(ret[f.getName()]) == 'undefined') {
6676                     ret[f.getName()] = ''; // empty..
6677                 }
6678                 
6679                 if (!f.el.dom.checked) {
6680                     return;
6681                     
6682                 }
6683                 v = f.el.dom.value;
6684                 
6685             }
6686             
6687             // not sure if this supported any more..
6688             if ((typeof(v) == 'object') && f.getRawValue) {
6689                 v = f.getRawValue() ; // dates..
6690             }
6691             // combo boxes where name != hiddenName...
6692             if (f.name != f.getName()) {
6693                 ret[f.name] = f.getRawValue();
6694             }
6695             ret[f.getName()] = v;
6696         });
6697         
6698         return ret;
6699     },
6700
6701     /**
6702      * Clears all invalid messages in this form.
6703      * @return {BasicForm} this
6704      */
6705     clearInvalid : function(){
6706         var items = this.getItems();
6707         
6708         items.each(function(f){
6709            f.clearInvalid();
6710         });
6711         
6712         
6713         
6714         return this;
6715     },
6716
6717     /**
6718      * Resets this form.
6719      * @return {BasicForm} this
6720      */
6721     reset : function(){
6722         var items = this.getItems();
6723         items.each(function(f){
6724             f.reset();
6725         });
6726         
6727         Roo.each(this.childForms || [], function (f) {
6728             f.reset();
6729         });
6730        
6731         
6732         return this;
6733     },
6734     getItems : function()
6735     {
6736         var r=new Roo.util.MixedCollection(false, function(o){
6737             return o.id || (o.id = Roo.id());
6738         });
6739         var iter = function(el) {
6740             if (el.inputEl) {
6741                 r.add(el);
6742             }
6743             if (!el.items) {
6744                 return;
6745             }
6746             Roo.each(el.items,function(e) {
6747                 iter(e);
6748             });
6749             
6750             
6751         };
6752         iter(this);
6753         return r;
6754         
6755         
6756         
6757         
6758     }
6759     
6760 });
6761
6762  
6763 /*
6764  * Based on:
6765  * Ext JS Library 1.1.1
6766  * Copyright(c) 2006-2007, Ext JS, LLC.
6767  *
6768  * Originally Released Under LGPL - original licence link has changed is not relivant.
6769  *
6770  * Fork - LGPL
6771  * <script type="text/javascript">
6772  */
6773 /**
6774  * @class Roo.form.VTypes
6775  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6776  * @singleton
6777  */
6778 Roo.form.VTypes = function(){
6779     // closure these in so they are only created once.
6780     var alpha = /^[a-zA-Z_]+$/;
6781     var alphanum = /^[a-zA-Z0-9_]+$/;
6782     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6783     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6784
6785     // All these messages and functions are configurable
6786     return {
6787         /**
6788          * The function used to validate email addresses
6789          * @param {String} value The email address
6790          */
6791         'email' : function(v){
6792             return email.test(v);
6793         },
6794         /**
6795          * The error text to display when the email validation function returns false
6796          * @type String
6797          */
6798         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6799         /**
6800          * The keystroke filter mask to be applied on email input
6801          * @type RegExp
6802          */
6803         'emailMask' : /[a-z0-9_\.\-@]/i,
6804
6805         /**
6806          * The function used to validate URLs
6807          * @param {String} value The URL
6808          */
6809         'url' : function(v){
6810             return url.test(v);
6811         },
6812         /**
6813          * The error text to display when the url validation function returns false
6814          * @type String
6815          */
6816         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6817         
6818         /**
6819          * The function used to validate alpha values
6820          * @param {String} value The value
6821          */
6822         'alpha' : function(v){
6823             return alpha.test(v);
6824         },
6825         /**
6826          * The error text to display when the alpha validation function returns false
6827          * @type String
6828          */
6829         'alphaText' : 'This field should only contain letters and _',
6830         /**
6831          * The keystroke filter mask to be applied on alpha input
6832          * @type RegExp
6833          */
6834         'alphaMask' : /[a-z_]/i,
6835
6836         /**
6837          * The function used to validate alphanumeric values
6838          * @param {String} value The value
6839          */
6840         'alphanum' : function(v){
6841             return alphanum.test(v);
6842         },
6843         /**
6844          * The error text to display when the alphanumeric validation function returns false
6845          * @type String
6846          */
6847         'alphanumText' : 'This field should only contain letters, numbers and _',
6848         /**
6849          * The keystroke filter mask to be applied on alphanumeric input
6850          * @type RegExp
6851          */
6852         'alphanumMask' : /[a-z0-9_]/i
6853     };
6854 }();/*
6855  * - LGPL
6856  *
6857  * Input
6858  * 
6859  */
6860
6861 /**
6862  * @class Roo.bootstrap.Input
6863  * @extends Roo.bootstrap.Component
6864  * Bootstrap Input class
6865  * @cfg {Boolean} disabled is it disabled
6866  * @cfg {String} fieldLabel - the label associated
6867  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6868  * @cfg {String} name name of the input
6869  * @cfg {string} fieldLabel - the label associated
6870  * @cfg {string}  inputType - input / file submit ...
6871  * @cfg {string} placeholder - placeholder to put in text.
6872  * @cfg {string}  before - input group add on before
6873  * @cfg {string} after - input group add on after
6874  * @cfg {string} size - (lg|sm) or leave empty..
6875  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6876  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6877  * @cfg {Number} md colspan out of 12 for computer-sized screens
6878  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6879  * @cfg {string} value default value of the input
6880  * @cfg {Number} labelWidth set the width of label (0-12)
6881  * @cfg {String} labelAlign (top|left)
6882  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6883  * @cfg {String} align (left|center|right) Default left
6884  * 
6885  * 
6886  * @constructor
6887  * Create a new Input
6888  * @param {Object} config The config object
6889  */
6890
6891 Roo.bootstrap.Input = function(config){
6892     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6893    
6894         this.addEvents({
6895             /**
6896              * @event focus
6897              * Fires when this field receives input focus.
6898              * @param {Roo.form.Field} this
6899              */
6900             focus : true,
6901             /**
6902              * @event blur
6903              * Fires when this field loses input focus.
6904              * @param {Roo.form.Field} this
6905              */
6906             blur : true,
6907             /**
6908              * @event specialkey
6909              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6910              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6911              * @param {Roo.form.Field} this
6912              * @param {Roo.EventObject} e The event object
6913              */
6914             specialkey : true,
6915             /**
6916              * @event change
6917              * Fires just before the field blurs if the field value has changed.
6918              * @param {Roo.form.Field} this
6919              * @param {Mixed} newValue The new value
6920              * @param {Mixed} oldValue The original value
6921              */
6922             change : true,
6923             /**
6924              * @event invalid
6925              * Fires after the field has been marked as invalid.
6926              * @param {Roo.form.Field} this
6927              * @param {String} msg The validation message
6928              */
6929             invalid : true,
6930             /**
6931              * @event valid
6932              * Fires after the field has been validated with no errors.
6933              * @param {Roo.form.Field} this
6934              */
6935             valid : true,
6936              /**
6937              * @event keyup
6938              * Fires after the key up
6939              * @param {Roo.form.Field} this
6940              * @param {Roo.EventObject}  e The event Object
6941              */
6942             keyup : true
6943         });
6944 };
6945
6946 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6947      /**
6948      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6949       automatic validation (defaults to "keyup").
6950      */
6951     validationEvent : "keyup",
6952      /**
6953      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6954      */
6955     validateOnBlur : true,
6956     /**
6957      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6958      */
6959     validationDelay : 250,
6960      /**
6961      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6962      */
6963     focusClass : "x-form-focus",  // not needed???
6964     
6965        
6966     /**
6967      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6968      */
6969     invalidClass : "has-error",
6970     
6971     /**
6972      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6973      */
6974     selectOnFocus : false,
6975     
6976      /**
6977      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6978      */
6979     maskRe : null,
6980        /**
6981      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6982      */
6983     vtype : null,
6984     
6985       /**
6986      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6987      */
6988     disableKeyFilter : false,
6989     
6990        /**
6991      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6992      */
6993     disabled : false,
6994      /**
6995      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6996      */
6997     allowBlank : true,
6998     /**
6999      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7000      */
7001     blankText : "This field is required",
7002     
7003      /**
7004      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7005      */
7006     minLength : 0,
7007     /**
7008      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7009      */
7010     maxLength : Number.MAX_VALUE,
7011     /**
7012      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7013      */
7014     minLengthText : "The minimum length for this field is {0}",
7015     /**
7016      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7017      */
7018     maxLengthText : "The maximum length for this field is {0}",
7019   
7020     
7021     /**
7022      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7023      * If available, this function will be called only after the basic validators all return true, and will be passed the
7024      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7025      */
7026     validator : null,
7027     /**
7028      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7029      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7030      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7031      */
7032     regex : null,
7033     /**
7034      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7035      */
7036     regexText : "",
7037     
7038     
7039     
7040     fieldLabel : '',
7041     inputType : 'text',
7042     
7043     name : false,
7044     placeholder: false,
7045     before : false,
7046     after : false,
7047     size : false,
7048     // private
7049     hasFocus : false,
7050     preventMark: false,
7051     isFormField : true,
7052     value : '',
7053     labelWidth : 2,
7054     labelAlign : false,
7055     readOnly : false,
7056     align : false,
7057     formatedValue : false,
7058     
7059     parentLabelAlign : function()
7060     {
7061         var parent = this;
7062         while (parent.parent()) {
7063             parent = parent.parent();
7064             if (typeof(parent.labelAlign) !='undefined') {
7065                 return parent.labelAlign;
7066             }
7067         }
7068         return 'left';
7069         
7070     },
7071     
7072     getAutoCreate : function(){
7073         
7074         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7075         
7076         var id = Roo.id();
7077         
7078         var cfg = {};
7079         
7080         if(this.inputType != 'hidden'){
7081             cfg.cls = 'form-group' //input-group
7082         }
7083         
7084         var input =  {
7085             tag: 'input',
7086             id : id,
7087             type : this.inputType,
7088             value : this.value,
7089             cls : 'form-control',
7090             placeholder : this.placeholder || ''
7091             
7092         };
7093         
7094         if(this.align){
7095             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7096         }
7097         
7098         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7099             input.maxLength = this.maxLength;
7100         }
7101         
7102         if (this.disabled) {
7103             input.disabled=true;
7104         }
7105         
7106         if (this.readOnly) {
7107             input.readonly=true;
7108         }
7109         
7110         if (this.name) {
7111             input.name = this.name;
7112         }
7113         if (this.size) {
7114             input.cls += ' input-' + this.size;
7115         }
7116         var settings=this;
7117         ['xs','sm','md','lg'].map(function(size){
7118             if (settings[size]) {
7119                 cfg.cls += ' col-' + size + '-' + settings[size];
7120             }
7121         });
7122         
7123         var inputblock = input;
7124         
7125         if (this.before || this.after) {
7126             
7127             inputblock = {
7128                 cls : 'input-group',
7129                 cn :  [] 
7130             };
7131             if (this.before && typeof(this.before) == 'string') {
7132                 
7133                 inputblock.cn.push({
7134                     tag :'span',
7135                     cls : 'roo-input-before input-group-addon',
7136                     html : this.before
7137                 });
7138             }
7139             if (this.before && typeof(this.before) == 'object') {
7140                 this.before = Roo.factory(this.before);
7141                 Roo.log(this.before);
7142                 inputblock.cn.push({
7143                     tag :'span',
7144                     cls : 'roo-input-before input-group-' +
7145                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7146                 });
7147             }
7148             
7149             inputblock.cn.push(input);
7150             
7151             if (this.after && typeof(this.after) == 'string') {
7152                 inputblock.cn.push({
7153                     tag :'span',
7154                     cls : 'roo-input-after input-group-addon',
7155                     html : this.after
7156                 });
7157             }
7158             if (this.after && typeof(this.after) == 'object') {
7159                 this.after = Roo.factory(this.after);
7160                 Roo.log(this.after);
7161                 inputblock.cn.push({
7162                     tag :'span',
7163                     cls : 'roo-input-after input-group-' +
7164                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7165                 });
7166             }
7167         };
7168         
7169         if (align ==='left' && this.fieldLabel.length) {
7170                 Roo.log("left and has label");
7171                 cfg.cn = [
7172                     
7173                     {
7174                         tag: 'label',
7175                         'for' :  id,
7176                         cls : 'control-label col-sm-' + this.labelWidth,
7177                         html : this.fieldLabel
7178                         
7179                     },
7180                     {
7181                         cls : "col-sm-" + (12 - this.labelWidth), 
7182                         cn: [
7183                             inputblock
7184                         ]
7185                     }
7186                     
7187                 ];
7188         } else if ( this.fieldLabel.length) {
7189                 Roo.log(" label");
7190                  cfg.cn = [
7191                    
7192                     {
7193                         tag: 'label',
7194                         //cls : 'input-group-addon',
7195                         html : this.fieldLabel
7196                         
7197                     },
7198                     
7199                     inputblock
7200                     
7201                 ];
7202
7203         } else {
7204             
7205                 Roo.log(" no label && no align");
7206                 cfg.cn = [
7207                     
7208                         inputblock
7209                     
7210                 ];
7211                 
7212                 
7213         };
7214         Roo.log('input-parentType: ' + this.parentType);
7215         
7216         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7217            cfg.cls += ' navbar-form';
7218            Roo.log(cfg);
7219         }
7220         
7221         return cfg;
7222         
7223     },
7224     /**
7225      * return the real input element.
7226      */
7227     inputEl: function ()
7228     {
7229         return this.el.select('input.form-control',true).first();
7230     },
7231     
7232     tooltipEl : function()
7233     {
7234         return this.inputEl();
7235     },
7236     
7237     setDisabled : function(v)
7238     {
7239         var i  = this.inputEl().dom;
7240         if (!v) {
7241             i.removeAttribute('disabled');
7242             return;
7243             
7244         }
7245         i.setAttribute('disabled','true');
7246     },
7247     initEvents : function()
7248     {
7249         Roo.bootstrap.Input.superclass.initEvents();
7250         
7251         this.inputEl().on("keydown" , this.fireKey,  this);
7252         this.inputEl().on("focus", this.onFocus,  this);
7253         this.inputEl().on("blur", this.onBlur,  this);
7254         
7255         this.inputEl().relayEvent('keyup', this);
7256
7257         // reference to original value for reset
7258         this.originalValue = this.getValue();
7259         //Roo.form.TextField.superclass.initEvents.call(this);
7260         if(this.validationEvent == 'keyup'){
7261             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7262             this.inputEl().on('keyup', this.filterValidation, this);
7263         }
7264         else if(this.validationEvent !== false){
7265             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7266         }
7267         
7268         if(this.selectOnFocus){
7269             this.on("focus", this.preFocus, this);
7270             
7271         }
7272         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7273             this.inputEl().on("keypress", this.filterKeys, this);
7274         }
7275        /* if(this.grow){
7276             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7277             this.el.on("click", this.autoSize,  this);
7278         }
7279         */
7280         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7281             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7282         }
7283         
7284         if (typeof(this.before) == 'object') {
7285             this.before.render(this.el.select('.roo-input-before',true).first());
7286         }
7287         if (typeof(this.after) == 'object') {
7288             this.after.render(this.el.select('.roo-input-after',true).first());
7289         }
7290         
7291         
7292     },
7293     filterValidation : function(e){
7294         if(!e.isNavKeyPress()){
7295             this.validationTask.delay(this.validationDelay);
7296         }
7297     },
7298      /**
7299      * Validates the field value
7300      * @return {Boolean} True if the value is valid, else false
7301      */
7302     validate : function(){
7303         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7304         if(this.disabled || this.validateValue(this.getRawValue())){
7305             this.clearInvalid();
7306             return true;
7307         }
7308         return false;
7309     },
7310     
7311     
7312     /**
7313      * Validates a value according to the field's validation rules and marks the field as invalid
7314      * if the validation fails
7315      * @param {Mixed} value The value to validate
7316      * @return {Boolean} True if the value is valid, else false
7317      */
7318     validateValue : function(value){
7319         if(value.length < 1)  { // if it's blank
7320              if(this.allowBlank){
7321                 this.clearInvalid();
7322                 return true;
7323              }else{
7324                 this.markInvalid(this.blankText);
7325                 return false;
7326              }
7327         }
7328         if(value.length < this.minLength){
7329             this.markInvalid(String.format(this.minLengthText, this.minLength));
7330             return false;
7331         }
7332         if(value.length > this.maxLength){
7333             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7334             return false;
7335         }
7336         if(this.vtype){
7337             var vt = Roo.form.VTypes;
7338             if(!vt[this.vtype](value, this)){
7339                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7340                 return false;
7341             }
7342         }
7343         if(typeof this.validator == "function"){
7344             var msg = this.validator(value);
7345             if(msg !== true){
7346                 this.markInvalid(msg);
7347                 return false;
7348             }
7349         }
7350         if(this.regex && !this.regex.test(value)){
7351             this.markInvalid(this.regexText);
7352             return false;
7353         }
7354         return true;
7355     },
7356
7357     
7358     
7359      // private
7360     fireKey : function(e){
7361         //Roo.log('field ' + e.getKey());
7362         if(e.isNavKeyPress()){
7363             this.fireEvent("specialkey", this, e);
7364         }
7365     },
7366     focus : function (selectText){
7367         if(this.rendered){
7368             this.inputEl().focus();
7369             if(selectText === true){
7370                 this.inputEl().dom.select();
7371             }
7372         }
7373         return this;
7374     } ,
7375     
7376     onFocus : function(){
7377         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7378            // this.el.addClass(this.focusClass);
7379         }
7380         if(!this.hasFocus){
7381             this.hasFocus = true;
7382             this.startValue = this.getValue();
7383             this.fireEvent("focus", this);
7384         }
7385     },
7386     
7387     beforeBlur : Roo.emptyFn,
7388
7389     
7390     // private
7391     onBlur : function(){
7392         this.beforeBlur();
7393         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7394             //this.el.removeClass(this.focusClass);
7395         }
7396         this.hasFocus = false;
7397         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7398             this.validate();
7399         }
7400         var v = this.getValue();
7401         if(String(v) !== String(this.startValue)){
7402             this.fireEvent('change', this, v, this.startValue);
7403         }
7404         this.fireEvent("blur", this);
7405     },
7406     
7407     /**
7408      * Resets the current field value to the originally loaded value and clears any validation messages
7409      */
7410     reset : function(){
7411         this.setValue(this.originalValue);
7412         this.clearInvalid();
7413     },
7414      /**
7415      * Returns the name of the field
7416      * @return {Mixed} name The name field
7417      */
7418     getName: function(){
7419         return this.name;
7420     },
7421      /**
7422      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7423      * @return {Mixed} value The field value
7424      */
7425     getValue : function(){
7426         
7427         var v = this.inputEl().getValue();
7428         
7429         return v;
7430     },
7431     /**
7432      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7433      * @return {Mixed} value The field value
7434      */
7435     getRawValue : function(){
7436         var v = this.inputEl().getValue();
7437         
7438         return v;
7439     },
7440     
7441     /**
7442      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7443      * @param {Mixed} value The value to set
7444      */
7445     setRawValue : function(v){
7446         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7447     },
7448     
7449     selectText : function(start, end){
7450         var v = this.getRawValue();
7451         if(v.length > 0){
7452             start = start === undefined ? 0 : start;
7453             end = end === undefined ? v.length : end;
7454             var d = this.inputEl().dom;
7455             if(d.setSelectionRange){
7456                 d.setSelectionRange(start, end);
7457             }else if(d.createTextRange){
7458                 var range = d.createTextRange();
7459                 range.moveStart("character", start);
7460                 range.moveEnd("character", v.length-end);
7461                 range.select();
7462             }
7463         }
7464     },
7465     
7466     /**
7467      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7468      * @param {Mixed} value The value to set
7469      */
7470     setValue : function(v){
7471         this.value = v;
7472         if(this.rendered){
7473             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7474             this.validate();
7475         }
7476     },
7477     
7478     /*
7479     processValue : function(value){
7480         if(this.stripCharsRe){
7481             var newValue = value.replace(this.stripCharsRe, '');
7482             if(newValue !== value){
7483                 this.setRawValue(newValue);
7484                 return newValue;
7485             }
7486         }
7487         return value;
7488     },
7489   */
7490     preFocus : function(){
7491         
7492         if(this.selectOnFocus){
7493             this.inputEl().dom.select();
7494         }
7495     },
7496     filterKeys : function(e){
7497         var k = e.getKey();
7498         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7499             return;
7500         }
7501         var c = e.getCharCode(), cc = String.fromCharCode(c);
7502         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7503             return;
7504         }
7505         if(!this.maskRe.test(cc)){
7506             e.stopEvent();
7507         }
7508     },
7509      /**
7510      * Clear any invalid styles/messages for this field
7511      */
7512     clearInvalid : function(){
7513         
7514         if(!this.el || this.preventMark){ // not rendered
7515             return;
7516         }
7517         this.el.removeClass(this.invalidClass);
7518         /*
7519         switch(this.msgTarget){
7520             case 'qtip':
7521                 this.el.dom.qtip = '';
7522                 break;
7523             case 'title':
7524                 this.el.dom.title = '';
7525                 break;
7526             case 'under':
7527                 if(this.errorEl){
7528                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7529                 }
7530                 break;
7531             case 'side':
7532                 if(this.errorIcon){
7533                     this.errorIcon.dom.qtip = '';
7534                     this.errorIcon.hide();
7535                     this.un('resize', this.alignErrorIcon, this);
7536                 }
7537                 break;
7538             default:
7539                 var t = Roo.getDom(this.msgTarget);
7540                 t.innerHTML = '';
7541                 t.style.display = 'none';
7542                 break;
7543         }
7544         */
7545         this.fireEvent('valid', this);
7546     },
7547      /**
7548      * Mark this field as invalid
7549      * @param {String} msg The validation message
7550      */
7551     markInvalid : function(msg){
7552         if(!this.el  || this.preventMark){ // not rendered
7553             return;
7554         }
7555         this.el.addClass(this.invalidClass);
7556         /*
7557         msg = msg || this.invalidText;
7558         switch(this.msgTarget){
7559             case 'qtip':
7560                 this.el.dom.qtip = msg;
7561                 this.el.dom.qclass = 'x-form-invalid-tip';
7562                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7563                     Roo.QuickTips.enable();
7564                 }
7565                 break;
7566             case 'title':
7567                 this.el.dom.title = msg;
7568                 break;
7569             case 'under':
7570                 if(!this.errorEl){
7571                     var elp = this.el.findParent('.x-form-element', 5, true);
7572                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7573                     this.errorEl.setWidth(elp.getWidth(true)-20);
7574                 }
7575                 this.errorEl.update(msg);
7576                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7577                 break;
7578             case 'side':
7579                 if(!this.errorIcon){
7580                     var elp = this.el.findParent('.x-form-element', 5, true);
7581                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7582                 }
7583                 this.alignErrorIcon();
7584                 this.errorIcon.dom.qtip = msg;
7585                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7586                 this.errorIcon.show();
7587                 this.on('resize', this.alignErrorIcon, this);
7588                 break;
7589             default:
7590                 var t = Roo.getDom(this.msgTarget);
7591                 t.innerHTML = msg;
7592                 t.style.display = this.msgDisplay;
7593                 break;
7594         }
7595         */
7596         this.fireEvent('invalid', this, msg);
7597     },
7598     // private
7599     SafariOnKeyDown : function(event)
7600     {
7601         // this is a workaround for a password hang bug on chrome/ webkit.
7602         
7603         var isSelectAll = false;
7604         
7605         if(this.inputEl().dom.selectionEnd > 0){
7606             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7607         }
7608         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7609             event.preventDefault();
7610             this.setValue('');
7611             return;
7612         }
7613         
7614         if(isSelectAll){ // backspace and delete key
7615             
7616             event.preventDefault();
7617             // this is very hacky as keydown always get's upper case.
7618             //
7619             var cc = String.fromCharCode(event.getCharCode());
7620             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7621             
7622         }
7623     },
7624     adjustWidth : function(tag, w){
7625         tag = tag.toLowerCase();
7626         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7627             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7628                 if(tag == 'input'){
7629                     return w + 2;
7630                 }
7631                 if(tag == 'textarea'){
7632                     return w-2;
7633                 }
7634             }else if(Roo.isOpera){
7635                 if(tag == 'input'){
7636                     return w + 2;
7637                 }
7638                 if(tag == 'textarea'){
7639                     return w-2;
7640                 }
7641             }
7642         }
7643         return w;
7644     }
7645     
7646 });
7647
7648  
7649 /*
7650  * - LGPL
7651  *
7652  * Input
7653  * 
7654  */
7655
7656 /**
7657  * @class Roo.bootstrap.TextArea
7658  * @extends Roo.bootstrap.Input
7659  * Bootstrap TextArea class
7660  * @cfg {Number} cols Specifies the visible width of a text area
7661  * @cfg {Number} rows Specifies the visible number of lines in a text area
7662  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7663  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7664  * @cfg {string} html text
7665  * 
7666  * @constructor
7667  * Create a new TextArea
7668  * @param {Object} config The config object
7669  */
7670
7671 Roo.bootstrap.TextArea = function(config){
7672     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7673    
7674 };
7675
7676 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7677      
7678     cols : false,
7679     rows : 5,
7680     readOnly : false,
7681     warp : 'soft',
7682     resize : false,
7683     value: false,
7684     html: false,
7685     
7686     getAutoCreate : function(){
7687         
7688         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7689         
7690         var id = Roo.id();
7691         
7692         var cfg = {};
7693         
7694         var input =  {
7695             tag: 'textarea',
7696             id : id,
7697             warp : this.warp,
7698             rows : this.rows,
7699             value : this.value || '',
7700             html: this.html || '',
7701             cls : 'form-control',
7702             placeholder : this.placeholder || '' 
7703             
7704         };
7705         
7706         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7707             input.maxLength = this.maxLength;
7708         }
7709         
7710         if(this.resize){
7711             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7712         }
7713         
7714         if(this.cols){
7715             input.cols = this.cols;
7716         }
7717         
7718         if (this.readOnly) {
7719             input.readonly = true;
7720         }
7721         
7722         if (this.name) {
7723             input.name = this.name;
7724         }
7725         
7726         if (this.size) {
7727             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7728         }
7729         
7730         var settings=this;
7731         ['xs','sm','md','lg'].map(function(size){
7732             if (settings[size]) {
7733                 cfg.cls += ' col-' + size + '-' + settings[size];
7734             }
7735         });
7736         
7737         var inputblock = input;
7738         
7739         if (this.before || this.after) {
7740             
7741             inputblock = {
7742                 cls : 'input-group',
7743                 cn :  [] 
7744             };
7745             if (this.before) {
7746                 inputblock.cn.push({
7747                     tag :'span',
7748                     cls : 'input-group-addon',
7749                     html : this.before
7750                 });
7751             }
7752             inputblock.cn.push(input);
7753             if (this.after) {
7754                 inputblock.cn.push({
7755                     tag :'span',
7756                     cls : 'input-group-addon',
7757                     html : this.after
7758                 });
7759             }
7760             
7761         }
7762         
7763         if (align ==='left' && this.fieldLabel.length) {
7764                 Roo.log("left and has label");
7765                 cfg.cn = [
7766                     
7767                     {
7768                         tag: 'label',
7769                         'for' :  id,
7770                         cls : 'control-label col-sm-' + this.labelWidth,
7771                         html : this.fieldLabel
7772                         
7773                     },
7774                     {
7775                         cls : "col-sm-" + (12 - this.labelWidth), 
7776                         cn: [
7777                             inputblock
7778                         ]
7779                     }
7780                     
7781                 ];
7782         } else if ( this.fieldLabel.length) {
7783                 Roo.log(" label");
7784                  cfg.cn = [
7785                    
7786                     {
7787                         tag: 'label',
7788                         //cls : 'input-group-addon',
7789                         html : this.fieldLabel
7790                         
7791                     },
7792                     
7793                     inputblock
7794                     
7795                 ];
7796
7797         } else {
7798             
7799                    Roo.log(" no label && no align");
7800                 cfg.cn = [
7801                     
7802                         inputblock
7803                     
7804                 ];
7805                 
7806                 
7807         }
7808         
7809         if (this.disabled) {
7810             input.disabled=true;
7811         }
7812         
7813         return cfg;
7814         
7815     },
7816     /**
7817      * return the real textarea element.
7818      */
7819     inputEl: function ()
7820     {
7821         return this.el.select('textarea.form-control',true).first();
7822     }
7823 });
7824
7825  
7826 /*
7827  * - LGPL
7828  *
7829  * trigger field - base class for combo..
7830  * 
7831  */
7832  
7833 /**
7834  * @class Roo.bootstrap.TriggerField
7835  * @extends Roo.bootstrap.Input
7836  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7837  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7838  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7839  * for which you can provide a custom implementation.  For example:
7840  * <pre><code>
7841 var trigger = new Roo.bootstrap.TriggerField();
7842 trigger.onTriggerClick = myTriggerFn;
7843 trigger.applyTo('my-field');
7844 </code></pre>
7845  *
7846  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7847  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7848  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7849  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7850  * @constructor
7851  * Create a new TriggerField.
7852  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7853  * to the base TextField)
7854  */
7855 Roo.bootstrap.TriggerField = function(config){
7856     this.mimicing = false;
7857     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7858 };
7859
7860 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7861     /**
7862      * @cfg {String} triggerClass A CSS class to apply to the trigger
7863      */
7864      /**
7865      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7866      */
7867     hideTrigger:false,
7868
7869     /** @cfg {Boolean} grow @hide */
7870     /** @cfg {Number} growMin @hide */
7871     /** @cfg {Number} growMax @hide */
7872
7873     /**
7874      * @hide 
7875      * @method
7876      */
7877     autoSize: Roo.emptyFn,
7878     // private
7879     monitorTab : true,
7880     // private
7881     deferHeight : true,
7882
7883     
7884     actionMode : 'wrap',
7885     
7886     
7887     
7888     getAutoCreate : function(){
7889        
7890         var align = this.labelAlign || this.parentLabelAlign();
7891         
7892         var id = Roo.id();
7893         
7894         var cfg = {
7895             cls: 'form-group' //input-group
7896         };
7897         
7898         
7899         var input =  {
7900             tag: 'input',
7901             id : id,
7902             type : this.inputType,
7903             cls : 'form-control',
7904             autocomplete: 'off',
7905             placeholder : this.placeholder || '' 
7906             
7907         };
7908         if (this.name) {
7909             input.name = this.name;
7910         }
7911         if (this.size) {
7912             input.cls += ' input-' + this.size;
7913         }
7914         
7915         if (this.disabled) {
7916             input.disabled=true;
7917         }
7918         
7919         var inputblock = input;
7920         
7921         if (this.before || this.after) {
7922             
7923             inputblock = {
7924                 cls : 'input-group',
7925                 cn :  [] 
7926             };
7927             if (this.before) {
7928                 inputblock.cn.push({
7929                     tag :'span',
7930                     cls : 'input-group-addon',
7931                     html : this.before
7932                 });
7933             }
7934             inputblock.cn.push(input);
7935             if (this.after) {
7936                 inputblock.cn.push({
7937                     tag :'span',
7938                     cls : 'input-group-addon',
7939                     html : this.after
7940                 });
7941             }
7942             
7943         };
7944         
7945         var box = {
7946             tag: 'div',
7947             cn: [
7948                 {
7949                     tag: 'input',
7950                     type : 'hidden',
7951                     cls: 'form-hidden-field'
7952                 },
7953                 inputblock
7954             ]
7955             
7956         };
7957         
7958         if(this.multiple){
7959             Roo.log('multiple');
7960             
7961             box = {
7962                 tag: 'div',
7963                 cn: [
7964                     {
7965                         tag: 'input',
7966                         type : 'hidden',
7967                         cls: 'form-hidden-field'
7968                     },
7969                     {
7970                         tag: 'ul',
7971                         cls: 'select2-choices',
7972                         cn:[
7973                             {
7974                                 tag: 'li',
7975                                 cls: 'select2-search-field',
7976                                 cn: [
7977
7978                                     inputblock
7979                                 ]
7980                             }
7981                         ]
7982                     }
7983                 ]
7984             }
7985         };
7986         
7987         var combobox = {
7988             cls: 'select2-container input-group',
7989             cn: [
7990                 box
7991 //                {
7992 //                    tag: 'ul',
7993 //                    cls: 'typeahead typeahead-long dropdown-menu',
7994 //                    style: 'display:none'
7995 //                }
7996             ]
7997         };
7998         
7999         if(!this.multiple && this.showToggleBtn){
8000             combobox.cn.push({
8001                 tag :'span',
8002                 cls : 'input-group-addon btn dropdown-toggle',
8003                 cn : [
8004                     {
8005                         tag: 'span',
8006                         cls: 'caret'
8007                     },
8008                     {
8009                         tag: 'span',
8010                         cls: 'combobox-clear',
8011                         cn  : [
8012                             {
8013                                 tag : 'i',
8014                                 cls: 'icon-remove'
8015                             }
8016                         ]
8017                     }
8018                 ]
8019
8020             })
8021         }
8022         
8023         if(this.multiple){
8024             combobox.cls += ' select2-container-multi';
8025         }
8026         
8027         if (align ==='left' && this.fieldLabel.length) {
8028             
8029                 Roo.log("left and has label");
8030                 cfg.cn = [
8031                     
8032                     {
8033                         tag: 'label',
8034                         'for' :  id,
8035                         cls : 'control-label col-sm-' + this.labelWidth,
8036                         html : this.fieldLabel
8037                         
8038                     },
8039                     {
8040                         cls : "col-sm-" + (12 - this.labelWidth), 
8041                         cn: [
8042                             combobox
8043                         ]
8044                     }
8045                     
8046                 ];
8047         } else if ( this.fieldLabel.length) {
8048                 Roo.log(" label");
8049                  cfg.cn = [
8050                    
8051                     {
8052                         tag: 'label',
8053                         //cls : 'input-group-addon',
8054                         html : this.fieldLabel
8055                         
8056                     },
8057                     
8058                     combobox
8059                     
8060                 ];
8061
8062         } else {
8063             
8064                 Roo.log(" no label && no align");
8065                 cfg = combobox
8066                      
8067                 
8068         }
8069          
8070         var settings=this;
8071         ['xs','sm','md','lg'].map(function(size){
8072             if (settings[size]) {
8073                 cfg.cls += ' col-' + size + '-' + settings[size];
8074             }
8075         });
8076         
8077         return cfg;
8078         
8079     },
8080     
8081     
8082     
8083     // private
8084     onResize : function(w, h){
8085 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8086 //        if(typeof w == 'number'){
8087 //            var x = w - this.trigger.getWidth();
8088 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8089 //            this.trigger.setStyle('left', x+'px');
8090 //        }
8091     },
8092
8093     // private
8094     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8095
8096     // private
8097     getResizeEl : function(){
8098         return this.inputEl();
8099     },
8100
8101     // private
8102     getPositionEl : function(){
8103         return this.inputEl();
8104     },
8105
8106     // private
8107     alignErrorIcon : function(){
8108         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8109     },
8110
8111     // private
8112     initEvents : function(){
8113         
8114         this.createList();
8115         
8116         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8117         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8118         if(!this.multiple && this.showToggleBtn){
8119             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8120             if(this.hideTrigger){
8121                 this.trigger.setDisplayed(false);
8122             }
8123             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8124         }
8125         
8126         if(this.multiple){
8127             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8128         }
8129         
8130         //this.trigger.addClassOnOver('x-form-trigger-over');
8131         //this.trigger.addClassOnClick('x-form-trigger-click');
8132         
8133         //if(!this.width){
8134         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8135         //}
8136     },
8137     
8138     createList : function()
8139     {
8140         this.list = Roo.get(document.body).createChild({
8141             tag: 'ul',
8142             cls: 'typeahead typeahead-long dropdown-menu',
8143             style: 'display:none'
8144         });
8145         
8146         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8147         
8148     },
8149
8150     // private
8151     initTrigger : function(){
8152        
8153     },
8154
8155     // private
8156     onDestroy : function(){
8157         if(this.trigger){
8158             this.trigger.removeAllListeners();
8159           //  this.trigger.remove();
8160         }
8161         //if(this.wrap){
8162         //    this.wrap.remove();
8163         //}
8164         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8165     },
8166
8167     // private
8168     onFocus : function(){
8169         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8170         /*
8171         if(!this.mimicing){
8172             this.wrap.addClass('x-trigger-wrap-focus');
8173             this.mimicing = true;
8174             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8175             if(this.monitorTab){
8176                 this.el.on("keydown", this.checkTab, this);
8177             }
8178         }
8179         */
8180     },
8181
8182     // private
8183     checkTab : function(e){
8184         if(e.getKey() == e.TAB){
8185             this.triggerBlur();
8186         }
8187     },
8188
8189     // private
8190     onBlur : function(){
8191         // do nothing
8192     },
8193
8194     // private
8195     mimicBlur : function(e, t){
8196         /*
8197         if(!this.wrap.contains(t) && this.validateBlur()){
8198             this.triggerBlur();
8199         }
8200         */
8201     },
8202
8203     // private
8204     triggerBlur : function(){
8205         this.mimicing = false;
8206         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8207         if(this.monitorTab){
8208             this.el.un("keydown", this.checkTab, this);
8209         }
8210         //this.wrap.removeClass('x-trigger-wrap-focus');
8211         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8212     },
8213
8214     // private
8215     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8216     validateBlur : function(e, t){
8217         return true;
8218     },
8219
8220     // private
8221     onDisable : function(){
8222         this.inputEl().dom.disabled = true;
8223         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8224         //if(this.wrap){
8225         //    this.wrap.addClass('x-item-disabled');
8226         //}
8227     },
8228
8229     // private
8230     onEnable : function(){
8231         this.inputEl().dom.disabled = false;
8232         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8233         //if(this.wrap){
8234         //    this.el.removeClass('x-item-disabled');
8235         //}
8236     },
8237
8238     // private
8239     onShow : function(){
8240         var ae = this.getActionEl();
8241         
8242         if(ae){
8243             ae.dom.style.display = '';
8244             ae.dom.style.visibility = 'visible';
8245         }
8246     },
8247
8248     // private
8249     
8250     onHide : function(){
8251         var ae = this.getActionEl();
8252         ae.dom.style.display = 'none';
8253     },
8254
8255     /**
8256      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8257      * by an implementing function.
8258      * @method
8259      * @param {EventObject} e
8260      */
8261     onTriggerClick : Roo.emptyFn
8262 });
8263  /*
8264  * Based on:
8265  * Ext JS Library 1.1.1
8266  * Copyright(c) 2006-2007, Ext JS, LLC.
8267  *
8268  * Originally Released Under LGPL - original licence link has changed is not relivant.
8269  *
8270  * Fork - LGPL
8271  * <script type="text/javascript">
8272  */
8273
8274
8275 /**
8276  * @class Roo.data.SortTypes
8277  * @singleton
8278  * Defines the default sorting (casting?) comparison functions used when sorting data.
8279  */
8280 Roo.data.SortTypes = {
8281     /**
8282      * Default sort that does nothing
8283      * @param {Mixed} s The value being converted
8284      * @return {Mixed} The comparison value
8285      */
8286     none : function(s){
8287         return s;
8288     },
8289     
8290     /**
8291      * The regular expression used to strip tags
8292      * @type {RegExp}
8293      * @property
8294      */
8295     stripTagsRE : /<\/?[^>]+>/gi,
8296     
8297     /**
8298      * Strips all HTML tags to sort on text only
8299      * @param {Mixed} s The value being converted
8300      * @return {String} The comparison value
8301      */
8302     asText : function(s){
8303         return String(s).replace(this.stripTagsRE, "");
8304     },
8305     
8306     /**
8307      * Strips all HTML tags to sort on text only - Case insensitive
8308      * @param {Mixed} s The value being converted
8309      * @return {String} The comparison value
8310      */
8311     asUCText : function(s){
8312         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8313     },
8314     
8315     /**
8316      * Case insensitive string
8317      * @param {Mixed} s The value being converted
8318      * @return {String} The comparison value
8319      */
8320     asUCString : function(s) {
8321         return String(s).toUpperCase();
8322     },
8323     
8324     /**
8325      * Date sorting
8326      * @param {Mixed} s The value being converted
8327      * @return {Number} The comparison value
8328      */
8329     asDate : function(s) {
8330         if(!s){
8331             return 0;
8332         }
8333         if(s instanceof Date){
8334             return s.getTime();
8335         }
8336         return Date.parse(String(s));
8337     },
8338     
8339     /**
8340      * Float sorting
8341      * @param {Mixed} s The value being converted
8342      * @return {Float} The comparison value
8343      */
8344     asFloat : function(s) {
8345         var val = parseFloat(String(s).replace(/,/g, ""));
8346         if(isNaN(val)) val = 0;
8347         return val;
8348     },
8349     
8350     /**
8351      * Integer sorting
8352      * @param {Mixed} s The value being converted
8353      * @return {Number} The comparison value
8354      */
8355     asInt : function(s) {
8356         var val = parseInt(String(s).replace(/,/g, ""));
8357         if(isNaN(val)) val = 0;
8358         return val;
8359     }
8360 };/*
8361  * Based on:
8362  * Ext JS Library 1.1.1
8363  * Copyright(c) 2006-2007, Ext JS, LLC.
8364  *
8365  * Originally Released Under LGPL - original licence link has changed is not relivant.
8366  *
8367  * Fork - LGPL
8368  * <script type="text/javascript">
8369  */
8370
8371 /**
8372 * @class Roo.data.Record
8373  * Instances of this class encapsulate both record <em>definition</em> information, and record
8374  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8375  * to access Records cached in an {@link Roo.data.Store} object.<br>
8376  * <p>
8377  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8378  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8379  * objects.<br>
8380  * <p>
8381  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8382  * @constructor
8383  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8384  * {@link #create}. The parameters are the same.
8385  * @param {Array} data An associative Array of data values keyed by the field name.
8386  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8387  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8388  * not specified an integer id is generated.
8389  */
8390 Roo.data.Record = function(data, id){
8391     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8392     this.data = data;
8393 };
8394
8395 /**
8396  * Generate a constructor for a specific record layout.
8397  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8398  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8399  * Each field definition object may contain the following properties: <ul>
8400  * <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,
8401  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8402  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8403  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8404  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8405  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8406  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8407  * this may be omitted.</p></li>
8408  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8409  * <ul><li>auto (Default, implies no conversion)</li>
8410  * <li>string</li>
8411  * <li>int</li>
8412  * <li>float</li>
8413  * <li>boolean</li>
8414  * <li>date</li></ul></p></li>
8415  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8416  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8417  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8418  * by the Reader into an object that will be stored in the Record. It is passed the
8419  * following parameters:<ul>
8420  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8421  * </ul></p></li>
8422  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8423  * </ul>
8424  * <br>usage:<br><pre><code>
8425 var TopicRecord = Roo.data.Record.create(
8426     {name: 'title', mapping: 'topic_title'},
8427     {name: 'author', mapping: 'username'},
8428     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8429     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8430     {name: 'lastPoster', mapping: 'user2'},
8431     {name: 'excerpt', mapping: 'post_text'}
8432 );
8433
8434 var myNewRecord = new TopicRecord({
8435     title: 'Do my job please',
8436     author: 'noobie',
8437     totalPosts: 1,
8438     lastPost: new Date(),
8439     lastPoster: 'Animal',
8440     excerpt: 'No way dude!'
8441 });
8442 myStore.add(myNewRecord);
8443 </code></pre>
8444  * @method create
8445  * @static
8446  */
8447 Roo.data.Record.create = function(o){
8448     var f = function(){
8449         f.superclass.constructor.apply(this, arguments);
8450     };
8451     Roo.extend(f, Roo.data.Record);
8452     var p = f.prototype;
8453     p.fields = new Roo.util.MixedCollection(false, function(field){
8454         return field.name;
8455     });
8456     for(var i = 0, len = o.length; i < len; i++){
8457         p.fields.add(new Roo.data.Field(o[i]));
8458     }
8459     f.getField = function(name){
8460         return p.fields.get(name);  
8461     };
8462     return f;
8463 };
8464
8465 Roo.data.Record.AUTO_ID = 1000;
8466 Roo.data.Record.EDIT = 'edit';
8467 Roo.data.Record.REJECT = 'reject';
8468 Roo.data.Record.COMMIT = 'commit';
8469
8470 Roo.data.Record.prototype = {
8471     /**
8472      * Readonly flag - true if this record has been modified.
8473      * @type Boolean
8474      */
8475     dirty : false,
8476     editing : false,
8477     error: null,
8478     modified: null,
8479
8480     // private
8481     join : function(store){
8482         this.store = store;
8483     },
8484
8485     /**
8486      * Set the named field to the specified value.
8487      * @param {String} name The name of the field to set.
8488      * @param {Object} value The value to set the field to.
8489      */
8490     set : function(name, value){
8491         if(this.data[name] == value){
8492             return;
8493         }
8494         this.dirty = true;
8495         if(!this.modified){
8496             this.modified = {};
8497         }
8498         if(typeof this.modified[name] == 'undefined'){
8499             this.modified[name] = this.data[name];
8500         }
8501         this.data[name] = value;
8502         if(!this.editing && this.store){
8503             this.store.afterEdit(this);
8504         }       
8505     },
8506
8507     /**
8508      * Get the value of the named field.
8509      * @param {String} name The name of the field to get the value of.
8510      * @return {Object} The value of the field.
8511      */
8512     get : function(name){
8513         return this.data[name]; 
8514     },
8515
8516     // private
8517     beginEdit : function(){
8518         this.editing = true;
8519         this.modified = {}; 
8520     },
8521
8522     // private
8523     cancelEdit : function(){
8524         this.editing = false;
8525         delete this.modified;
8526     },
8527
8528     // private
8529     endEdit : function(){
8530         this.editing = false;
8531         if(this.dirty && this.store){
8532             this.store.afterEdit(this);
8533         }
8534     },
8535
8536     /**
8537      * Usually called by the {@link Roo.data.Store} which owns the Record.
8538      * Rejects all changes made to the Record since either creation, or the last commit operation.
8539      * Modified fields are reverted to their original values.
8540      * <p>
8541      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8542      * of reject operations.
8543      */
8544     reject : function(){
8545         var m = this.modified;
8546         for(var n in m){
8547             if(typeof m[n] != "function"){
8548                 this.data[n] = m[n];
8549             }
8550         }
8551         this.dirty = false;
8552         delete this.modified;
8553         this.editing = false;
8554         if(this.store){
8555             this.store.afterReject(this);
8556         }
8557     },
8558
8559     /**
8560      * Usually called by the {@link Roo.data.Store} which owns the Record.
8561      * Commits all changes made to the Record since either creation, or the last commit operation.
8562      * <p>
8563      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8564      * of commit operations.
8565      */
8566     commit : function(){
8567         this.dirty = false;
8568         delete this.modified;
8569         this.editing = false;
8570         if(this.store){
8571             this.store.afterCommit(this);
8572         }
8573     },
8574
8575     // private
8576     hasError : function(){
8577         return this.error != null;
8578     },
8579
8580     // private
8581     clearError : function(){
8582         this.error = null;
8583     },
8584
8585     /**
8586      * Creates a copy of this record.
8587      * @param {String} id (optional) A new record id if you don't want to use this record's id
8588      * @return {Record}
8589      */
8590     copy : function(newId) {
8591         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8592     }
8593 };/*
8594  * Based on:
8595  * Ext JS Library 1.1.1
8596  * Copyright(c) 2006-2007, Ext JS, LLC.
8597  *
8598  * Originally Released Under LGPL - original licence link has changed is not relivant.
8599  *
8600  * Fork - LGPL
8601  * <script type="text/javascript">
8602  */
8603
8604
8605
8606 /**
8607  * @class Roo.data.Store
8608  * @extends Roo.util.Observable
8609  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8610  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8611  * <p>
8612  * 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
8613  * has no knowledge of the format of the data returned by the Proxy.<br>
8614  * <p>
8615  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8616  * instances from the data object. These records are cached and made available through accessor functions.
8617  * @constructor
8618  * Creates a new Store.
8619  * @param {Object} config A config object containing the objects needed for the Store to access data,
8620  * and read the data into Records.
8621  */
8622 Roo.data.Store = function(config){
8623     this.data = new Roo.util.MixedCollection(false);
8624     this.data.getKey = function(o){
8625         return o.id;
8626     };
8627     this.baseParams = {};
8628     // private
8629     this.paramNames = {
8630         "start" : "start",
8631         "limit" : "limit",
8632         "sort" : "sort",
8633         "dir" : "dir",
8634         "multisort" : "_multisort"
8635     };
8636
8637     if(config && config.data){
8638         this.inlineData = config.data;
8639         delete config.data;
8640     }
8641
8642     Roo.apply(this, config);
8643     
8644     if(this.reader){ // reader passed
8645         this.reader = Roo.factory(this.reader, Roo.data);
8646         this.reader.xmodule = this.xmodule || false;
8647         if(!this.recordType){
8648             this.recordType = this.reader.recordType;
8649         }
8650         if(this.reader.onMetaChange){
8651             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8652         }
8653     }
8654
8655     if(this.recordType){
8656         this.fields = this.recordType.prototype.fields;
8657     }
8658     this.modified = [];
8659
8660     this.addEvents({
8661         /**
8662          * @event datachanged
8663          * Fires when the data cache has changed, and a widget which is using this Store
8664          * as a Record cache should refresh its view.
8665          * @param {Store} this
8666          */
8667         datachanged : true,
8668         /**
8669          * @event metachange
8670          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8671          * @param {Store} this
8672          * @param {Object} meta The JSON metadata
8673          */
8674         metachange : true,
8675         /**
8676          * @event add
8677          * Fires when Records have been added to the Store
8678          * @param {Store} this
8679          * @param {Roo.data.Record[]} records The array of Records added
8680          * @param {Number} index The index at which the record(s) were added
8681          */
8682         add : true,
8683         /**
8684          * @event remove
8685          * Fires when a Record has been removed from the Store
8686          * @param {Store} this
8687          * @param {Roo.data.Record} record The Record that was removed
8688          * @param {Number} index The index at which the record was removed
8689          */
8690         remove : true,
8691         /**
8692          * @event update
8693          * Fires when a Record has been updated
8694          * @param {Store} this
8695          * @param {Roo.data.Record} record The Record that was updated
8696          * @param {String} operation The update operation being performed.  Value may be one of:
8697          * <pre><code>
8698  Roo.data.Record.EDIT
8699  Roo.data.Record.REJECT
8700  Roo.data.Record.COMMIT
8701          * </code></pre>
8702          */
8703         update : true,
8704         /**
8705          * @event clear
8706          * Fires when the data cache has been cleared.
8707          * @param {Store} this
8708          */
8709         clear : true,
8710         /**
8711          * @event beforeload
8712          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8713          * the load action will be canceled.
8714          * @param {Store} this
8715          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8716          */
8717         beforeload : true,
8718         /**
8719          * @event beforeloadadd
8720          * Fires after a new set of Records has been loaded.
8721          * @param {Store} this
8722          * @param {Roo.data.Record[]} records The Records that were loaded
8723          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8724          */
8725         beforeloadadd : true,
8726         /**
8727          * @event load
8728          * Fires after a new set of Records has been loaded, before they are added to the store.
8729          * @param {Store} this
8730          * @param {Roo.data.Record[]} records The Records that were loaded
8731          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8732          * @params {Object} return from reader
8733          */
8734         load : true,
8735         /**
8736          * @event loadexception
8737          * Fires if an exception occurs in the Proxy during loading.
8738          * Called with the signature of the Proxy's "loadexception" event.
8739          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8740          * 
8741          * @param {Proxy} 
8742          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8743          * @param {Object} load options 
8744          * @param {Object} jsonData from your request (normally this contains the Exception)
8745          */
8746         loadexception : true
8747     });
8748     
8749     if(this.proxy){
8750         this.proxy = Roo.factory(this.proxy, Roo.data);
8751         this.proxy.xmodule = this.xmodule || false;
8752         this.relayEvents(this.proxy,  ["loadexception"]);
8753     }
8754     this.sortToggle = {};
8755     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8756
8757     Roo.data.Store.superclass.constructor.call(this);
8758
8759     if(this.inlineData){
8760         this.loadData(this.inlineData);
8761         delete this.inlineData;
8762     }
8763 };
8764
8765 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8766      /**
8767     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8768     * without a remote query - used by combo/forms at present.
8769     */
8770     
8771     /**
8772     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8773     */
8774     /**
8775     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8776     */
8777     /**
8778     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8779     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8780     */
8781     /**
8782     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8783     * on any HTTP request
8784     */
8785     /**
8786     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8787     */
8788     /**
8789     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8790     */
8791     multiSort: false,
8792     /**
8793     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8794     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8795     */
8796     remoteSort : false,
8797
8798     /**
8799     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8800      * loaded or when a record is removed. (defaults to false).
8801     */
8802     pruneModifiedRecords : false,
8803
8804     // private
8805     lastOptions : null,
8806
8807     /**
8808      * Add Records to the Store and fires the add event.
8809      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8810      */
8811     add : function(records){
8812         records = [].concat(records);
8813         for(var i = 0, len = records.length; i < len; i++){
8814             records[i].join(this);
8815         }
8816         var index = this.data.length;
8817         this.data.addAll(records);
8818         this.fireEvent("add", this, records, index);
8819     },
8820
8821     /**
8822      * Remove a Record from the Store and fires the remove event.
8823      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8824      */
8825     remove : function(record){
8826         var index = this.data.indexOf(record);
8827         this.data.removeAt(index);
8828         if(this.pruneModifiedRecords){
8829             this.modified.remove(record);
8830         }
8831         this.fireEvent("remove", this, record, index);
8832     },
8833
8834     /**
8835      * Remove all Records from the Store and fires the clear event.
8836      */
8837     removeAll : function(){
8838         this.data.clear();
8839         if(this.pruneModifiedRecords){
8840             this.modified = [];
8841         }
8842         this.fireEvent("clear", this);
8843     },
8844
8845     /**
8846      * Inserts Records to the Store at the given index and fires the add event.
8847      * @param {Number} index The start index at which to insert the passed Records.
8848      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8849      */
8850     insert : function(index, records){
8851         records = [].concat(records);
8852         for(var i = 0, len = records.length; i < len; i++){
8853             this.data.insert(index, records[i]);
8854             records[i].join(this);
8855         }
8856         this.fireEvent("add", this, records, index);
8857     },
8858
8859     /**
8860      * Get the index within the cache of the passed Record.
8861      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8862      * @return {Number} The index of the passed Record. Returns -1 if not found.
8863      */
8864     indexOf : function(record){
8865         return this.data.indexOf(record);
8866     },
8867
8868     /**
8869      * Get the index within the cache of the Record with the passed id.
8870      * @param {String} id The id of the Record to find.
8871      * @return {Number} The index of the Record. Returns -1 if not found.
8872      */
8873     indexOfId : function(id){
8874         return this.data.indexOfKey(id);
8875     },
8876
8877     /**
8878      * Get the Record with the specified id.
8879      * @param {String} id The id of the Record to find.
8880      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8881      */
8882     getById : function(id){
8883         return this.data.key(id);
8884     },
8885
8886     /**
8887      * Get the Record at the specified index.
8888      * @param {Number} index The index of the Record to find.
8889      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8890      */
8891     getAt : function(index){
8892         return this.data.itemAt(index);
8893     },
8894
8895     /**
8896      * Returns a range of Records between specified indices.
8897      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8898      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8899      * @return {Roo.data.Record[]} An array of Records
8900      */
8901     getRange : function(start, end){
8902         return this.data.getRange(start, end);
8903     },
8904
8905     // private
8906     storeOptions : function(o){
8907         o = Roo.apply({}, o);
8908         delete o.callback;
8909         delete o.scope;
8910         this.lastOptions = o;
8911     },
8912
8913     /**
8914      * Loads the Record cache from the configured Proxy using the configured Reader.
8915      * <p>
8916      * If using remote paging, then the first load call must specify the <em>start</em>
8917      * and <em>limit</em> properties in the options.params property to establish the initial
8918      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8919      * <p>
8920      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8921      * and this call will return before the new data has been loaded. Perform any post-processing
8922      * in a callback function, or in a "load" event handler.</strong>
8923      * <p>
8924      * @param {Object} options An object containing properties which control loading options:<ul>
8925      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8926      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8927      * passed the following arguments:<ul>
8928      * <li>r : Roo.data.Record[]</li>
8929      * <li>options: Options object from the load call</li>
8930      * <li>success: Boolean success indicator</li></ul></li>
8931      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8932      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8933      * </ul>
8934      */
8935     load : function(options){
8936         options = options || {};
8937         if(this.fireEvent("beforeload", this, options) !== false){
8938             this.storeOptions(options);
8939             var p = Roo.apply(options.params || {}, this.baseParams);
8940             // if meta was not loaded from remote source.. try requesting it.
8941             if (!this.reader.metaFromRemote) {
8942                 p._requestMeta = 1;
8943             }
8944             if(this.sortInfo && this.remoteSort){
8945                 var pn = this.paramNames;
8946                 p[pn["sort"]] = this.sortInfo.field;
8947                 p[pn["dir"]] = this.sortInfo.direction;
8948             }
8949             if (this.multiSort) {
8950                 var pn = this.paramNames;
8951                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8952             }
8953             
8954             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8955         }
8956     },
8957
8958     /**
8959      * Reloads the Record cache from the configured Proxy using the configured Reader and
8960      * the options from the last load operation performed.
8961      * @param {Object} options (optional) An object containing properties which may override the options
8962      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8963      * the most recently used options are reused).
8964      */
8965     reload : function(options){
8966         this.load(Roo.applyIf(options||{}, this.lastOptions));
8967     },
8968
8969     // private
8970     // Called as a callback by the Reader during a load operation.
8971     loadRecords : function(o, options, success){
8972         if(!o || success === false){
8973             if(success !== false){
8974                 this.fireEvent("load", this, [], options, o);
8975             }
8976             if(options.callback){
8977                 options.callback.call(options.scope || this, [], options, false);
8978             }
8979             return;
8980         }
8981         // if data returned failure - throw an exception.
8982         if (o.success === false) {
8983             // show a message if no listener is registered.
8984             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8985                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8986             }
8987             // loadmask wil be hooked into this..
8988             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8989             return;
8990         }
8991         var r = o.records, t = o.totalRecords || r.length;
8992         
8993         this.fireEvent("beforeloadadd", this, r, options, o);
8994         
8995         if(!options || options.add !== true){
8996             if(this.pruneModifiedRecords){
8997                 this.modified = [];
8998             }
8999             for(var i = 0, len = r.length; i < len; i++){
9000                 r[i].join(this);
9001             }
9002             if(this.snapshot){
9003                 this.data = this.snapshot;
9004                 delete this.snapshot;
9005             }
9006             this.data.clear();
9007             this.data.addAll(r);
9008             this.totalLength = t;
9009             this.applySort();
9010             this.fireEvent("datachanged", this);
9011         }else{
9012             this.totalLength = Math.max(t, this.data.length+r.length);
9013             this.add(r);
9014         }
9015         this.fireEvent("load", this, r, options, o);
9016         if(options.callback){
9017             options.callback.call(options.scope || this, r, options, true);
9018         }
9019     },
9020
9021
9022     /**
9023      * Loads data from a passed data block. A Reader which understands the format of the data
9024      * must have been configured in the constructor.
9025      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9026      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9027      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9028      */
9029     loadData : function(o, append){
9030         var r = this.reader.readRecords(o);
9031         this.loadRecords(r, {add: append}, true);
9032     },
9033
9034     /**
9035      * Gets the number of cached records.
9036      * <p>
9037      * <em>If using paging, this may not be the total size of the dataset. If the data object
9038      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9039      * the data set size</em>
9040      */
9041     getCount : function(){
9042         return this.data.length || 0;
9043     },
9044
9045     /**
9046      * Gets the total number of records in the dataset as returned by the server.
9047      * <p>
9048      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9049      * the dataset size</em>
9050      */
9051     getTotalCount : function(){
9052         return this.totalLength || 0;
9053     },
9054
9055     /**
9056      * Returns the sort state of the Store as an object with two properties:
9057      * <pre><code>
9058  field {String} The name of the field by which the Records are sorted
9059  direction {String} The sort order, "ASC" or "DESC"
9060      * </code></pre>
9061      */
9062     getSortState : function(){
9063         return this.sortInfo;
9064     },
9065
9066     // private
9067     applySort : function(){
9068         if(this.sortInfo && !this.remoteSort){
9069             var s = this.sortInfo, f = s.field;
9070             var st = this.fields.get(f).sortType;
9071             var fn = function(r1, r2){
9072                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9073                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9074             };
9075             this.data.sort(s.direction, fn);
9076             if(this.snapshot && this.snapshot != this.data){
9077                 this.snapshot.sort(s.direction, fn);
9078             }
9079         }
9080     },
9081
9082     /**
9083      * Sets the default sort column and order to be used by the next load operation.
9084      * @param {String} fieldName The name of the field to sort by.
9085      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9086      */
9087     setDefaultSort : function(field, dir){
9088         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9089     },
9090
9091     /**
9092      * Sort the Records.
9093      * If remote sorting is used, the sort is performed on the server, and the cache is
9094      * reloaded. If local sorting is used, the cache is sorted internally.
9095      * @param {String} fieldName The name of the field to sort by.
9096      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9097      */
9098     sort : function(fieldName, dir){
9099         var f = this.fields.get(fieldName);
9100         if(!dir){
9101             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9102             
9103             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9104                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9105             }else{
9106                 dir = f.sortDir;
9107             }
9108         }
9109         this.sortToggle[f.name] = dir;
9110         this.sortInfo = {field: f.name, direction: dir};
9111         if(!this.remoteSort){
9112             this.applySort();
9113             this.fireEvent("datachanged", this);
9114         }else{
9115             this.load(this.lastOptions);
9116         }
9117     },
9118
9119     /**
9120      * Calls the specified function for each of the Records in the cache.
9121      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9122      * Returning <em>false</em> aborts and exits the iteration.
9123      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9124      */
9125     each : function(fn, scope){
9126         this.data.each(fn, scope);
9127     },
9128
9129     /**
9130      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9131      * (e.g., during paging).
9132      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9133      */
9134     getModifiedRecords : function(){
9135         return this.modified;
9136     },
9137
9138     // private
9139     createFilterFn : function(property, value, anyMatch){
9140         if(!value.exec){ // not a regex
9141             value = String(value);
9142             if(value.length == 0){
9143                 return false;
9144             }
9145             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9146         }
9147         return function(r){
9148             return value.test(r.data[property]);
9149         };
9150     },
9151
9152     /**
9153      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9154      * @param {String} property A field on your records
9155      * @param {Number} start The record index to start at (defaults to 0)
9156      * @param {Number} end The last record index to include (defaults to length - 1)
9157      * @return {Number} The sum
9158      */
9159     sum : function(property, start, end){
9160         var rs = this.data.items, v = 0;
9161         start = start || 0;
9162         end = (end || end === 0) ? end : rs.length-1;
9163
9164         for(var i = start; i <= end; i++){
9165             v += (rs[i].data[property] || 0);
9166         }
9167         return v;
9168     },
9169
9170     /**
9171      * Filter the records by a specified property.
9172      * @param {String} field A field on your records
9173      * @param {String/RegExp} value Either a string that the field
9174      * should start with or a RegExp to test against the field
9175      * @param {Boolean} anyMatch True to match any part not just the beginning
9176      */
9177     filter : function(property, value, anyMatch){
9178         var fn = this.createFilterFn(property, value, anyMatch);
9179         return fn ? this.filterBy(fn) : this.clearFilter();
9180     },
9181
9182     /**
9183      * Filter by a function. The specified function will be called with each
9184      * record in this data source. If the function returns true the record is included,
9185      * otherwise it is filtered.
9186      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9187      * @param {Object} scope (optional) The scope of the function (defaults to this)
9188      */
9189     filterBy : function(fn, scope){
9190         this.snapshot = this.snapshot || this.data;
9191         this.data = this.queryBy(fn, scope||this);
9192         this.fireEvent("datachanged", this);
9193     },
9194
9195     /**
9196      * Query the records by a specified property.
9197      * @param {String} field A field on your records
9198      * @param {String/RegExp} value Either a string that the field
9199      * should start with or a RegExp to test against the field
9200      * @param {Boolean} anyMatch True to match any part not just the beginning
9201      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9202      */
9203     query : function(property, value, anyMatch){
9204         var fn = this.createFilterFn(property, value, anyMatch);
9205         return fn ? this.queryBy(fn) : this.data.clone();
9206     },
9207
9208     /**
9209      * Query by a function. The specified function will be called with each
9210      * record in this data source. If the function returns true the record is included
9211      * in the results.
9212      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9213      * @param {Object} scope (optional) The scope of the function (defaults to this)
9214       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9215      **/
9216     queryBy : function(fn, scope){
9217         var data = this.snapshot || this.data;
9218         return data.filterBy(fn, scope||this);
9219     },
9220
9221     /**
9222      * Collects unique values for a particular dataIndex from this store.
9223      * @param {String} dataIndex The property to collect
9224      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9225      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9226      * @return {Array} An array of the unique values
9227      **/
9228     collect : function(dataIndex, allowNull, bypassFilter){
9229         var d = (bypassFilter === true && this.snapshot) ?
9230                 this.snapshot.items : this.data.items;
9231         var v, sv, r = [], l = {};
9232         for(var i = 0, len = d.length; i < len; i++){
9233             v = d[i].data[dataIndex];
9234             sv = String(v);
9235             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9236                 l[sv] = true;
9237                 r[r.length] = v;
9238             }
9239         }
9240         return r;
9241     },
9242
9243     /**
9244      * Revert to a view of the Record cache with no filtering applied.
9245      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9246      */
9247     clearFilter : function(suppressEvent){
9248         if(this.snapshot && this.snapshot != this.data){
9249             this.data = this.snapshot;
9250             delete this.snapshot;
9251             if(suppressEvent !== true){
9252                 this.fireEvent("datachanged", this);
9253             }
9254         }
9255     },
9256
9257     // private
9258     afterEdit : function(record){
9259         if(this.modified.indexOf(record) == -1){
9260             this.modified.push(record);
9261         }
9262         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9263     },
9264     
9265     // private
9266     afterReject : function(record){
9267         this.modified.remove(record);
9268         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9269     },
9270
9271     // private
9272     afterCommit : function(record){
9273         this.modified.remove(record);
9274         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9275     },
9276
9277     /**
9278      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9279      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9280      */
9281     commitChanges : function(){
9282         var m = this.modified.slice(0);
9283         this.modified = [];
9284         for(var i = 0, len = m.length; i < len; i++){
9285             m[i].commit();
9286         }
9287     },
9288
9289     /**
9290      * Cancel outstanding changes on all changed records.
9291      */
9292     rejectChanges : function(){
9293         var m = this.modified.slice(0);
9294         this.modified = [];
9295         for(var i = 0, len = m.length; i < len; i++){
9296             m[i].reject();
9297         }
9298     },
9299
9300     onMetaChange : function(meta, rtype, o){
9301         this.recordType = rtype;
9302         this.fields = rtype.prototype.fields;
9303         delete this.snapshot;
9304         this.sortInfo = meta.sortInfo || this.sortInfo;
9305         this.modified = [];
9306         this.fireEvent('metachange', this, this.reader.meta);
9307     },
9308     
9309     moveIndex : function(data, type)
9310     {
9311         var index = this.indexOf(data);
9312         
9313         var newIndex = index + type;
9314         
9315         this.remove(data);
9316         
9317         this.insert(newIndex, data);
9318         
9319     }
9320 });/*
9321  * Based on:
9322  * Ext JS Library 1.1.1
9323  * Copyright(c) 2006-2007, Ext JS, LLC.
9324  *
9325  * Originally Released Under LGPL - original licence link has changed is not relivant.
9326  *
9327  * Fork - LGPL
9328  * <script type="text/javascript">
9329  */
9330
9331 /**
9332  * @class Roo.data.SimpleStore
9333  * @extends Roo.data.Store
9334  * Small helper class to make creating Stores from Array data easier.
9335  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9336  * @cfg {Array} fields An array of field definition objects, or field name strings.
9337  * @cfg {Array} data The multi-dimensional array of data
9338  * @constructor
9339  * @param {Object} config
9340  */
9341 Roo.data.SimpleStore = function(config){
9342     Roo.data.SimpleStore.superclass.constructor.call(this, {
9343         isLocal : true,
9344         reader: new Roo.data.ArrayReader({
9345                 id: config.id
9346             },
9347             Roo.data.Record.create(config.fields)
9348         ),
9349         proxy : new Roo.data.MemoryProxy(config.data)
9350     });
9351     this.load();
9352 };
9353 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9354  * Based on:
9355  * Ext JS Library 1.1.1
9356  * Copyright(c) 2006-2007, Ext JS, LLC.
9357  *
9358  * Originally Released Under LGPL - original licence link has changed is not relivant.
9359  *
9360  * Fork - LGPL
9361  * <script type="text/javascript">
9362  */
9363
9364 /**
9365 /**
9366  * @extends Roo.data.Store
9367  * @class Roo.data.JsonStore
9368  * Small helper class to make creating Stores for JSON data easier. <br/>
9369 <pre><code>
9370 var store = new Roo.data.JsonStore({
9371     url: 'get-images.php',
9372     root: 'images',
9373     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9374 });
9375 </code></pre>
9376  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9377  * JsonReader and HttpProxy (unless inline data is provided).</b>
9378  * @cfg {Array} fields An array of field definition objects, or field name strings.
9379  * @constructor
9380  * @param {Object} config
9381  */
9382 Roo.data.JsonStore = function(c){
9383     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9384         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9385         reader: new Roo.data.JsonReader(c, c.fields)
9386     }));
9387 };
9388 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398
9399  
9400 Roo.data.Field = function(config){
9401     if(typeof config == "string"){
9402         config = {name: config};
9403     }
9404     Roo.apply(this, config);
9405     
9406     if(!this.type){
9407         this.type = "auto";
9408     }
9409     
9410     var st = Roo.data.SortTypes;
9411     // named sortTypes are supported, here we look them up
9412     if(typeof this.sortType == "string"){
9413         this.sortType = st[this.sortType];
9414     }
9415     
9416     // set default sortType for strings and dates
9417     if(!this.sortType){
9418         switch(this.type){
9419             case "string":
9420                 this.sortType = st.asUCString;
9421                 break;
9422             case "date":
9423                 this.sortType = st.asDate;
9424                 break;
9425             default:
9426                 this.sortType = st.none;
9427         }
9428     }
9429
9430     // define once
9431     var stripRe = /[\$,%]/g;
9432
9433     // prebuilt conversion function for this field, instead of
9434     // switching every time we're reading a value
9435     if(!this.convert){
9436         var cv, dateFormat = this.dateFormat;
9437         switch(this.type){
9438             case "":
9439             case "auto":
9440             case undefined:
9441                 cv = function(v){ return v; };
9442                 break;
9443             case "string":
9444                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9445                 break;
9446             case "int":
9447                 cv = function(v){
9448                     return v !== undefined && v !== null && v !== '' ?
9449                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9450                     };
9451                 break;
9452             case "float":
9453                 cv = function(v){
9454                     return v !== undefined && v !== null && v !== '' ?
9455                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9456                     };
9457                 break;
9458             case "bool":
9459             case "boolean":
9460                 cv = function(v){ return v === true || v === "true" || v == 1; };
9461                 break;
9462             case "date":
9463                 cv = function(v){
9464                     if(!v){
9465                         return '';
9466                     }
9467                     if(v instanceof Date){
9468                         return v;
9469                     }
9470                     if(dateFormat){
9471                         if(dateFormat == "timestamp"){
9472                             return new Date(v*1000);
9473                         }
9474                         return Date.parseDate(v, dateFormat);
9475                     }
9476                     var parsed = Date.parse(v);
9477                     return parsed ? new Date(parsed) : null;
9478                 };
9479              break;
9480             
9481         }
9482         this.convert = cv;
9483     }
9484 };
9485
9486 Roo.data.Field.prototype = {
9487     dateFormat: null,
9488     defaultValue: "",
9489     mapping: null,
9490     sortType : null,
9491     sortDir : "ASC"
9492 };/*
9493  * Based on:
9494  * Ext JS Library 1.1.1
9495  * Copyright(c) 2006-2007, Ext JS, LLC.
9496  *
9497  * Originally Released Under LGPL - original licence link has changed is not relivant.
9498  *
9499  * Fork - LGPL
9500  * <script type="text/javascript">
9501  */
9502  
9503 // Base class for reading structured data from a data source.  This class is intended to be
9504 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9505
9506 /**
9507  * @class Roo.data.DataReader
9508  * Base class for reading structured data from a data source.  This class is intended to be
9509  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9510  */
9511
9512 Roo.data.DataReader = function(meta, recordType){
9513     
9514     this.meta = meta;
9515     
9516     this.recordType = recordType instanceof Array ? 
9517         Roo.data.Record.create(recordType) : recordType;
9518 };
9519
9520 Roo.data.DataReader.prototype = {
9521      /**
9522      * Create an empty record
9523      * @param {Object} data (optional) - overlay some values
9524      * @return {Roo.data.Record} record created.
9525      */
9526     newRow :  function(d) {
9527         var da =  {};
9528         this.recordType.prototype.fields.each(function(c) {
9529             switch( c.type) {
9530                 case 'int' : da[c.name] = 0; break;
9531                 case 'date' : da[c.name] = new Date(); break;
9532                 case 'float' : da[c.name] = 0.0; break;
9533                 case 'boolean' : da[c.name] = false; break;
9534                 default : da[c.name] = ""; break;
9535             }
9536             
9537         });
9538         return new this.recordType(Roo.apply(da, d));
9539     }
9540     
9541 };/*
9542  * Based on:
9543  * Ext JS Library 1.1.1
9544  * Copyright(c) 2006-2007, Ext JS, LLC.
9545  *
9546  * Originally Released Under LGPL - original licence link has changed is not relivant.
9547  *
9548  * Fork - LGPL
9549  * <script type="text/javascript">
9550  */
9551
9552 /**
9553  * @class Roo.data.DataProxy
9554  * @extends Roo.data.Observable
9555  * This class is an abstract base class for implementations which provide retrieval of
9556  * unformatted data objects.<br>
9557  * <p>
9558  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9559  * (of the appropriate type which knows how to parse the data object) to provide a block of
9560  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9561  * <p>
9562  * Custom implementations must implement the load method as described in
9563  * {@link Roo.data.HttpProxy#load}.
9564  */
9565 Roo.data.DataProxy = function(){
9566     this.addEvents({
9567         /**
9568          * @event beforeload
9569          * Fires before a network request is made to retrieve a data object.
9570          * @param {Object} This DataProxy object.
9571          * @param {Object} params The params parameter to the load function.
9572          */
9573         beforeload : true,
9574         /**
9575          * @event load
9576          * Fires before the load method's callback is called.
9577          * @param {Object} This DataProxy object.
9578          * @param {Object} o The data object.
9579          * @param {Object} arg The callback argument object passed to the load function.
9580          */
9581         load : true,
9582         /**
9583          * @event loadexception
9584          * Fires if an Exception occurs during data retrieval.
9585          * @param {Object} This DataProxy object.
9586          * @param {Object} o The data object.
9587          * @param {Object} arg The callback argument object passed to the load function.
9588          * @param {Object} e The Exception.
9589          */
9590         loadexception : true
9591     });
9592     Roo.data.DataProxy.superclass.constructor.call(this);
9593 };
9594
9595 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9596
9597     /**
9598      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9599      */
9600 /*
9601  * Based on:
9602  * Ext JS Library 1.1.1
9603  * Copyright(c) 2006-2007, Ext JS, LLC.
9604  *
9605  * Originally Released Under LGPL - original licence link has changed is not relivant.
9606  *
9607  * Fork - LGPL
9608  * <script type="text/javascript">
9609  */
9610 /**
9611  * @class Roo.data.MemoryProxy
9612  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9613  * to the Reader when its load method is called.
9614  * @constructor
9615  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9616  */
9617 Roo.data.MemoryProxy = function(data){
9618     if (data.data) {
9619         data = data.data;
9620     }
9621     Roo.data.MemoryProxy.superclass.constructor.call(this);
9622     this.data = data;
9623 };
9624
9625 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9626     /**
9627      * Load data from the requested source (in this case an in-memory
9628      * data object passed to the constructor), read the data object into
9629      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9630      * process that block using the passed callback.
9631      * @param {Object} params This parameter is not used by the MemoryProxy class.
9632      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9633      * object into a block of Roo.data.Records.
9634      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9635      * The function must be passed <ul>
9636      * <li>The Record block object</li>
9637      * <li>The "arg" argument from the load function</li>
9638      * <li>A boolean success indicator</li>
9639      * </ul>
9640      * @param {Object} scope The scope in which to call the callback
9641      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9642      */
9643     load : function(params, reader, callback, scope, arg){
9644         params = params || {};
9645         var result;
9646         try {
9647             result = reader.readRecords(this.data);
9648         }catch(e){
9649             this.fireEvent("loadexception", this, arg, null, e);
9650             callback.call(scope, null, arg, false);
9651             return;
9652         }
9653         callback.call(scope, result, arg, true);
9654     },
9655     
9656     // private
9657     update : function(params, records){
9658         
9659     }
9660 });/*
9661  * Based on:
9662  * Ext JS Library 1.1.1
9663  * Copyright(c) 2006-2007, Ext JS, LLC.
9664  *
9665  * Originally Released Under LGPL - original licence link has changed is not relivant.
9666  *
9667  * Fork - LGPL
9668  * <script type="text/javascript">
9669  */
9670 /**
9671  * @class Roo.data.HttpProxy
9672  * @extends Roo.data.DataProxy
9673  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9674  * configured to reference a certain URL.<br><br>
9675  * <p>
9676  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9677  * from which the running page was served.<br><br>
9678  * <p>
9679  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9680  * <p>
9681  * Be aware that to enable the browser to parse an XML document, the server must set
9682  * the Content-Type header in the HTTP response to "text/xml".
9683  * @constructor
9684  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9685  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9686  * will be used to make the request.
9687  */
9688 Roo.data.HttpProxy = function(conn){
9689     Roo.data.HttpProxy.superclass.constructor.call(this);
9690     // is conn a conn config or a real conn?
9691     this.conn = conn;
9692     this.useAjax = !conn || !conn.events;
9693   
9694 };
9695
9696 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9697     // thse are take from connection...
9698     
9699     /**
9700      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9701      */
9702     /**
9703      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9704      * extra parameters to each request made by this object. (defaults to undefined)
9705      */
9706     /**
9707      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9708      *  to each request made by this object. (defaults to undefined)
9709      */
9710     /**
9711      * @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)
9712      */
9713     /**
9714      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9715      */
9716      /**
9717      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9718      * @type Boolean
9719      */
9720   
9721
9722     /**
9723      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9724      * @type Boolean
9725      */
9726     /**
9727      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9728      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9729      * a finer-grained basis than the DataProxy events.
9730      */
9731     getConnection : function(){
9732         return this.useAjax ? Roo.Ajax : this.conn;
9733     },
9734
9735     /**
9736      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9737      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9738      * process that block using the passed callback.
9739      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9740      * for the request to the remote server.
9741      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9742      * object into a block of Roo.data.Records.
9743      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9744      * The function must be passed <ul>
9745      * <li>The Record block object</li>
9746      * <li>The "arg" argument from the load function</li>
9747      * <li>A boolean success indicator</li>
9748      * </ul>
9749      * @param {Object} scope The scope in which to call the callback
9750      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9751      */
9752     load : function(params, reader, callback, scope, arg){
9753         if(this.fireEvent("beforeload", this, params) !== false){
9754             var  o = {
9755                 params : params || {},
9756                 request: {
9757                     callback : callback,
9758                     scope : scope,
9759                     arg : arg
9760                 },
9761                 reader: reader,
9762                 callback : this.loadResponse,
9763                 scope: this
9764             };
9765             if(this.useAjax){
9766                 Roo.applyIf(o, this.conn);
9767                 if(this.activeRequest){
9768                     Roo.Ajax.abort(this.activeRequest);
9769                 }
9770                 this.activeRequest = Roo.Ajax.request(o);
9771             }else{
9772                 this.conn.request(o);
9773             }
9774         }else{
9775             callback.call(scope||this, null, arg, false);
9776         }
9777     },
9778
9779     // private
9780     loadResponse : function(o, success, response){
9781         delete this.activeRequest;
9782         if(!success){
9783             this.fireEvent("loadexception", this, o, response);
9784             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9785             return;
9786         }
9787         var result;
9788         try {
9789             result = o.reader.read(response);
9790         }catch(e){
9791             this.fireEvent("loadexception", this, o, response, e);
9792             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9793             return;
9794         }
9795         
9796         this.fireEvent("load", this, o, o.request.arg);
9797         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9798     },
9799
9800     // private
9801     update : function(dataSet){
9802
9803     },
9804
9805     // private
9806     updateResponse : function(dataSet){
9807
9808     }
9809 });/*
9810  * Based on:
9811  * Ext JS Library 1.1.1
9812  * Copyright(c) 2006-2007, Ext JS, LLC.
9813  *
9814  * Originally Released Under LGPL - original licence link has changed is not relivant.
9815  *
9816  * Fork - LGPL
9817  * <script type="text/javascript">
9818  */
9819
9820 /**
9821  * @class Roo.data.ScriptTagProxy
9822  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9823  * other than the originating domain of the running page.<br><br>
9824  * <p>
9825  * <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
9826  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9827  * <p>
9828  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9829  * source code that is used as the source inside a &lt;script> tag.<br><br>
9830  * <p>
9831  * In order for the browser to process the returned data, the server must wrap the data object
9832  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9833  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9834  * depending on whether the callback name was passed:
9835  * <p>
9836  * <pre><code>
9837 boolean scriptTag = false;
9838 String cb = request.getParameter("callback");
9839 if (cb != null) {
9840     scriptTag = true;
9841     response.setContentType("text/javascript");
9842 } else {
9843     response.setContentType("application/x-json");
9844 }
9845 Writer out = response.getWriter();
9846 if (scriptTag) {
9847     out.write(cb + "(");
9848 }
9849 out.print(dataBlock.toJsonString());
9850 if (scriptTag) {
9851     out.write(");");
9852 }
9853 </pre></code>
9854  *
9855  * @constructor
9856  * @param {Object} config A configuration object.
9857  */
9858 Roo.data.ScriptTagProxy = function(config){
9859     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9860     Roo.apply(this, config);
9861     this.head = document.getElementsByTagName("head")[0];
9862 };
9863
9864 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9865
9866 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9867     /**
9868      * @cfg {String} url The URL from which to request the data object.
9869      */
9870     /**
9871      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9872      */
9873     timeout : 30000,
9874     /**
9875      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9876      * the server the name of the callback function set up by the load call to process the returned data object.
9877      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9878      * javascript output which calls this named function passing the data object as its only parameter.
9879      */
9880     callbackParam : "callback",
9881     /**
9882      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9883      * name to the request.
9884      */
9885     nocache : true,
9886
9887     /**
9888      * Load data from the configured URL, read the data object into
9889      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9890      * process that block using the passed callback.
9891      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9892      * for the request to the remote server.
9893      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9894      * object into a block of Roo.data.Records.
9895      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9896      * The function must be passed <ul>
9897      * <li>The Record block object</li>
9898      * <li>The "arg" argument from the load function</li>
9899      * <li>A boolean success indicator</li>
9900      * </ul>
9901      * @param {Object} scope The scope in which to call the callback
9902      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9903      */
9904     load : function(params, reader, callback, scope, arg){
9905         if(this.fireEvent("beforeload", this, params) !== false){
9906
9907             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9908
9909             var url = this.url;
9910             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9911             if(this.nocache){
9912                 url += "&_dc=" + (new Date().getTime());
9913             }
9914             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9915             var trans = {
9916                 id : transId,
9917                 cb : "stcCallback"+transId,
9918                 scriptId : "stcScript"+transId,
9919                 params : params,
9920                 arg : arg,
9921                 url : url,
9922                 callback : callback,
9923                 scope : scope,
9924                 reader : reader
9925             };
9926             var conn = this;
9927
9928             window[trans.cb] = function(o){
9929                 conn.handleResponse(o, trans);
9930             };
9931
9932             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9933
9934             if(this.autoAbort !== false){
9935                 this.abort();
9936             }
9937
9938             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9939
9940             var script = document.createElement("script");
9941             script.setAttribute("src", url);
9942             script.setAttribute("type", "text/javascript");
9943             script.setAttribute("id", trans.scriptId);
9944             this.head.appendChild(script);
9945
9946             this.trans = trans;
9947         }else{
9948             callback.call(scope||this, null, arg, false);
9949         }
9950     },
9951
9952     // private
9953     isLoading : function(){
9954         return this.trans ? true : false;
9955     },
9956
9957     /**
9958      * Abort the current server request.
9959      */
9960     abort : function(){
9961         if(this.isLoading()){
9962             this.destroyTrans(this.trans);
9963         }
9964     },
9965
9966     // private
9967     destroyTrans : function(trans, isLoaded){
9968         this.head.removeChild(document.getElementById(trans.scriptId));
9969         clearTimeout(trans.timeoutId);
9970         if(isLoaded){
9971             window[trans.cb] = undefined;
9972             try{
9973                 delete window[trans.cb];
9974             }catch(e){}
9975         }else{
9976             // if hasn't been loaded, wait for load to remove it to prevent script error
9977             window[trans.cb] = function(){
9978                 window[trans.cb] = undefined;
9979                 try{
9980                     delete window[trans.cb];
9981                 }catch(e){}
9982             };
9983         }
9984     },
9985
9986     // private
9987     handleResponse : function(o, trans){
9988         this.trans = false;
9989         this.destroyTrans(trans, true);
9990         var result;
9991         try {
9992             result = trans.reader.readRecords(o);
9993         }catch(e){
9994             this.fireEvent("loadexception", this, o, trans.arg, e);
9995             trans.callback.call(trans.scope||window, null, trans.arg, false);
9996             return;
9997         }
9998         this.fireEvent("load", this, o, trans.arg);
9999         trans.callback.call(trans.scope||window, result, trans.arg, true);
10000     },
10001
10002     // private
10003     handleFailure : function(trans){
10004         this.trans = false;
10005         this.destroyTrans(trans, false);
10006         this.fireEvent("loadexception", this, null, trans.arg);
10007         trans.callback.call(trans.scope||window, null, trans.arg, false);
10008     }
10009 });/*
10010  * Based on:
10011  * Ext JS Library 1.1.1
10012  * Copyright(c) 2006-2007, Ext JS, LLC.
10013  *
10014  * Originally Released Under LGPL - original licence link has changed is not relivant.
10015  *
10016  * Fork - LGPL
10017  * <script type="text/javascript">
10018  */
10019
10020 /**
10021  * @class Roo.data.JsonReader
10022  * @extends Roo.data.DataReader
10023  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10024  * based on mappings in a provided Roo.data.Record constructor.
10025  * 
10026  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10027  * in the reply previously. 
10028  * 
10029  * <p>
10030  * Example code:
10031  * <pre><code>
10032 var RecordDef = Roo.data.Record.create([
10033     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10034     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10035 ]);
10036 var myReader = new Roo.data.JsonReader({
10037     totalProperty: "results",    // The property which contains the total dataset size (optional)
10038     root: "rows",                // The property which contains an Array of row objects
10039     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10040 }, RecordDef);
10041 </code></pre>
10042  * <p>
10043  * This would consume a JSON file like this:
10044  * <pre><code>
10045 { 'results': 2, 'rows': [
10046     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10047     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10048 }
10049 </code></pre>
10050  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10051  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10052  * paged from the remote server.
10053  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10054  * @cfg {String} root name of the property which contains the Array of row objects.
10055  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10056  * @constructor
10057  * Create a new JsonReader
10058  * @param {Object} meta Metadata configuration options
10059  * @param {Object} recordType Either an Array of field definition objects,
10060  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10061  */
10062 Roo.data.JsonReader = function(meta, recordType){
10063     
10064     meta = meta || {};
10065     // set some defaults:
10066     Roo.applyIf(meta, {
10067         totalProperty: 'total',
10068         successProperty : 'success',
10069         root : 'data',
10070         id : 'id'
10071     });
10072     
10073     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10074 };
10075 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10076     
10077     /**
10078      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10079      * Used by Store query builder to append _requestMeta to params.
10080      * 
10081      */
10082     metaFromRemote : false,
10083     /**
10084      * This method is only used by a DataProxy which has retrieved data from a remote server.
10085      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10086      * @return {Object} data A data block which is used by an Roo.data.Store object as
10087      * a cache of Roo.data.Records.
10088      */
10089     read : function(response){
10090         var json = response.responseText;
10091        
10092         var o = /* eval:var:o */ eval("("+json+")");
10093         if(!o) {
10094             throw {message: "JsonReader.read: Json object not found"};
10095         }
10096         
10097         if(o.metaData){
10098             
10099             delete this.ef;
10100             this.metaFromRemote = true;
10101             this.meta = o.metaData;
10102             this.recordType = Roo.data.Record.create(o.metaData.fields);
10103             this.onMetaChange(this.meta, this.recordType, o);
10104         }
10105         return this.readRecords(o);
10106     },
10107
10108     // private function a store will implement
10109     onMetaChange : function(meta, recordType, o){
10110
10111     },
10112
10113     /**
10114          * @ignore
10115          */
10116     simpleAccess: function(obj, subsc) {
10117         return obj[subsc];
10118     },
10119
10120         /**
10121          * @ignore
10122          */
10123     getJsonAccessor: function(){
10124         var re = /[\[\.]/;
10125         return function(expr) {
10126             try {
10127                 return(re.test(expr))
10128                     ? new Function("obj", "return obj." + expr)
10129                     : function(obj){
10130                         return obj[expr];
10131                     };
10132             } catch(e){}
10133             return Roo.emptyFn;
10134         };
10135     }(),
10136
10137     /**
10138      * Create a data block containing Roo.data.Records from an XML document.
10139      * @param {Object} o An object which contains an Array of row objects in the property specified
10140      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10141      * which contains the total size of the dataset.
10142      * @return {Object} data A data block which is used by an Roo.data.Store object as
10143      * a cache of Roo.data.Records.
10144      */
10145     readRecords : function(o){
10146         /**
10147          * After any data loads, the raw JSON data is available for further custom processing.
10148          * @type Object
10149          */
10150         this.o = o;
10151         var s = this.meta, Record = this.recordType,
10152             f = Record.prototype.fields, fi = f.items, fl = f.length;
10153
10154 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10155         if (!this.ef) {
10156             if(s.totalProperty) {
10157                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10158                 }
10159                 if(s.successProperty) {
10160                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10161                 }
10162                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10163                 if (s.id) {
10164                         var g = this.getJsonAccessor(s.id);
10165                         this.getId = function(rec) {
10166                                 var r = g(rec);  
10167                                 return (r === undefined || r === "") ? null : r;
10168                         };
10169                 } else {
10170                         this.getId = function(){return null;};
10171                 }
10172             this.ef = [];
10173             for(var jj = 0; jj < fl; jj++){
10174                 f = fi[jj];
10175                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10176                 this.ef[jj] = this.getJsonAccessor(map);
10177             }
10178         }
10179
10180         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10181         if(s.totalProperty){
10182             var vt = parseInt(this.getTotal(o), 10);
10183             if(!isNaN(vt)){
10184                 totalRecords = vt;
10185             }
10186         }
10187         if(s.successProperty){
10188             var vs = this.getSuccess(o);
10189             if(vs === false || vs === 'false'){
10190                 success = false;
10191             }
10192         }
10193         var records = [];
10194             for(var i = 0; i < c; i++){
10195                     var n = root[i];
10196                 var values = {};
10197                 var id = this.getId(n);
10198                 for(var j = 0; j < fl; j++){
10199                     f = fi[j];
10200                 var v = this.ef[j](n);
10201                 if (!f.convert) {
10202                     Roo.log('missing convert for ' + f.name);
10203                     Roo.log(f);
10204                     continue;
10205                 }
10206                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10207                 }
10208                 var record = new Record(values, id);
10209                 record.json = n;
10210                 records[i] = record;
10211             }
10212             return {
10213             raw : o,
10214                 success : success,
10215                 records : records,
10216                 totalRecords : totalRecords
10217             };
10218     }
10219 });/*
10220  * Based on:
10221  * Ext JS Library 1.1.1
10222  * Copyright(c) 2006-2007, Ext JS, LLC.
10223  *
10224  * Originally Released Under LGPL - original licence link has changed is not relivant.
10225  *
10226  * Fork - LGPL
10227  * <script type="text/javascript">
10228  */
10229
10230 /**
10231  * @class Roo.data.ArrayReader
10232  * @extends Roo.data.DataReader
10233  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10234  * Each element of that Array represents a row of data fields. The
10235  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10236  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10237  * <p>
10238  * Example code:.
10239  * <pre><code>
10240 var RecordDef = Roo.data.Record.create([
10241     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10242     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10243 ]);
10244 var myReader = new Roo.data.ArrayReader({
10245     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10246 }, RecordDef);
10247 </code></pre>
10248  * <p>
10249  * This would consume an Array like this:
10250  * <pre><code>
10251 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10252   </code></pre>
10253  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10254  * @constructor
10255  * Create a new JsonReader
10256  * @param {Object} meta Metadata configuration options.
10257  * @param {Object} recordType Either an Array of field definition objects
10258  * as specified to {@link Roo.data.Record#create},
10259  * or an {@link Roo.data.Record} object
10260  * created using {@link Roo.data.Record#create}.
10261  */
10262 Roo.data.ArrayReader = function(meta, recordType){
10263     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10264 };
10265
10266 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10267     /**
10268      * Create a data block containing Roo.data.Records from an XML document.
10269      * @param {Object} o An Array of row objects which represents the dataset.
10270      * @return {Object} data A data block which is used by an Roo.data.Store object as
10271      * a cache of Roo.data.Records.
10272      */
10273     readRecords : function(o){
10274         var sid = this.meta ? this.meta.id : null;
10275         var recordType = this.recordType, fields = recordType.prototype.fields;
10276         var records = [];
10277         var root = o;
10278             for(var i = 0; i < root.length; i++){
10279                     var n = root[i];
10280                 var values = {};
10281                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10282                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10283                 var f = fields.items[j];
10284                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10285                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10286                 v = f.convert(v);
10287                 values[f.name] = v;
10288             }
10289                 var record = new recordType(values, id);
10290                 record.json = n;
10291                 records[records.length] = record;
10292             }
10293             return {
10294                 records : records,
10295                 totalRecords : records.length
10296             };
10297     }
10298 });/*
10299  * - LGPL
10300  * * 
10301  */
10302
10303 /**
10304  * @class Roo.bootstrap.ComboBox
10305  * @extends Roo.bootstrap.TriggerField
10306  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10307  * @cfg {Boolean} append (true|false) default false
10308  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10309  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10310  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10311  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10312  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10313  * @constructor
10314  * Create a new ComboBox.
10315  * @param {Object} config Configuration options
10316  */
10317 Roo.bootstrap.ComboBox = function(config){
10318     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10319     this.addEvents({
10320         /**
10321          * @event expand
10322          * Fires when the dropdown list is expanded
10323              * @param {Roo.bootstrap.ComboBox} combo This combo box
10324              */
10325         'expand' : true,
10326         /**
10327          * @event collapse
10328          * Fires when the dropdown list is collapsed
10329              * @param {Roo.bootstrap.ComboBox} combo This combo box
10330              */
10331         'collapse' : true,
10332         /**
10333          * @event beforeselect
10334          * Fires before a list item is selected. Return false to cancel the selection.
10335              * @param {Roo.bootstrap.ComboBox} combo This combo box
10336              * @param {Roo.data.Record} record The data record returned from the underlying store
10337              * @param {Number} index The index of the selected item in the dropdown list
10338              */
10339         'beforeselect' : true,
10340         /**
10341          * @event select
10342          * Fires when a list item is selected
10343              * @param {Roo.bootstrap.ComboBox} combo This combo box
10344              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10345              * @param {Number} index The index of the selected item in the dropdown list
10346              */
10347         'select' : true,
10348         /**
10349          * @event beforequery
10350          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10351          * The event object passed has these properties:
10352              * @param {Roo.bootstrap.ComboBox} combo This combo box
10353              * @param {String} query The query
10354              * @param {Boolean} forceAll true to force "all" query
10355              * @param {Boolean} cancel true to cancel the query
10356              * @param {Object} e The query event object
10357              */
10358         'beforequery': true,
10359          /**
10360          * @event add
10361          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10362              * @param {Roo.bootstrap.ComboBox} combo This combo box
10363              */
10364         'add' : true,
10365         /**
10366          * @event edit
10367          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10368              * @param {Roo.bootstrap.ComboBox} combo This combo box
10369              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10370              */
10371         'edit' : true,
10372         /**
10373          * @event remove
10374          * Fires when the remove value from the combobox array
10375              * @param {Roo.bootstrap.ComboBox} combo This combo box
10376              */
10377         'remove' : true
10378         
10379     });
10380     
10381     this.item = [];
10382     this.tickItems = [];
10383     
10384     this.selectedIndex = -1;
10385     if(this.mode == 'local'){
10386         if(config.queryDelay === undefined){
10387             this.queryDelay = 10;
10388         }
10389         if(config.minChars === undefined){
10390             this.minChars = 0;
10391         }
10392     }
10393 };
10394
10395 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10396      
10397     /**
10398      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10399      * rendering into an Roo.Editor, defaults to false)
10400      */
10401     /**
10402      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10403      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10404      */
10405     /**
10406      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10407      */
10408     /**
10409      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10410      * the dropdown list (defaults to undefined, with no header element)
10411      */
10412
10413      /**
10414      * @cfg {String/Roo.Template} tpl The template to use to render the output
10415      */
10416      
10417      /**
10418      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10419      */
10420     listWidth: undefined,
10421     /**
10422      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10423      * mode = 'remote' or 'text' if mode = 'local')
10424      */
10425     displayField: undefined,
10426     /**
10427      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10428      * mode = 'remote' or 'value' if mode = 'local'). 
10429      * Note: use of a valueField requires the user make a selection
10430      * in order for a value to be mapped.
10431      */
10432     valueField: undefined,
10433     
10434     
10435     /**
10436      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10437      * field's data value (defaults to the underlying DOM element's name)
10438      */
10439     hiddenName: undefined,
10440     /**
10441      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10442      */
10443     listClass: '',
10444     /**
10445      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10446      */
10447     selectedClass: 'active',
10448     
10449     /**
10450      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10451      */
10452     shadow:'sides',
10453     /**
10454      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10455      * anchor positions (defaults to 'tl-bl')
10456      */
10457     listAlign: 'tl-bl?',
10458     /**
10459      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10460      */
10461     maxHeight: 300,
10462     /**
10463      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10464      * query specified by the allQuery config option (defaults to 'query')
10465      */
10466     triggerAction: 'query',
10467     /**
10468      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10469      * (defaults to 4, does not apply if editable = false)
10470      */
10471     minChars : 4,
10472     /**
10473      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10474      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10475      */
10476     typeAhead: false,
10477     /**
10478      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10479      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10480      */
10481     queryDelay: 500,
10482     /**
10483      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10484      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10485      */
10486     pageSize: 0,
10487     /**
10488      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10489      * when editable = true (defaults to false)
10490      */
10491     selectOnFocus:false,
10492     /**
10493      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10494      */
10495     queryParam: 'query',
10496     /**
10497      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10498      * when mode = 'remote' (defaults to 'Loading...')
10499      */
10500     loadingText: 'Loading...',
10501     /**
10502      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10503      */
10504     resizable: false,
10505     /**
10506      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10507      */
10508     handleHeight : 8,
10509     /**
10510      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10511      * traditional select (defaults to true)
10512      */
10513     editable: true,
10514     /**
10515      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10516      */
10517     allQuery: '',
10518     /**
10519      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10520      */
10521     mode: 'remote',
10522     /**
10523      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10524      * listWidth has a higher value)
10525      */
10526     minListWidth : 70,
10527     /**
10528      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10529      * allow the user to set arbitrary text into the field (defaults to false)
10530      */
10531     forceSelection:false,
10532     /**
10533      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10534      * if typeAhead = true (defaults to 250)
10535      */
10536     typeAheadDelay : 250,
10537     /**
10538      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10539      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10540      */
10541     valueNotFoundText : undefined,
10542     /**
10543      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10544      */
10545     blockFocus : false,
10546     
10547     /**
10548      * @cfg {Boolean} disableClear Disable showing of clear button.
10549      */
10550     disableClear : false,
10551     /**
10552      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10553      */
10554     alwaysQuery : false,
10555     
10556     /**
10557      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10558      */
10559     multiple : false,
10560     
10561     //private
10562     addicon : false,
10563     editicon: false,
10564     
10565     page: 0,
10566     hasQuery: false,
10567     append: false,
10568     loadNext: false,
10569     autoFocus : true,
10570     tickable : false,
10571     btnPosition : 'right',
10572     triggerList : true,
10573     showToggleBtn : true,
10574     // element that contains real text value.. (when hidden is used..)
10575     
10576     getAutoCreate : function()
10577     {
10578         var cfg = false;
10579         
10580         /*
10581          *  Normal ComboBox
10582          */
10583         if(!this.tickable){
10584             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10585             return cfg;
10586         }
10587         
10588         /*
10589          *  ComboBox with tickable selections
10590          */
10591              
10592         var align = this.labelAlign || this.parentLabelAlign();
10593         
10594         cfg = {
10595             cls : 'form-group roo-combobox-tickable' //input-group
10596         };
10597         
10598         
10599         var buttons = {
10600             tag : 'div',
10601             cls : 'tickable-buttons',
10602             cn : [
10603                 {
10604                     tag : 'button',
10605                     type : 'button',
10606                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10607                     html : 'Edit'
10608                 },
10609                 {
10610                     tag : 'button',
10611                     type : 'button',
10612                     name : 'ok',
10613                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10614                     html : 'Done'
10615                 },
10616                 {
10617                     tag : 'button',
10618                     type : 'button',
10619                     name : 'cancel',
10620                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10621                     html : 'Cancel'
10622                 }
10623             ]
10624         };
10625         
10626         var _this = this;
10627         Roo.each(buttons.cn, function(c){
10628             if (_this.size) {
10629                 c.cls += ' btn-' + _this.size;
10630             }
10631
10632             if (_this.disabled) {
10633                 c.disabled = true;
10634             }
10635         });
10636         
10637         var box = {
10638             tag: 'div',
10639             cn: [
10640                 {
10641                     tag: 'input',
10642                     type : 'hidden',
10643                     cls: 'form-hidden-field'
10644                 },
10645                 {
10646                     tag: 'ul',
10647                     cls: 'select2-choices',
10648                     cn:[
10649                         {
10650                             tag: 'li',
10651                             cls: 'select2-search-field',
10652                             cn: [
10653
10654                                 buttons
10655                             ]
10656                         }
10657                     ]
10658                 }
10659             ]
10660         }
10661         
10662         var combobox = {
10663             cls: 'select2-container input-group select2-container-multi',
10664             cn: [
10665                 box
10666 //                {
10667 //                    tag: 'ul',
10668 //                    cls: 'typeahead typeahead-long dropdown-menu',
10669 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10670 //                }
10671             ]
10672         };
10673         
10674         if (align ==='left' && this.fieldLabel.length) {
10675             
10676                 Roo.log("left and has label");
10677                 cfg.cn = [
10678                     
10679                     {
10680                         tag: 'label',
10681                         'for' :  id,
10682                         cls : 'control-label col-sm-' + this.labelWidth,
10683                         html : this.fieldLabel
10684                         
10685                     },
10686                     {
10687                         cls : "col-sm-" + (12 - this.labelWidth), 
10688                         cn: [
10689                             combobox
10690                         ]
10691                     }
10692                     
10693                 ];
10694         } else if ( this.fieldLabel.length) {
10695                 Roo.log(" label");
10696                  cfg.cn = [
10697                    
10698                     {
10699                         tag: 'label',
10700                         //cls : 'input-group-addon',
10701                         html : this.fieldLabel
10702                         
10703                     },
10704                     
10705                     combobox
10706                     
10707                 ];
10708
10709         } else {
10710             
10711                 Roo.log(" no label && no align");
10712                 cfg = combobox
10713                      
10714                 
10715         }
10716          
10717         var settings=this;
10718         ['xs','sm','md','lg'].map(function(size){
10719             if (settings[size]) {
10720                 cfg.cls += ' col-' + size + '-' + settings[size];
10721             }
10722         });
10723         
10724         return cfg;
10725         
10726     },
10727     
10728     // private
10729     initEvents: function()
10730     {
10731         
10732         if (!this.store) {
10733             throw "can not find store for combo";
10734         }
10735         this.store = Roo.factory(this.store, Roo.data);
10736         
10737         if(this.tickable){
10738             this.initTickableEvents();
10739             return;
10740         }
10741         
10742         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10743         
10744         if(this.hiddenName){
10745             
10746             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10747             
10748             this.hiddenField.dom.value =
10749                 this.hiddenValue !== undefined ? this.hiddenValue :
10750                 this.value !== undefined ? this.value : '';
10751
10752             // prevent input submission
10753             this.el.dom.removeAttribute('name');
10754             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10755              
10756              
10757         }
10758         //if(Roo.isGecko){
10759         //    this.el.dom.setAttribute('autocomplete', 'off');
10760         //}
10761         
10762         var cls = 'x-combo-list';
10763         
10764         //this.list = new Roo.Layer({
10765         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10766         //});
10767         
10768         var _this = this;
10769         
10770         (function(){
10771             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10772             _this.list.setWidth(lw);
10773         }).defer(100);
10774         
10775         this.list.on('mouseover', this.onViewOver, this);
10776         this.list.on('mousemove', this.onViewMove, this);
10777         
10778         this.list.on('scroll', this.onViewScroll, this);
10779         
10780         /*
10781         this.list.swallowEvent('mousewheel');
10782         this.assetHeight = 0;
10783
10784         if(this.title){
10785             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10786             this.assetHeight += this.header.getHeight();
10787         }
10788
10789         this.innerList = this.list.createChild({cls:cls+'-inner'});
10790         this.innerList.on('mouseover', this.onViewOver, this);
10791         this.innerList.on('mousemove', this.onViewMove, this);
10792         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10793         
10794         if(this.allowBlank && !this.pageSize && !this.disableClear){
10795             this.footer = this.list.createChild({cls:cls+'-ft'});
10796             this.pageTb = new Roo.Toolbar(this.footer);
10797            
10798         }
10799         if(this.pageSize){
10800             this.footer = this.list.createChild({cls:cls+'-ft'});
10801             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10802                     {pageSize: this.pageSize});
10803             
10804         }
10805         
10806         if (this.pageTb && this.allowBlank && !this.disableClear) {
10807             var _this = this;
10808             this.pageTb.add(new Roo.Toolbar.Fill(), {
10809                 cls: 'x-btn-icon x-btn-clear',
10810                 text: '&#160;',
10811                 handler: function()
10812                 {
10813                     _this.collapse();
10814                     _this.clearValue();
10815                     _this.onSelect(false, -1);
10816                 }
10817             });
10818         }
10819         if (this.footer) {
10820             this.assetHeight += this.footer.getHeight();
10821         }
10822         */
10823             
10824         if(!this.tpl){
10825             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10826         }
10827
10828         this.view = new Roo.View(this.list, this.tpl, {
10829             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10830         });
10831         //this.view.wrapEl.setDisplayed(false);
10832         this.view.on('click', this.onViewClick, this);
10833         
10834         
10835         
10836         this.store.on('beforeload', this.onBeforeLoad, this);
10837         this.store.on('load', this.onLoad, this);
10838         this.store.on('loadexception', this.onLoadException, this);
10839         /*
10840         if(this.resizable){
10841             this.resizer = new Roo.Resizable(this.list,  {
10842                pinned:true, handles:'se'
10843             });
10844             this.resizer.on('resize', function(r, w, h){
10845                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10846                 this.listWidth = w;
10847                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10848                 this.restrictHeight();
10849             }, this);
10850             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10851         }
10852         */
10853         if(!this.editable){
10854             this.editable = true;
10855             this.setEditable(false);
10856         }
10857         
10858         /*
10859         
10860         if (typeof(this.events.add.listeners) != 'undefined') {
10861             
10862             this.addicon = this.wrap.createChild(
10863                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10864        
10865             this.addicon.on('click', function(e) {
10866                 this.fireEvent('add', this);
10867             }, this);
10868         }
10869         if (typeof(this.events.edit.listeners) != 'undefined') {
10870             
10871             this.editicon = this.wrap.createChild(
10872                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10873             if (this.addicon) {
10874                 this.editicon.setStyle('margin-left', '40px');
10875             }
10876             this.editicon.on('click', function(e) {
10877                 
10878                 // we fire even  if inothing is selected..
10879                 this.fireEvent('edit', this, this.lastData );
10880                 
10881             }, this);
10882         }
10883         */
10884         
10885         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10886             "up" : function(e){
10887                 this.inKeyMode = true;
10888                 this.selectPrev();
10889             },
10890
10891             "down" : function(e){
10892                 if(!this.isExpanded()){
10893                     this.onTriggerClick();
10894                 }else{
10895                     this.inKeyMode = true;
10896                     this.selectNext();
10897                 }
10898             },
10899
10900             "enter" : function(e){
10901 //                this.onViewClick();
10902                 //return true;
10903                 this.collapse();
10904                 
10905                 if(this.fireEvent("specialkey", this, e)){
10906                     this.onViewClick(false);
10907                 }
10908                 
10909                 return true;
10910             },
10911
10912             "esc" : function(e){
10913                 this.collapse();
10914             },
10915
10916             "tab" : function(e){
10917                 this.collapse();
10918                 
10919                 if(this.fireEvent("specialkey", this, e)){
10920                     this.onViewClick(false);
10921                 }
10922                 
10923                 return true;
10924             },
10925
10926             scope : this,
10927
10928             doRelay : function(foo, bar, hname){
10929                 if(hname == 'down' || this.scope.isExpanded()){
10930                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10931                 }
10932                 return true;
10933             },
10934
10935             forceKeyDown: true
10936         });
10937         
10938         
10939         this.queryDelay = Math.max(this.queryDelay || 10,
10940                 this.mode == 'local' ? 10 : 250);
10941         
10942         
10943         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10944         
10945         if(this.typeAhead){
10946             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10947         }
10948         if(this.editable !== false){
10949             this.inputEl().on("keyup", this.onKeyUp, this);
10950         }
10951         if(this.forceSelection){
10952             this.inputEl().on('blur', this.doForce, this);
10953         }
10954         
10955         if(this.multiple){
10956             this.choices = this.el.select('ul.select2-choices', true).first();
10957             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10958         }
10959     },
10960     
10961     initTickableEvents: function()
10962     {   
10963         this.createList();
10964         
10965         if(this.hiddenName){
10966             
10967             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10968             
10969             this.hiddenField.dom.value =
10970                 this.hiddenValue !== undefined ? this.hiddenValue :
10971                 this.value !== undefined ? this.value : '';
10972
10973             // prevent input submission
10974             this.el.dom.removeAttribute('name');
10975             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10976              
10977              
10978         }
10979         
10980 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10981         
10982         this.choices = this.el.select('ul.select2-choices', true).first();
10983         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10984         if(this.triggerList){
10985             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10986         }
10987          
10988         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10989         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10990         
10991         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10992         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10993         
10994         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10995         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10996         
10997         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10998         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10999         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11000         
11001         this.okBtn.hide();
11002         this.cancelBtn.hide();
11003         
11004         var _this = this;
11005         
11006         (function(){
11007             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11008             _this.list.setWidth(lw);
11009         }).defer(100);
11010         
11011         this.list.on('mouseover', this.onViewOver, this);
11012         this.list.on('mousemove', this.onViewMove, this);
11013         
11014         this.list.on('scroll', this.onViewScroll, this);
11015         
11016         if(!this.tpl){
11017             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>';
11018         }
11019
11020         this.view = new Roo.View(this.list, this.tpl, {
11021             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11022         });
11023         
11024         //this.view.wrapEl.setDisplayed(false);
11025         this.view.on('click', this.onViewClick, this);
11026         
11027         
11028         
11029         this.store.on('beforeload', this.onBeforeLoad, this);
11030         this.store.on('load', this.onLoad, this);
11031         this.store.on('loadexception', this.onLoadException, this);
11032         
11033 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11034 //            "up" : function(e){
11035 //                this.inKeyMode = true;
11036 //                this.selectPrev();
11037 //            },
11038 //
11039 //            "down" : function(e){
11040 //                if(!this.isExpanded()){
11041 //                    this.onTriggerClick();
11042 //                }else{
11043 //                    this.inKeyMode = true;
11044 //                    this.selectNext();
11045 //                }
11046 //            },
11047 //
11048 //            "enter" : function(e){
11049 ////                this.onViewClick();
11050 //                //return true;
11051 //                this.collapse();
11052 //                
11053 //                if(this.fireEvent("specialkey", this, e)){
11054 //                    this.onViewClick(false);
11055 //                }
11056 //                
11057 //                return true;
11058 //            },
11059 //
11060 //            "esc" : function(e){
11061 //                this.collapse();
11062 //            },
11063 //
11064 //            "tab" : function(e){
11065 //                this.collapse();
11066 //                
11067 //                if(this.fireEvent("specialkey", this, e)){
11068 //                    this.onViewClick(false);
11069 //                }
11070 //                
11071 //                return true;
11072 //            },
11073 //
11074 //            scope : this,
11075 //
11076 //            doRelay : function(foo, bar, hname){
11077 //                if(hname == 'down' || this.scope.isExpanded()){
11078 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11079 //                }
11080 //                return true;
11081 //            },
11082 //
11083 //            forceKeyDown: true
11084 //        });
11085         
11086         
11087         this.queryDelay = Math.max(this.queryDelay || 10,
11088                 this.mode == 'local' ? 10 : 250);
11089         
11090         
11091         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11092         
11093         if(this.typeAhead){
11094             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11095         }
11096     },
11097
11098     onDestroy : function(){
11099         if(this.view){
11100             this.view.setStore(null);
11101             this.view.el.removeAllListeners();
11102             this.view.el.remove();
11103             this.view.purgeListeners();
11104         }
11105         if(this.list){
11106             this.list.dom.innerHTML  = '';
11107         }
11108         
11109         if(this.store){
11110             this.store.un('beforeload', this.onBeforeLoad, this);
11111             this.store.un('load', this.onLoad, this);
11112             this.store.un('loadexception', this.onLoadException, this);
11113         }
11114         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11115     },
11116
11117     // private
11118     fireKey : function(e){
11119         if(e.isNavKeyPress() && !this.list.isVisible()){
11120             this.fireEvent("specialkey", this, e);
11121         }
11122     },
11123
11124     // private
11125     onResize: function(w, h){
11126 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11127 //        
11128 //        if(typeof w != 'number'){
11129 //            // we do not handle it!?!?
11130 //            return;
11131 //        }
11132 //        var tw = this.trigger.getWidth();
11133 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11134 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11135 //        var x = w - tw;
11136 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11137 //            
11138 //        //this.trigger.setStyle('left', x+'px');
11139 //        
11140 //        if(this.list && this.listWidth === undefined){
11141 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11142 //            this.list.setWidth(lw);
11143 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11144 //        }
11145         
11146     
11147         
11148     },
11149
11150     /**
11151      * Allow or prevent the user from directly editing the field text.  If false is passed,
11152      * the user will only be able to select from the items defined in the dropdown list.  This method
11153      * is the runtime equivalent of setting the 'editable' config option at config time.
11154      * @param {Boolean} value True to allow the user to directly edit the field text
11155      */
11156     setEditable : function(value){
11157         if(value == this.editable){
11158             return;
11159         }
11160         this.editable = value;
11161         if(!value){
11162             this.inputEl().dom.setAttribute('readOnly', true);
11163             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11164             this.inputEl().addClass('x-combo-noedit');
11165         }else{
11166             this.inputEl().dom.setAttribute('readOnly', false);
11167             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11168             this.inputEl().removeClass('x-combo-noedit');
11169         }
11170     },
11171
11172     // private
11173     
11174     onBeforeLoad : function(combo,opts){
11175         if(!this.hasFocus){
11176             return;
11177         }
11178          if (!opts.add) {
11179             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11180          }
11181         this.restrictHeight();
11182         this.selectedIndex = -1;
11183     },
11184
11185     // private
11186     onLoad : function(){
11187         
11188         this.hasQuery = false;
11189         
11190         if(!this.hasFocus){
11191             return;
11192         }
11193         
11194         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11195             this.loading.hide();
11196         }
11197         
11198         if(this.store.getCount() > 0){
11199             this.expand();
11200 //            this.restrictHeight();
11201             if(this.lastQuery == this.allQuery){
11202                 if(this.editable && !this.tickable){
11203                     this.inputEl().dom.select();
11204                 }
11205                 
11206                 if(
11207                     !this.selectByValue(this.value, true) &&
11208                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11209                     this.store.lastOptions.add != true)
11210                 ){
11211                     this.select(0, true);
11212                 }
11213             }else{
11214                 if(this.autoFocus){
11215                     this.selectNext();
11216                 }
11217                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11218                     this.taTask.delay(this.typeAheadDelay);
11219                 }
11220             }
11221         }else{
11222             this.onEmptyResults();
11223         }
11224         
11225         //this.el.focus();
11226     },
11227     // private
11228     onLoadException : function()
11229     {
11230         this.hasQuery = false;
11231         
11232         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11233             this.loading.hide();
11234         }
11235         
11236         this.collapse();
11237         Roo.log(this.store.reader.jsonData);
11238         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11239             // fixme
11240             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11241         }
11242         
11243         
11244     },
11245     // private
11246     onTypeAhead : function(){
11247         if(this.store.getCount() > 0){
11248             var r = this.store.getAt(0);
11249             var newValue = r.data[this.displayField];
11250             var len = newValue.length;
11251             var selStart = this.getRawValue().length;
11252             
11253             if(selStart != len){
11254                 this.setRawValue(newValue);
11255                 this.selectText(selStart, newValue.length);
11256             }
11257         }
11258     },
11259
11260     // private
11261     onSelect : function(record, index){
11262         
11263         if(this.fireEvent('beforeselect', this, record, index) !== false){
11264         
11265             this.setFromData(index > -1 ? record.data : false);
11266             
11267             this.collapse();
11268             this.fireEvent('select', this, record, index);
11269         }
11270     },
11271
11272     /**
11273      * Returns the currently selected field value or empty string if no value is set.
11274      * @return {String} value The selected value
11275      */
11276     getValue : function(){
11277         
11278         if(this.multiple){
11279             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11280         }
11281         
11282         if(this.valueField){
11283             return typeof this.value != 'undefined' ? this.value : '';
11284         }else{
11285             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11286         }
11287     },
11288
11289     /**
11290      * Clears any text/value currently set in the field
11291      */
11292     clearValue : function(){
11293         if(this.hiddenField){
11294             this.hiddenField.dom.value = '';
11295         }
11296         this.value = '';
11297         this.setRawValue('');
11298         this.lastSelectionText = '';
11299         
11300     },
11301
11302     /**
11303      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11304      * will be displayed in the field.  If the value does not match the data value of an existing item,
11305      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11306      * Otherwise the field will be blank (although the value will still be set).
11307      * @param {String} value The value to match
11308      */
11309     setValue : function(v){
11310         if(this.multiple){
11311             this.syncValue();
11312             return;
11313         }
11314         
11315         var text = v;
11316         if(this.valueField){
11317             var r = this.findRecord(this.valueField, v);
11318             if(r){
11319                 text = r.data[this.displayField];
11320             }else if(this.valueNotFoundText !== undefined){
11321                 text = this.valueNotFoundText;
11322             }
11323         }
11324         this.lastSelectionText = text;
11325         if(this.hiddenField){
11326             this.hiddenField.dom.value = v;
11327         }
11328         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11329         this.value = v;
11330     },
11331     /**
11332      * @property {Object} the last set data for the element
11333      */
11334     
11335     lastData : false,
11336     /**
11337      * Sets the value of the field based on a object which is related to the record format for the store.
11338      * @param {Object} value the value to set as. or false on reset?
11339      */
11340     setFromData : function(o){
11341         
11342         if(this.multiple){
11343             this.addItem(o);
11344             return;
11345         }
11346             
11347         var dv = ''; // display value
11348         var vv = ''; // value value..
11349         this.lastData = o;
11350         if (this.displayField) {
11351             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11352         } else {
11353             // this is an error condition!!!
11354             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11355         }
11356         
11357         if(this.valueField){
11358             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11359         }
11360         
11361         if(this.hiddenField){
11362             this.hiddenField.dom.value = vv;
11363             
11364             this.lastSelectionText = dv;
11365             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11366             this.value = vv;
11367             return;
11368         }
11369         // no hidden field.. - we store the value in 'value', but still display
11370         // display field!!!!
11371         this.lastSelectionText = dv;
11372         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11373         this.value = vv;
11374         
11375         
11376     },
11377     // private
11378     reset : function(){
11379         // overridden so that last data is reset..
11380         this.setValue(this.originalValue);
11381         this.clearInvalid();
11382         this.lastData = false;
11383         if (this.view) {
11384             this.view.clearSelections();
11385         }
11386     },
11387     // private
11388     findRecord : function(prop, value){
11389         var record;
11390         if(this.store.getCount() > 0){
11391             this.store.each(function(r){
11392                 if(r.data[prop] == value){
11393                     record = r;
11394                     return false;
11395                 }
11396                 return true;
11397             });
11398         }
11399         return record;
11400     },
11401     
11402     getName: function()
11403     {
11404         // returns hidden if it's set..
11405         if (!this.rendered) {return ''};
11406         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11407         
11408     },
11409     // private
11410     onViewMove : function(e, t){
11411         this.inKeyMode = false;
11412     },
11413
11414     // private
11415     onViewOver : function(e, t){
11416         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11417             return;
11418         }
11419         var item = this.view.findItemFromChild(t);
11420         
11421         if(item){
11422             var index = this.view.indexOf(item);
11423             this.select(index, false);
11424         }
11425     },
11426
11427     // private
11428     onViewClick : function(view, doFocus, el, e)
11429     {
11430         var index = this.view.getSelectedIndexes()[0];
11431         
11432         var r = this.store.getAt(index);
11433         
11434         if(this.tickable){
11435             
11436             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11437                 return;
11438             }
11439             
11440             var rm = false;
11441             var _this = this;
11442             
11443             Roo.each(this.tickItems, function(v,k){
11444                 
11445                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11446                     _this.tickItems.splice(k, 1);
11447                     rm = true;
11448                     return;
11449                 }
11450             })
11451             
11452             if(rm){
11453                 return;
11454             }
11455             
11456             this.tickItems.push(r.data);
11457             return;
11458         }
11459         
11460         if(r){
11461             this.onSelect(r, index);
11462         }
11463         if(doFocus !== false && !this.blockFocus){
11464             this.inputEl().focus();
11465         }
11466     },
11467
11468     // private
11469     restrictHeight : function(){
11470         //this.innerList.dom.style.height = '';
11471         //var inner = this.innerList.dom;
11472         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11473         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11474         //this.list.beginUpdate();
11475         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11476         this.list.alignTo(this.inputEl(), this.listAlign);
11477         this.list.alignTo(this.inputEl(), this.listAlign);
11478         //this.list.endUpdate();
11479     },
11480
11481     // private
11482     onEmptyResults : function(){
11483         this.collapse();
11484     },
11485
11486     /**
11487      * Returns true if the dropdown list is expanded, else false.
11488      */
11489     isExpanded : function(){
11490         return this.list.isVisible();
11491     },
11492
11493     /**
11494      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11495      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11496      * @param {String} value The data value of the item to select
11497      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11498      * selected item if it is not currently in view (defaults to true)
11499      * @return {Boolean} True if the value matched an item in the list, else false
11500      */
11501     selectByValue : function(v, scrollIntoView){
11502         if(v !== undefined && v !== null){
11503             var r = this.findRecord(this.valueField || this.displayField, v);
11504             if(r){
11505                 this.select(this.store.indexOf(r), scrollIntoView);
11506                 return true;
11507             }
11508         }
11509         return false;
11510     },
11511
11512     /**
11513      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11514      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11515      * @param {Number} index The zero-based index of the list item to select
11516      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11517      * selected item if it is not currently in view (defaults to true)
11518      */
11519     select : function(index, scrollIntoView){
11520         this.selectedIndex = index;
11521         this.view.select(index);
11522         if(scrollIntoView !== false){
11523             var el = this.view.getNode(index);
11524             if(el && !this.multiple && !this.tickable){
11525                 this.list.scrollChildIntoView(el, false);
11526             }
11527         }
11528     },
11529
11530     // private
11531     selectNext : function(){
11532         var ct = this.store.getCount();
11533         if(ct > 0){
11534             if(this.selectedIndex == -1){
11535                 this.select(0);
11536             }else if(this.selectedIndex < ct-1){
11537                 this.select(this.selectedIndex+1);
11538             }
11539         }
11540     },
11541
11542     // private
11543     selectPrev : function(){
11544         var ct = this.store.getCount();
11545         if(ct > 0){
11546             if(this.selectedIndex == -1){
11547                 this.select(0);
11548             }else if(this.selectedIndex != 0){
11549                 this.select(this.selectedIndex-1);
11550             }
11551         }
11552     },
11553
11554     // private
11555     onKeyUp : function(e){
11556         if(this.editable !== false && !e.isSpecialKey()){
11557             this.lastKey = e.getKey();
11558             this.dqTask.delay(this.queryDelay);
11559         }
11560     },
11561
11562     // private
11563     validateBlur : function(){
11564         return !this.list || !this.list.isVisible();   
11565     },
11566
11567     // private
11568     initQuery : function(){
11569         this.doQuery(this.getRawValue());
11570     },
11571
11572     // private
11573     doForce : function(){
11574         if(this.inputEl().dom.value.length > 0){
11575             this.inputEl().dom.value =
11576                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11577              
11578         }
11579     },
11580
11581     /**
11582      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11583      * query allowing the query action to be canceled if needed.
11584      * @param {String} query The SQL query to execute
11585      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11586      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11587      * saved in the current store (defaults to false)
11588      */
11589     doQuery : function(q, forceAll){
11590         
11591         if(q === undefined || q === null){
11592             q = '';
11593         }
11594         var qe = {
11595             query: q,
11596             forceAll: forceAll,
11597             combo: this,
11598             cancel:false
11599         };
11600         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11601             return false;
11602         }
11603         q = qe.query;
11604         
11605         forceAll = qe.forceAll;
11606         if(forceAll === true || (q.length >= this.minChars)){
11607             
11608             this.hasQuery = true;
11609             
11610             if(this.lastQuery != q || this.alwaysQuery){
11611                 this.lastQuery = q;
11612                 if(this.mode == 'local'){
11613                     this.selectedIndex = -1;
11614                     if(forceAll){
11615                         this.store.clearFilter();
11616                     }else{
11617                         this.store.filter(this.displayField, q);
11618                     }
11619                     this.onLoad();
11620                 }else{
11621                     this.store.baseParams[this.queryParam] = q;
11622                     
11623                     var options = {params : this.getParams(q)};
11624                     
11625                     if(this.loadNext){
11626                         options.add = true;
11627                         options.params.start = this.page * this.pageSize;
11628                     }
11629                     
11630                     this.store.load(options);
11631                     /*
11632                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11633                      *  we should expand the list on onLoad
11634                      *  so command out it
11635                      */
11636 //                    this.expand();
11637                 }
11638             }else{
11639                 this.selectedIndex = -1;
11640                 this.onLoad();   
11641             }
11642         }
11643         
11644         this.loadNext = false;
11645     },
11646
11647     // private
11648     getParams : function(q){
11649         var p = {};
11650         //p[this.queryParam] = q;
11651         
11652         if(this.pageSize){
11653             p.start = 0;
11654             p.limit = this.pageSize;
11655         }
11656         return p;
11657     },
11658
11659     /**
11660      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11661      */
11662     collapse : function(){
11663         if(!this.isExpanded()){
11664             return;
11665         }
11666         
11667         this.list.hide();
11668         
11669         if(this.tickable){
11670             this.okBtn.hide();
11671             this.cancelBtn.hide();
11672             this.trigger.show();
11673         }
11674         
11675         Roo.get(document).un('mousedown', this.collapseIf, this);
11676         Roo.get(document).un('mousewheel', this.collapseIf, this);
11677         if (!this.editable) {
11678             Roo.get(document).un('keydown', this.listKeyPress, this);
11679         }
11680         this.fireEvent('collapse', this);
11681     },
11682
11683     // private
11684     collapseIf : function(e){
11685         var in_combo  = e.within(this.el);
11686         var in_list =  e.within(this.list);
11687         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11688         
11689         if (in_combo || in_list || is_list) {
11690             //e.stopPropagation();
11691             return;
11692         }
11693         
11694         if(this.tickable){
11695             this.onTickableFooterButtonClick(e, false, false);
11696         }
11697
11698         this.collapse();
11699         
11700     },
11701
11702     /**
11703      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11704      */
11705     expand : function(){
11706        
11707         if(this.isExpanded() || !this.hasFocus){
11708             return;
11709         }
11710         
11711         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11712         this.list.setWidth(lw);
11713         
11714         
11715          Roo.log('expand');
11716         
11717         this.list.show();
11718         
11719         this.restrictHeight();
11720         
11721         if(this.tickable){
11722             
11723             this.tickItems = Roo.apply([], this.item);
11724             
11725             this.okBtn.show();
11726             this.cancelBtn.show();
11727             this.trigger.hide();
11728             
11729         }
11730         
11731         Roo.get(document).on('mousedown', this.collapseIf, this);
11732         Roo.get(document).on('mousewheel', this.collapseIf, this);
11733         if (!this.editable) {
11734             Roo.get(document).on('keydown', this.listKeyPress, this);
11735         }
11736         
11737         this.fireEvent('expand', this);
11738     },
11739
11740     // private
11741     // Implements the default empty TriggerField.onTriggerClick function
11742     onTriggerClick : function(e)
11743     {
11744         Roo.log('trigger click');
11745         
11746         if(this.disabled || !this.triggerList){
11747             return;
11748         }
11749         
11750         this.page = 0;
11751         this.loadNext = false;
11752         
11753         if(this.isExpanded()){
11754             this.collapse();
11755             if (!this.blockFocus) {
11756                 this.inputEl().focus();
11757             }
11758             
11759         }else {
11760             this.hasFocus = true;
11761             if(this.triggerAction == 'all') {
11762                 this.doQuery(this.allQuery, true);
11763             } else {
11764                 this.doQuery(this.getRawValue());
11765             }
11766             if (!this.blockFocus) {
11767                 this.inputEl().focus();
11768             }
11769         }
11770     },
11771     
11772     onTickableTriggerClick : function(e)
11773     {
11774         if(this.disabled){
11775             return;
11776         }
11777         
11778         this.page = 0;
11779         this.loadNext = false;
11780         this.hasFocus = true;
11781         
11782         if(this.triggerAction == 'all') {
11783             this.doQuery(this.allQuery, true);
11784         } else {
11785             this.doQuery(this.getRawValue());
11786         }
11787     },
11788     
11789     onSearchFieldClick : function(e)
11790     {
11791         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11792             return;
11793         }
11794         
11795         this.page = 0;
11796         this.loadNext = false;
11797         this.hasFocus = true;
11798         
11799         if(this.triggerAction == 'all') {
11800             this.doQuery(this.allQuery, true);
11801         } else {
11802             this.doQuery(this.getRawValue());
11803         }
11804     },
11805     
11806     listKeyPress : function(e)
11807     {
11808         //Roo.log('listkeypress');
11809         // scroll to first matching element based on key pres..
11810         if (e.isSpecialKey()) {
11811             return false;
11812         }
11813         var k = String.fromCharCode(e.getKey()).toUpperCase();
11814         //Roo.log(k);
11815         var match  = false;
11816         var csel = this.view.getSelectedNodes();
11817         var cselitem = false;
11818         if (csel.length) {
11819             var ix = this.view.indexOf(csel[0]);
11820             cselitem  = this.store.getAt(ix);
11821             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11822                 cselitem = false;
11823             }
11824             
11825         }
11826         
11827         this.store.each(function(v) { 
11828             if (cselitem) {
11829                 // start at existing selection.
11830                 if (cselitem.id == v.id) {
11831                     cselitem = false;
11832                 }
11833                 return true;
11834             }
11835                 
11836             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11837                 match = this.store.indexOf(v);
11838                 return false;
11839             }
11840             return true;
11841         }, this);
11842         
11843         if (match === false) {
11844             return true; // no more action?
11845         }
11846         // scroll to?
11847         this.view.select(match);
11848         var sn = Roo.get(this.view.getSelectedNodes()[0])
11849         //sn.scrollIntoView(sn.dom.parentNode, false);
11850     },
11851     
11852     onViewScroll : function(e, t){
11853         
11854         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){
11855             return;
11856         }
11857         
11858         this.hasQuery = true;
11859         
11860         this.loading = this.list.select('.loading', true).first();
11861         
11862         if(this.loading === null){
11863             this.list.createChild({
11864                 tag: 'div',
11865                 cls: 'loading select2-more-results select2-active',
11866                 html: 'Loading more results...'
11867             })
11868             
11869             this.loading = this.list.select('.loading', true).first();
11870             
11871             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11872             
11873             this.loading.hide();
11874         }
11875         
11876         this.loading.show();
11877         
11878         var _combo = this;
11879         
11880         this.page++;
11881         this.loadNext = true;
11882         
11883         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11884         
11885         return;
11886     },
11887     
11888     addItem : function(o)
11889     {   
11890         var dv = ''; // display value
11891         
11892         if (this.displayField) {
11893             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11894         } else {
11895             // this is an error condition!!!
11896             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11897         }
11898         
11899         if(!dv.length){
11900             return;
11901         }
11902         
11903         var choice = this.choices.createChild({
11904             tag: 'li',
11905             cls: 'select2-search-choice',
11906             cn: [
11907                 {
11908                     tag: 'div',
11909                     html: dv
11910                 },
11911                 {
11912                     tag: 'a',
11913                     href: '#',
11914                     cls: 'select2-search-choice-close',
11915                     tabindex: '-1'
11916                 }
11917             ]
11918             
11919         }, this.searchField);
11920         
11921         var close = choice.select('a.select2-search-choice-close', true).first()
11922         
11923         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11924         
11925         this.item.push(o);
11926         
11927         this.lastData = o;
11928         
11929         this.syncValue();
11930         
11931         this.inputEl().dom.value = '';
11932         
11933     },
11934     
11935     onRemoveItem : function(e, _self, o)
11936     {
11937         e.preventDefault();
11938         var index = this.item.indexOf(o.data) * 1;
11939         
11940         if( index < 0){
11941             Roo.log('not this item?!');
11942             return;
11943         }
11944         
11945         this.item.splice(index, 1);
11946         o.item.remove();
11947         
11948         this.syncValue();
11949         
11950         this.fireEvent('remove', this, e);
11951         
11952     },
11953     
11954     syncValue : function()
11955     {
11956         if(!this.item.length){
11957             this.clearValue();
11958             return;
11959         }
11960             
11961         var value = [];
11962         var _this = this;
11963         Roo.each(this.item, function(i){
11964             if(_this.valueField){
11965                 value.push(i[_this.valueField]);
11966                 return;
11967             }
11968
11969             value.push(i);
11970         });
11971
11972         this.value = value.join(',');
11973
11974         if(this.hiddenField){
11975             this.hiddenField.dom.value = this.value;
11976         }
11977     },
11978     
11979     clearItem : function()
11980     {
11981         if(!this.multiple){
11982             return;
11983         }
11984         
11985         this.item = [];
11986         
11987         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11988            c.remove();
11989         });
11990         
11991         this.syncValue();
11992     },
11993     
11994     inputEl: function ()
11995     {
11996         if(this.tickable){
11997             return this.searchField;
11998         }
11999         return this.el.select('input.form-control',true).first();
12000     },
12001     
12002     
12003     onTickableFooterButtonClick : function(e, btn, el)
12004     {
12005         e.preventDefault();
12006         
12007         if(btn && btn.name == 'cancel'){
12008             this.tickItems = Roo.apply([], this.item);
12009             this.collapse();
12010             return;
12011         }
12012         
12013         this.clearItem();
12014         
12015         var _this = this;
12016         
12017         Roo.each(this.tickItems, function(o){
12018             _this.addItem(o);
12019         });
12020         
12021         this.collapse();
12022         
12023     }
12024     
12025     
12026
12027     /** 
12028     * @cfg {Boolean} grow 
12029     * @hide 
12030     */
12031     /** 
12032     * @cfg {Number} growMin 
12033     * @hide 
12034     */
12035     /** 
12036     * @cfg {Number} growMax 
12037     * @hide 
12038     */
12039     /**
12040      * @hide
12041      * @method autoSize
12042      */
12043 });
12044 /*
12045  * Based on:
12046  * Ext JS Library 1.1.1
12047  * Copyright(c) 2006-2007, Ext JS, LLC.
12048  *
12049  * Originally Released Under LGPL - original licence link has changed is not relivant.
12050  *
12051  * Fork - LGPL
12052  * <script type="text/javascript">
12053  */
12054
12055 /**
12056  * @class Roo.View
12057  * @extends Roo.util.Observable
12058  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12059  * This class also supports single and multi selection modes. <br>
12060  * Create a data model bound view:
12061  <pre><code>
12062  var store = new Roo.data.Store(...);
12063
12064  var view = new Roo.View({
12065     el : "my-element",
12066     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12067  
12068     singleSelect: true,
12069     selectedClass: "ydataview-selected",
12070     store: store
12071  });
12072
12073  // listen for node click?
12074  view.on("click", function(vw, index, node, e){
12075  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12076  });
12077
12078  // load XML data
12079  dataModel.load("foobar.xml");
12080  </code></pre>
12081  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12082  * <br><br>
12083  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12084  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12085  * 
12086  * Note: old style constructor is still suported (container, template, config)
12087  * 
12088  * @constructor
12089  * Create a new View
12090  * @param {Object} config The config object
12091  * 
12092  */
12093 Roo.View = function(config, depreciated_tpl, depreciated_config){
12094     
12095     this.parent = false;
12096     
12097     if (typeof(depreciated_tpl) == 'undefined') {
12098         // new way.. - universal constructor.
12099         Roo.apply(this, config);
12100         this.el  = Roo.get(this.el);
12101     } else {
12102         // old format..
12103         this.el  = Roo.get(config);
12104         this.tpl = depreciated_tpl;
12105         Roo.apply(this, depreciated_config);
12106     }
12107     this.wrapEl  = this.el.wrap().wrap();
12108     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12109     
12110     
12111     if(typeof(this.tpl) == "string"){
12112         this.tpl = new Roo.Template(this.tpl);
12113     } else {
12114         // support xtype ctors..
12115         this.tpl = new Roo.factory(this.tpl, Roo);
12116     }
12117     
12118     
12119     this.tpl.compile();
12120     
12121     /** @private */
12122     this.addEvents({
12123         /**
12124          * @event beforeclick
12125          * Fires before a click is processed. Returns false to cancel the default action.
12126          * @param {Roo.View} this
12127          * @param {Number} index The index of the target node
12128          * @param {HTMLElement} node The target node
12129          * @param {Roo.EventObject} e The raw event object
12130          */
12131             "beforeclick" : true,
12132         /**
12133          * @event click
12134          * Fires when a template node is clicked.
12135          * @param {Roo.View} this
12136          * @param {Number} index The index of the target node
12137          * @param {HTMLElement} node The target node
12138          * @param {Roo.EventObject} e The raw event object
12139          */
12140             "click" : true,
12141         /**
12142          * @event dblclick
12143          * Fires when a template node is double clicked.
12144          * @param {Roo.View} this
12145          * @param {Number} index The index of the target node
12146          * @param {HTMLElement} node The target node
12147          * @param {Roo.EventObject} e The raw event object
12148          */
12149             "dblclick" : true,
12150         /**
12151          * @event contextmenu
12152          * Fires when a template node is right clicked.
12153          * @param {Roo.View} this
12154          * @param {Number} index The index of the target node
12155          * @param {HTMLElement} node The target node
12156          * @param {Roo.EventObject} e The raw event object
12157          */
12158             "contextmenu" : true,
12159         /**
12160          * @event selectionchange
12161          * Fires when the selected nodes change.
12162          * @param {Roo.View} this
12163          * @param {Array} selections Array of the selected nodes
12164          */
12165             "selectionchange" : true,
12166     
12167         /**
12168          * @event beforeselect
12169          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12170          * @param {Roo.View} this
12171          * @param {HTMLElement} node The node to be selected
12172          * @param {Array} selections Array of currently selected nodes
12173          */
12174             "beforeselect" : true,
12175         /**
12176          * @event preparedata
12177          * Fires on every row to render, to allow you to change the data.
12178          * @param {Roo.View} this
12179          * @param {Object} data to be rendered (change this)
12180          */
12181           "preparedata" : true
12182           
12183           
12184         });
12185
12186
12187
12188     this.el.on({
12189         "click": this.onClick,
12190         "dblclick": this.onDblClick,
12191         "contextmenu": this.onContextMenu,
12192         scope:this
12193     });
12194
12195     this.selections = [];
12196     this.nodes = [];
12197     this.cmp = new Roo.CompositeElementLite([]);
12198     if(this.store){
12199         this.store = Roo.factory(this.store, Roo.data);
12200         this.setStore(this.store, true);
12201     }
12202     
12203     if ( this.footer && this.footer.xtype) {
12204            
12205          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12206         
12207         this.footer.dataSource = this.store
12208         this.footer.container = fctr;
12209         this.footer = Roo.factory(this.footer, Roo);
12210         fctr.insertFirst(this.el);
12211         
12212         // this is a bit insane - as the paging toolbar seems to detach the el..
12213 //        dom.parentNode.parentNode.parentNode
12214          // they get detached?
12215     }
12216     
12217     
12218     Roo.View.superclass.constructor.call(this);
12219     
12220     
12221 };
12222
12223 Roo.extend(Roo.View, Roo.util.Observable, {
12224     
12225      /**
12226      * @cfg {Roo.data.Store} store Data store to load data from.
12227      */
12228     store : false,
12229     
12230     /**
12231      * @cfg {String|Roo.Element} el The container element.
12232      */
12233     el : '',
12234     
12235     /**
12236      * @cfg {String|Roo.Template} tpl The template used by this View 
12237      */
12238     tpl : false,
12239     /**
12240      * @cfg {String} dataName the named area of the template to use as the data area
12241      *                          Works with domtemplates roo-name="name"
12242      */
12243     dataName: false,
12244     /**
12245      * @cfg {String} selectedClass The css class to add to selected nodes
12246      */
12247     selectedClass : "x-view-selected",
12248      /**
12249      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12250      */
12251     emptyText : "",
12252     
12253     /**
12254      * @cfg {String} text to display on mask (default Loading)
12255      */
12256     mask : false,
12257     /**
12258      * @cfg {Boolean} multiSelect Allow multiple selection
12259      */
12260     multiSelect : false,
12261     /**
12262      * @cfg {Boolean} singleSelect Allow single selection
12263      */
12264     singleSelect:  false,
12265     
12266     /**
12267      * @cfg {Boolean} toggleSelect - selecting 
12268      */
12269     toggleSelect : false,
12270     
12271     /**
12272      * @cfg {Boolean} tickable - selecting 
12273      */
12274     tickable : false,
12275     
12276     /**
12277      * Returns the element this view is bound to.
12278      * @return {Roo.Element}
12279      */
12280     getEl : function(){
12281         return this.wrapEl;
12282     },
12283     
12284     
12285
12286     /**
12287      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12288      */
12289     refresh : function(){
12290         Roo.log('refresh');
12291         var t = this.tpl;
12292         
12293         // if we are using something like 'domtemplate', then
12294         // the what gets used is:
12295         // t.applySubtemplate(NAME, data, wrapping data..)
12296         // the outer template then get' applied with
12297         //     the store 'extra data'
12298         // and the body get's added to the
12299         //      roo-name="data" node?
12300         //      <span class='roo-tpl-{name}'></span> ?????
12301         
12302         
12303         
12304         this.clearSelections();
12305         this.el.update("");
12306         var html = [];
12307         var records = this.store.getRange();
12308         if(records.length < 1) {
12309             
12310             // is this valid??  = should it render a template??
12311             
12312             this.el.update(this.emptyText);
12313             return;
12314         }
12315         var el = this.el;
12316         if (this.dataName) {
12317             this.el.update(t.apply(this.store.meta)); //????
12318             el = this.el.child('.roo-tpl-' + this.dataName);
12319         }
12320         
12321         for(var i = 0, len = records.length; i < len; i++){
12322             var data = this.prepareData(records[i].data, i, records[i]);
12323             this.fireEvent("preparedata", this, data, i, records[i]);
12324             
12325             var d = Roo.apply({}, data);
12326             
12327             if(this.tickable){
12328                 Roo.apply(d, {'roo-id' : Roo.id()});
12329                 
12330                 var _this = this;
12331             
12332                 Roo.each(this.parent.item, function(item){
12333                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12334                         return;
12335                     }
12336                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12337                 });
12338             }
12339             
12340             html[html.length] = Roo.util.Format.trim(
12341                 this.dataName ?
12342                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12343                     t.apply(d)
12344             );
12345         }
12346         
12347         
12348         
12349         el.update(html.join(""));
12350         this.nodes = el.dom.childNodes;
12351         this.updateIndexes(0);
12352     },
12353     
12354
12355     /**
12356      * Function to override to reformat the data that is sent to
12357      * the template for each node.
12358      * DEPRICATED - use the preparedata event handler.
12359      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12360      * a JSON object for an UpdateManager bound view).
12361      */
12362     prepareData : function(data, index, record)
12363     {
12364         this.fireEvent("preparedata", this, data, index, record);
12365         return data;
12366     },
12367
12368     onUpdate : function(ds, record){
12369          Roo.log('on update');   
12370         this.clearSelections();
12371         var index = this.store.indexOf(record);
12372         var n = this.nodes[index];
12373         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12374         n.parentNode.removeChild(n);
12375         this.updateIndexes(index, index);
12376     },
12377
12378     
12379     
12380 // --------- FIXME     
12381     onAdd : function(ds, records, index)
12382     {
12383         Roo.log(['on Add', ds, records, index] );        
12384         this.clearSelections();
12385         if(this.nodes.length == 0){
12386             this.refresh();
12387             return;
12388         }
12389         var n = this.nodes[index];
12390         for(var i = 0, len = records.length; i < len; i++){
12391             var d = this.prepareData(records[i].data, i, records[i]);
12392             if(n){
12393                 this.tpl.insertBefore(n, d);
12394             }else{
12395                 
12396                 this.tpl.append(this.el, d);
12397             }
12398         }
12399         this.updateIndexes(index);
12400     },
12401
12402     onRemove : function(ds, record, index){
12403         Roo.log('onRemove');
12404         this.clearSelections();
12405         var el = this.dataName  ?
12406             this.el.child('.roo-tpl-' + this.dataName) :
12407             this.el; 
12408         
12409         el.dom.removeChild(this.nodes[index]);
12410         this.updateIndexes(index);
12411     },
12412
12413     /**
12414      * Refresh an individual node.
12415      * @param {Number} index
12416      */
12417     refreshNode : function(index){
12418         this.onUpdate(this.store, this.store.getAt(index));
12419     },
12420
12421     updateIndexes : function(startIndex, endIndex){
12422         var ns = this.nodes;
12423         startIndex = startIndex || 0;
12424         endIndex = endIndex || ns.length - 1;
12425         for(var i = startIndex; i <= endIndex; i++){
12426             ns[i].nodeIndex = i;
12427         }
12428     },
12429
12430     /**
12431      * Changes the data store this view uses and refresh the view.
12432      * @param {Store} store
12433      */
12434     setStore : function(store, initial){
12435         if(!initial && this.store){
12436             this.store.un("datachanged", this.refresh);
12437             this.store.un("add", this.onAdd);
12438             this.store.un("remove", this.onRemove);
12439             this.store.un("update", this.onUpdate);
12440             this.store.un("clear", this.refresh);
12441             this.store.un("beforeload", this.onBeforeLoad);
12442             this.store.un("load", this.onLoad);
12443             this.store.un("loadexception", this.onLoad);
12444         }
12445         if(store){
12446           
12447             store.on("datachanged", this.refresh, this);
12448             store.on("add", this.onAdd, this);
12449             store.on("remove", this.onRemove, this);
12450             store.on("update", this.onUpdate, this);
12451             store.on("clear", this.refresh, this);
12452             store.on("beforeload", this.onBeforeLoad, this);
12453             store.on("load", this.onLoad, this);
12454             store.on("loadexception", this.onLoad, this);
12455         }
12456         
12457         if(store){
12458             this.refresh();
12459         }
12460     },
12461     /**
12462      * onbeforeLoad - masks the loading area.
12463      *
12464      */
12465     onBeforeLoad : function(store,opts)
12466     {
12467          Roo.log('onBeforeLoad');   
12468         if (!opts.add) {
12469             this.el.update("");
12470         }
12471         this.el.mask(this.mask ? this.mask : "Loading" ); 
12472     },
12473     onLoad : function ()
12474     {
12475         this.el.unmask();
12476     },
12477     
12478
12479     /**
12480      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12481      * @param {HTMLElement} node
12482      * @return {HTMLElement} The template node
12483      */
12484     findItemFromChild : function(node){
12485         var el = this.dataName  ?
12486             this.el.child('.roo-tpl-' + this.dataName,true) :
12487             this.el.dom; 
12488         
12489         if(!node || node.parentNode == el){
12490                     return node;
12491             }
12492             var p = node.parentNode;
12493             while(p && p != el){
12494             if(p.parentNode == el){
12495                 return p;
12496             }
12497             p = p.parentNode;
12498         }
12499             return null;
12500     },
12501
12502     /** @ignore */
12503     onClick : function(e){
12504         var item = this.findItemFromChild(e.getTarget());
12505         if(item){
12506             var index = this.indexOf(item);
12507             if(this.onItemClick(item, index, e) !== false){
12508                 this.fireEvent("click", this, index, item, e);
12509             }
12510         }else{
12511             this.clearSelections();
12512         }
12513     },
12514
12515     /** @ignore */
12516     onContextMenu : function(e){
12517         var item = this.findItemFromChild(e.getTarget());
12518         if(item){
12519             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12520         }
12521     },
12522
12523     /** @ignore */
12524     onDblClick : function(e){
12525         var item = this.findItemFromChild(e.getTarget());
12526         if(item){
12527             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12528         }
12529     },
12530
12531     onItemClick : function(item, index, e)
12532     {
12533         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12534             return false;
12535         }
12536         if (this.toggleSelect) {
12537             var m = this.isSelected(item) ? 'unselect' : 'select';
12538             Roo.log(m);
12539             var _t = this;
12540             _t[m](item, true, false);
12541             return true;
12542         }
12543         if(this.multiSelect || this.singleSelect){
12544             if(this.multiSelect && e.shiftKey && this.lastSelection){
12545                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12546             }else{
12547                 this.select(item, this.multiSelect && e.ctrlKey);
12548                 this.lastSelection = item;
12549             }
12550             
12551             if(!this.tickable){
12552                 e.preventDefault();
12553             }
12554             
12555         }
12556         return true;
12557     },
12558
12559     /**
12560      * Get the number of selected nodes.
12561      * @return {Number}
12562      */
12563     getSelectionCount : function(){
12564         return this.selections.length;
12565     },
12566
12567     /**
12568      * Get the currently selected nodes.
12569      * @return {Array} An array of HTMLElements
12570      */
12571     getSelectedNodes : function(){
12572         return this.selections;
12573     },
12574
12575     /**
12576      * Get the indexes of the selected nodes.
12577      * @return {Array}
12578      */
12579     getSelectedIndexes : function(){
12580         var indexes = [], s = this.selections;
12581         for(var i = 0, len = s.length; i < len; i++){
12582             indexes.push(s[i].nodeIndex);
12583         }
12584         return indexes;
12585     },
12586
12587     /**
12588      * Clear all selections
12589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12590      */
12591     clearSelections : function(suppressEvent){
12592         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12593             this.cmp.elements = this.selections;
12594             this.cmp.removeClass(this.selectedClass);
12595             this.selections = [];
12596             if(!suppressEvent){
12597                 this.fireEvent("selectionchange", this, this.selections);
12598             }
12599         }
12600     },
12601
12602     /**
12603      * Returns true if the passed node is selected
12604      * @param {HTMLElement/Number} node The node or node index
12605      * @return {Boolean}
12606      */
12607     isSelected : function(node){
12608         var s = this.selections;
12609         if(s.length < 1){
12610             return false;
12611         }
12612         node = this.getNode(node);
12613         return s.indexOf(node) !== -1;
12614     },
12615
12616     /**
12617      * Selects nodes.
12618      * @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
12619      * @param {Boolean} keepExisting (optional) true to keep existing selections
12620      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12621      */
12622     select : function(nodeInfo, keepExisting, suppressEvent){
12623         if(nodeInfo instanceof Array){
12624             if(!keepExisting){
12625                 this.clearSelections(true);
12626             }
12627             for(var i = 0, len = nodeInfo.length; i < len; i++){
12628                 this.select(nodeInfo[i], true, true);
12629             }
12630             return;
12631         } 
12632         var node = this.getNode(nodeInfo);
12633         if(!node || this.isSelected(node)){
12634             return; // already selected.
12635         }
12636         if(!keepExisting){
12637             this.clearSelections(true);
12638         }
12639         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12640             Roo.fly(node).addClass(this.selectedClass);
12641             this.selections.push(node);
12642             if(!suppressEvent){
12643                 this.fireEvent("selectionchange", this, this.selections);
12644             }
12645         }
12646         
12647         
12648     },
12649       /**
12650      * Unselects nodes.
12651      * @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
12652      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12653      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12654      */
12655     unselect : function(nodeInfo, keepExisting, suppressEvent)
12656     {
12657         if(nodeInfo instanceof Array){
12658             Roo.each(this.selections, function(s) {
12659                 this.unselect(s, nodeInfo);
12660             }, this);
12661             return;
12662         }
12663         var node = this.getNode(nodeInfo);
12664         if(!node || !this.isSelected(node)){
12665             Roo.log("not selected");
12666             return; // not selected.
12667         }
12668         // fireevent???
12669         var ns = [];
12670         Roo.each(this.selections, function(s) {
12671             if (s == node ) {
12672                 Roo.fly(node).removeClass(this.selectedClass);
12673
12674                 return;
12675             }
12676             ns.push(s);
12677         },this);
12678         
12679         this.selections= ns;
12680         this.fireEvent("selectionchange", this, this.selections);
12681     },
12682
12683     /**
12684      * Gets a template node.
12685      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12686      * @return {HTMLElement} The node or null if it wasn't found
12687      */
12688     getNode : function(nodeInfo){
12689         if(typeof nodeInfo == "string"){
12690             return document.getElementById(nodeInfo);
12691         }else if(typeof nodeInfo == "number"){
12692             return this.nodes[nodeInfo];
12693         }
12694         return nodeInfo;
12695     },
12696
12697     /**
12698      * Gets a range template nodes.
12699      * @param {Number} startIndex
12700      * @param {Number} endIndex
12701      * @return {Array} An array of nodes
12702      */
12703     getNodes : function(start, end){
12704         var ns = this.nodes;
12705         start = start || 0;
12706         end = typeof end == "undefined" ? ns.length - 1 : end;
12707         var nodes = [];
12708         if(start <= end){
12709             for(var i = start; i <= end; i++){
12710                 nodes.push(ns[i]);
12711             }
12712         } else{
12713             for(var i = start; i >= end; i--){
12714                 nodes.push(ns[i]);
12715             }
12716         }
12717         return nodes;
12718     },
12719
12720     /**
12721      * Finds the index of the passed node
12722      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12723      * @return {Number} The index of the node or -1
12724      */
12725     indexOf : function(node){
12726         node = this.getNode(node);
12727         if(typeof node.nodeIndex == "number"){
12728             return node.nodeIndex;
12729         }
12730         var ns = this.nodes;
12731         for(var i = 0, len = ns.length; i < len; i++){
12732             if(ns[i] == node){
12733                 return i;
12734             }
12735         }
12736         return -1;
12737     }
12738 });
12739 /*
12740  * - LGPL
12741  *
12742  * based on jquery fullcalendar
12743  * 
12744  */
12745
12746 Roo.bootstrap = Roo.bootstrap || {};
12747 /**
12748  * @class Roo.bootstrap.Calendar
12749  * @extends Roo.bootstrap.Component
12750  * Bootstrap Calendar class
12751  * @cfg {Boolean} loadMask (true|false) default false
12752  * @cfg {Object} header generate the user specific header of the calendar, default false
12753
12754  * @constructor
12755  * Create a new Container
12756  * @param {Object} config The config object
12757  */
12758
12759
12760
12761 Roo.bootstrap.Calendar = function(config){
12762     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12763      this.addEvents({
12764         /**
12765              * @event select
12766              * Fires when a date is selected
12767              * @param {DatePicker} this
12768              * @param {Date} date The selected date
12769              */
12770         'select': true,
12771         /**
12772              * @event monthchange
12773              * Fires when the displayed month changes 
12774              * @param {DatePicker} this
12775              * @param {Date} date The selected month
12776              */
12777         'monthchange': true,
12778         /**
12779              * @event evententer
12780              * Fires when mouse over an event
12781              * @param {Calendar} this
12782              * @param {event} Event
12783              */
12784         'evententer': true,
12785         /**
12786              * @event eventleave
12787              * Fires when the mouse leaves an
12788              * @param {Calendar} this
12789              * @param {event}
12790              */
12791         'eventleave': true,
12792         /**
12793              * @event eventclick
12794              * Fires when the mouse click an
12795              * @param {Calendar} this
12796              * @param {event}
12797              */
12798         'eventclick': true
12799         
12800     });
12801
12802 };
12803
12804 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12805     
12806      /**
12807      * @cfg {Number} startDay
12808      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12809      */
12810     startDay : 0,
12811     
12812     loadMask : false,
12813     
12814     header : false,
12815       
12816     getAutoCreate : function(){
12817         
12818         
12819         var fc_button = function(name, corner, style, content ) {
12820             return Roo.apply({},{
12821                 tag : 'span',
12822                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12823                          (corner.length ?
12824                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12825                             ''
12826                         ),
12827                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12828                 unselectable: 'on'
12829             });
12830         };
12831         
12832         var header = {};
12833         
12834         if(!this.header){
12835             header = {
12836                 tag : 'table',
12837                 cls : 'fc-header',
12838                 style : 'width:100%',
12839                 cn : [
12840                     {
12841                         tag: 'tr',
12842                         cn : [
12843                             {
12844                                 tag : 'td',
12845                                 cls : 'fc-header-left',
12846                                 cn : [
12847                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12848                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12849                                     { tag: 'span', cls: 'fc-header-space' },
12850                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12851
12852
12853                                 ]
12854                             },
12855
12856                             {
12857                                 tag : 'td',
12858                                 cls : 'fc-header-center',
12859                                 cn : [
12860                                     {
12861                                         tag: 'span',
12862                                         cls: 'fc-header-title',
12863                                         cn : {
12864                                             tag: 'H2',
12865                                             html : 'month / year'
12866                                         }
12867                                     }
12868
12869                                 ]
12870                             },
12871                             {
12872                                 tag : 'td',
12873                                 cls : 'fc-header-right',
12874                                 cn : [
12875                               /*      fc_button('month', 'left', '', 'month' ),
12876                                     fc_button('week', '', '', 'week' ),
12877                                     fc_button('day', 'right', '', 'day' )
12878                                 */    
12879
12880                                 ]
12881                             }
12882
12883                         ]
12884                     }
12885                 ]
12886             };
12887         }
12888         
12889         header = this.header;
12890         
12891        
12892         var cal_heads = function() {
12893             var ret = [];
12894             // fixme - handle this.
12895             
12896             for (var i =0; i < Date.dayNames.length; i++) {
12897                 var d = Date.dayNames[i];
12898                 ret.push({
12899                     tag: 'th',
12900                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12901                     html : d.substring(0,3)
12902                 });
12903                 
12904             }
12905             ret[0].cls += ' fc-first';
12906             ret[6].cls += ' fc-last';
12907             return ret;
12908         };
12909         var cal_cell = function(n) {
12910             return  {
12911                 tag: 'td',
12912                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12913                 cn : [
12914                     {
12915                         cn : [
12916                             {
12917                                 cls: 'fc-day-number',
12918                                 html: 'D'
12919                             },
12920                             {
12921                                 cls: 'fc-day-content',
12922                              
12923                                 cn : [
12924                                      {
12925                                         style: 'position: relative;' // height: 17px;
12926                                     }
12927                                 ]
12928                             }
12929                             
12930                             
12931                         ]
12932                     }
12933                 ]
12934                 
12935             }
12936         };
12937         var cal_rows = function() {
12938             
12939             var ret = []
12940             for (var r = 0; r < 6; r++) {
12941                 var row= {
12942                     tag : 'tr',
12943                     cls : 'fc-week',
12944                     cn : []
12945                 };
12946                 
12947                 for (var i =0; i < Date.dayNames.length; i++) {
12948                     var d = Date.dayNames[i];
12949                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12950
12951                 }
12952                 row.cn[0].cls+=' fc-first';
12953                 row.cn[0].cn[0].style = 'min-height:90px';
12954                 row.cn[6].cls+=' fc-last';
12955                 ret.push(row);
12956                 
12957             }
12958             ret[0].cls += ' fc-first';
12959             ret[4].cls += ' fc-prev-last';
12960             ret[5].cls += ' fc-last';
12961             return ret;
12962             
12963         };
12964         
12965         var cal_table = {
12966             tag: 'table',
12967             cls: 'fc-border-separate',
12968             style : 'width:100%',
12969             cellspacing  : 0,
12970             cn : [
12971                 { 
12972                     tag: 'thead',
12973                     cn : [
12974                         { 
12975                             tag: 'tr',
12976                             cls : 'fc-first fc-last',
12977                             cn : cal_heads()
12978                         }
12979                     ]
12980                 },
12981                 { 
12982                     tag: 'tbody',
12983                     cn : cal_rows()
12984                 }
12985                   
12986             ]
12987         };
12988          
12989          var cfg = {
12990             cls : 'fc fc-ltr',
12991             cn : [
12992                 header,
12993                 {
12994                     cls : 'fc-content',
12995                     style : "position: relative;",
12996                     cn : [
12997                         {
12998                             cls : 'fc-view fc-view-month fc-grid',
12999                             style : 'position: relative',
13000                             unselectable : 'on',
13001                             cn : [
13002                                 {
13003                                     cls : 'fc-event-container',
13004                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13005                                 },
13006                                 cal_table
13007                             ]
13008                         }
13009                     ]
13010     
13011                 }
13012            ] 
13013             
13014         };
13015         
13016          
13017         
13018         return cfg;
13019     },
13020     
13021     
13022     initEvents : function()
13023     {
13024         if(!this.store){
13025             throw "can not find store for calendar";
13026         }
13027         
13028         var mark = {
13029             tag: "div",
13030             cls:"x-dlg-mask",
13031             style: "text-align:center",
13032             cn: [
13033                 {
13034                     tag: "div",
13035                     style: "background-color:white;width:50%;margin:250 auto",
13036                     cn: [
13037                         {
13038                             tag: "img",
13039                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13040                         },
13041                         {
13042                             tag: "span",
13043                             html: "Loading"
13044                         }
13045                         
13046                     ]
13047                 }
13048             ]
13049         }
13050         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13051         
13052         var size = this.el.select('.fc-content', true).first().getSize();
13053         this.maskEl.setSize(size.width, size.height);
13054         this.maskEl.enableDisplayMode("block");
13055         if(!this.loadMask){
13056             this.maskEl.hide();
13057         }
13058         
13059         this.store = Roo.factory(this.store, Roo.data);
13060         this.store.on('load', this.onLoad, this);
13061         this.store.on('beforeload', this.onBeforeLoad, this);
13062         
13063         this.resize();
13064         
13065         this.cells = this.el.select('.fc-day',true);
13066         //Roo.log(this.cells);
13067         this.textNodes = this.el.query('.fc-day-number');
13068         this.cells.addClassOnOver('fc-state-hover');
13069         
13070         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13071         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13072         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13073         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13074         
13075         this.on('monthchange', this.onMonthChange, this);
13076         
13077         this.update(new Date().clearTime());
13078     },
13079     
13080     resize : function() {
13081         var sz  = this.el.getSize();
13082         
13083         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13084         this.el.select('.fc-day-content div',true).setHeight(34);
13085     },
13086     
13087     
13088     // private
13089     showPrevMonth : function(e){
13090         this.update(this.activeDate.add("mo", -1));
13091     },
13092     showToday : function(e){
13093         this.update(new Date().clearTime());
13094     },
13095     // private
13096     showNextMonth : function(e){
13097         this.update(this.activeDate.add("mo", 1));
13098     },
13099
13100     // private
13101     showPrevYear : function(){
13102         this.update(this.activeDate.add("y", -1));
13103     },
13104
13105     // private
13106     showNextYear : function(){
13107         this.update(this.activeDate.add("y", 1));
13108     },
13109
13110     
13111    // private
13112     update : function(date)
13113     {
13114         var vd = this.activeDate;
13115         this.activeDate = date;
13116 //        if(vd && this.el){
13117 //            var t = date.getTime();
13118 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13119 //                Roo.log('using add remove');
13120 //                
13121 //                this.fireEvent('monthchange', this, date);
13122 //                
13123 //                this.cells.removeClass("fc-state-highlight");
13124 //                this.cells.each(function(c){
13125 //                   if(c.dateValue == t){
13126 //                       c.addClass("fc-state-highlight");
13127 //                       setTimeout(function(){
13128 //                            try{c.dom.firstChild.focus();}catch(e){}
13129 //                       }, 50);
13130 //                       return false;
13131 //                   }
13132 //                   return true;
13133 //                });
13134 //                return;
13135 //            }
13136 //        }
13137         
13138         var days = date.getDaysInMonth();
13139         
13140         var firstOfMonth = date.getFirstDateOfMonth();
13141         var startingPos = firstOfMonth.getDay()-this.startDay;
13142         
13143         if(startingPos < this.startDay){
13144             startingPos += 7;
13145         }
13146         
13147         var pm = date.add(Date.MONTH, -1);
13148         var prevStart = pm.getDaysInMonth()-startingPos;
13149 //        
13150         this.cells = this.el.select('.fc-day',true);
13151         this.textNodes = this.el.query('.fc-day-number');
13152         this.cells.addClassOnOver('fc-state-hover');
13153         
13154         var cells = this.cells.elements;
13155         var textEls = this.textNodes;
13156         
13157         Roo.each(cells, function(cell){
13158             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13159         });
13160         
13161         days += startingPos;
13162
13163         // convert everything to numbers so it's fast
13164         var day = 86400000;
13165         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13166         //Roo.log(d);
13167         //Roo.log(pm);
13168         //Roo.log(prevStart);
13169         
13170         var today = new Date().clearTime().getTime();
13171         var sel = date.clearTime().getTime();
13172         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13173         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13174         var ddMatch = this.disabledDatesRE;
13175         var ddText = this.disabledDatesText;
13176         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13177         var ddaysText = this.disabledDaysText;
13178         var format = this.format;
13179         
13180         var setCellClass = function(cal, cell){
13181             cell.row = 0;
13182             cell.events = [];
13183             cell.more = [];
13184             //Roo.log('set Cell Class');
13185             cell.title = "";
13186             var t = d.getTime();
13187             
13188             //Roo.log(d);
13189             
13190             cell.dateValue = t;
13191             if(t == today){
13192                 cell.className += " fc-today";
13193                 cell.className += " fc-state-highlight";
13194                 cell.title = cal.todayText;
13195             }
13196             if(t == sel){
13197                 // disable highlight in other month..
13198                 //cell.className += " fc-state-highlight";
13199                 
13200             }
13201             // disabling
13202             if(t < min) {
13203                 cell.className = " fc-state-disabled";
13204                 cell.title = cal.minText;
13205                 return;
13206             }
13207             if(t > max) {
13208                 cell.className = " fc-state-disabled";
13209                 cell.title = cal.maxText;
13210                 return;
13211             }
13212             if(ddays){
13213                 if(ddays.indexOf(d.getDay()) != -1){
13214                     cell.title = ddaysText;
13215                     cell.className = " fc-state-disabled";
13216                 }
13217             }
13218             if(ddMatch && format){
13219                 var fvalue = d.dateFormat(format);
13220                 if(ddMatch.test(fvalue)){
13221                     cell.title = ddText.replace("%0", fvalue);
13222                     cell.className = " fc-state-disabled";
13223                 }
13224             }
13225             
13226             if (!cell.initialClassName) {
13227                 cell.initialClassName = cell.dom.className;
13228             }
13229             
13230             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13231         };
13232
13233         var i = 0;
13234         
13235         for(; i < startingPos; i++) {
13236             textEls[i].innerHTML = (++prevStart);
13237             d.setDate(d.getDate()+1);
13238             
13239             cells[i].className = "fc-past fc-other-month";
13240             setCellClass(this, cells[i]);
13241         }
13242         
13243         var intDay = 0;
13244         
13245         for(; i < days; i++){
13246             intDay = i - startingPos + 1;
13247             textEls[i].innerHTML = (intDay);
13248             d.setDate(d.getDate()+1);
13249             
13250             cells[i].className = ''; // "x-date-active";
13251             setCellClass(this, cells[i]);
13252         }
13253         var extraDays = 0;
13254         
13255         for(; i < 42; i++) {
13256             textEls[i].innerHTML = (++extraDays);
13257             d.setDate(d.getDate()+1);
13258             
13259             cells[i].className = "fc-future fc-other-month";
13260             setCellClass(this, cells[i]);
13261         }
13262         
13263         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13264         
13265         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13266         
13267         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13268         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13269         
13270         if(totalRows != 6){
13271             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13272             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13273         }
13274         
13275         this.fireEvent('monthchange', this, date);
13276         
13277         
13278         /*
13279         if(!this.internalRender){
13280             var main = this.el.dom.firstChild;
13281             var w = main.offsetWidth;
13282             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13283             Roo.fly(main).setWidth(w);
13284             this.internalRender = true;
13285             // opera does not respect the auto grow header center column
13286             // then, after it gets a width opera refuses to recalculate
13287             // without a second pass
13288             if(Roo.isOpera && !this.secondPass){
13289                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13290                 this.secondPass = true;
13291                 this.update.defer(10, this, [date]);
13292             }
13293         }
13294         */
13295         
13296     },
13297     
13298     findCell : function(dt) {
13299         dt = dt.clearTime().getTime();
13300         var ret = false;
13301         this.cells.each(function(c){
13302             //Roo.log("check " +c.dateValue + '?=' + dt);
13303             if(c.dateValue == dt){
13304                 ret = c;
13305                 return false;
13306             }
13307             return true;
13308         });
13309         
13310         return ret;
13311     },
13312     
13313     findCells : function(ev) {
13314         var s = ev.start.clone().clearTime().getTime();
13315        // Roo.log(s);
13316         var e= ev.end.clone().clearTime().getTime();
13317        // Roo.log(e);
13318         var ret = [];
13319         this.cells.each(function(c){
13320              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13321             
13322             if(c.dateValue > e){
13323                 return ;
13324             }
13325             if(c.dateValue < s){
13326                 return ;
13327             }
13328             ret.push(c);
13329         });
13330         
13331         return ret;    
13332     },
13333     
13334 //    findBestRow: function(cells)
13335 //    {
13336 //        var ret = 0;
13337 //        
13338 //        for (var i =0 ; i < cells.length;i++) {
13339 //            ret  = Math.max(cells[i].rows || 0,ret);
13340 //        }
13341 //        return ret;
13342 //        
13343 //    },
13344     
13345     
13346     addItem : function(ev)
13347     {
13348         // look for vertical location slot in
13349         var cells = this.findCells(ev);
13350         
13351 //        ev.row = this.findBestRow(cells);
13352         
13353         // work out the location.
13354         
13355         var crow = false;
13356         var rows = [];
13357         for(var i =0; i < cells.length; i++) {
13358             
13359             cells[i].row = cells[0].row;
13360             
13361             if(i == 0){
13362                 cells[i].row = cells[i].row + 1;
13363             }
13364             
13365             if (!crow) {
13366                 crow = {
13367                     start : cells[i],
13368                     end :  cells[i]
13369                 };
13370                 continue;
13371             }
13372             if (crow.start.getY() == cells[i].getY()) {
13373                 // on same row.
13374                 crow.end = cells[i];
13375                 continue;
13376             }
13377             // different row.
13378             rows.push(crow);
13379             crow = {
13380                 start: cells[i],
13381                 end : cells[i]
13382             };
13383             
13384         }
13385         
13386         rows.push(crow);
13387         ev.els = [];
13388         ev.rows = rows;
13389         ev.cells = cells;
13390         
13391         cells[0].events.push(ev);
13392         
13393         this.calevents.push(ev);
13394     },
13395     
13396     clearEvents: function() {
13397         
13398         if(!this.calevents){
13399             return;
13400         }
13401         
13402         Roo.each(this.cells.elements, function(c){
13403             c.row = 0;
13404             c.events = [];
13405             c.more = [];
13406         });
13407         
13408         Roo.each(this.calevents, function(e) {
13409             Roo.each(e.els, function(el) {
13410                 el.un('mouseenter' ,this.onEventEnter, this);
13411                 el.un('mouseleave' ,this.onEventLeave, this);
13412                 el.remove();
13413             },this);
13414         },this);
13415         
13416         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13417             e.remove();
13418         });
13419         
13420     },
13421     
13422     renderEvents: function()
13423     {   
13424         var _this = this;
13425         
13426         this.cells.each(function(c) {
13427             
13428             if(c.row < 5){
13429                 return;
13430             }
13431             
13432             var ev = c.events;
13433             
13434             var r = 4;
13435             if(c.row != c.events.length){
13436                 r = 4 - (4 - (c.row - c.events.length));
13437             }
13438             
13439             c.events = ev.slice(0, r);
13440             c.more = ev.slice(r);
13441             
13442             if(c.more.length && c.more.length == 1){
13443                 c.events.push(c.more.pop());
13444             }
13445             
13446             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13447             
13448         });
13449             
13450         this.cells.each(function(c) {
13451             
13452             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13453             
13454             
13455             for (var e = 0; e < c.events.length; e++){
13456                 var ev = c.events[e];
13457                 var rows = ev.rows;
13458                 
13459                 for(var i = 0; i < rows.length; i++) {
13460                 
13461                     // how many rows should it span..
13462
13463                     var  cfg = {
13464                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13465                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13466
13467                         unselectable : "on",
13468                         cn : [
13469                             {
13470                                 cls: 'fc-event-inner',
13471                                 cn : [
13472     //                                {
13473     //                                  tag:'span',
13474     //                                  cls: 'fc-event-time',
13475     //                                  html : cells.length > 1 ? '' : ev.time
13476     //                                },
13477                                     {
13478                                       tag:'span',
13479                                       cls: 'fc-event-title',
13480                                       html : String.format('{0}', ev.title)
13481                                     }
13482
13483
13484                                 ]
13485                             },
13486                             {
13487                                 cls: 'ui-resizable-handle ui-resizable-e',
13488                                 html : '&nbsp;&nbsp;&nbsp'
13489                             }
13490
13491                         ]
13492                     };
13493
13494                     if (i == 0) {
13495                         cfg.cls += ' fc-event-start';
13496                     }
13497                     if ((i+1) == rows.length) {
13498                         cfg.cls += ' fc-event-end';
13499                     }
13500
13501                     var ctr = _this.el.select('.fc-event-container',true).first();
13502                     var cg = ctr.createChild(cfg);
13503
13504                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13505                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13506
13507                     var r = (c.more.length) ? 1 : 0;
13508                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13509                     cg.setWidth(ebox.right - sbox.x -2);
13510
13511                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13512                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13513                     cg.on('click', _this.onEventClick, _this, ev);
13514
13515                     ev.els.push(cg);
13516                     
13517                 }
13518                 
13519             }
13520             
13521             
13522             if(c.more.length){
13523                 var  cfg = {
13524                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13525                     style : 'position: absolute',
13526                     unselectable : "on",
13527                     cn : [
13528                         {
13529                             cls: 'fc-event-inner',
13530                             cn : [
13531                                 {
13532                                   tag:'span',
13533                                   cls: 'fc-event-title',
13534                                   html : 'More'
13535                                 }
13536
13537
13538                             ]
13539                         },
13540                         {
13541                             cls: 'ui-resizable-handle ui-resizable-e',
13542                             html : '&nbsp;&nbsp;&nbsp'
13543                         }
13544
13545                     ]
13546                 };
13547
13548                 var ctr = _this.el.select('.fc-event-container',true).first();
13549                 var cg = ctr.createChild(cfg);
13550
13551                 var sbox = c.select('.fc-day-content',true).first().getBox();
13552                 var ebox = c.select('.fc-day-content',true).first().getBox();
13553                 //Roo.log(cg);
13554                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13555                 cg.setWidth(ebox.right - sbox.x -2);
13556
13557                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13558                 
13559             }
13560             
13561         });
13562         
13563         
13564         
13565     },
13566     
13567     onEventEnter: function (e, el,event,d) {
13568         this.fireEvent('evententer', this, el, event);
13569     },
13570     
13571     onEventLeave: function (e, el,event,d) {
13572         this.fireEvent('eventleave', this, el, event);
13573     },
13574     
13575     onEventClick: function (e, el,event,d) {
13576         this.fireEvent('eventclick', this, el, event);
13577     },
13578     
13579     onMonthChange: function () {
13580         this.store.load();
13581     },
13582     
13583     onMoreEventClick: function(e, el, more)
13584     {
13585         var _this = this;
13586         
13587         this.calpopover.placement = 'right';
13588         this.calpopover.setTitle('More');
13589         
13590         this.calpopover.setContent('');
13591         
13592         var ctr = this.calpopover.el.select('.popover-content', true).first();
13593         
13594         Roo.each(more, function(m){
13595             var cfg = {
13596                 cls : 'fc-event-hori fc-event-draggable',
13597                 html : m.title
13598             }
13599             var cg = ctr.createChild(cfg);
13600             
13601             cg.on('click', _this.onEventClick, _this, m);
13602         });
13603         
13604         this.calpopover.show(el);
13605         
13606         
13607     },
13608     
13609     onLoad: function () 
13610     {   
13611         this.calevents = [];
13612         var cal = this;
13613         
13614         if(this.store.getCount() > 0){
13615             this.store.data.each(function(d){
13616                cal.addItem({
13617                     id : d.data.id,
13618                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13619                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13620                     time : d.data.start_time,
13621                     title : d.data.title,
13622                     description : d.data.description,
13623                     venue : d.data.venue
13624                 });
13625             });
13626         }
13627         
13628         this.renderEvents();
13629         
13630         if(this.calevents.length && this.loadMask){
13631             this.maskEl.hide();
13632         }
13633     },
13634     
13635     onBeforeLoad: function()
13636     {
13637         this.clearEvents();
13638         if(this.loadMask){
13639             this.maskEl.show();
13640         }
13641     }
13642 });
13643
13644  
13645  /*
13646  * - LGPL
13647  *
13648  * element
13649  * 
13650  */
13651
13652 /**
13653  * @class Roo.bootstrap.Popover
13654  * @extends Roo.bootstrap.Component
13655  * Bootstrap Popover class
13656  * @cfg {String} html contents of the popover   (or false to use children..)
13657  * @cfg {String} title of popover (or false to hide)
13658  * @cfg {String} placement how it is placed
13659  * @cfg {String} trigger click || hover (or false to trigger manually)
13660  * @cfg {String} over what (parent or false to trigger manually.)
13661  * @cfg {Number} delay - delay before showing
13662  
13663  * @constructor
13664  * Create a new Popover
13665  * @param {Object} config The config object
13666  */
13667
13668 Roo.bootstrap.Popover = function(config){
13669     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13670 };
13671
13672 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13673     
13674     title: 'Fill in a title',
13675     html: false,
13676     
13677     placement : 'right',
13678     trigger : 'hover', // hover
13679     
13680     delay : 0,
13681     
13682     over: 'parent',
13683     
13684     can_build_overlaid : false,
13685     
13686     getChildContainer : function()
13687     {
13688         return this.el.select('.popover-content',true).first();
13689     },
13690     
13691     getAutoCreate : function(){
13692          Roo.log('make popover?');
13693         var cfg = {
13694            cls : 'popover roo-dynamic',
13695            style: 'display:block',
13696            cn : [
13697                 {
13698                     cls : 'arrow'
13699                 },
13700                 {
13701                     cls : 'popover-inner',
13702                     cn : [
13703                         {
13704                             tag: 'h3',
13705                             cls: 'popover-title',
13706                             html : this.title
13707                         },
13708                         {
13709                             cls : 'popover-content',
13710                             html : this.html
13711                         }
13712                     ]
13713                     
13714                 }
13715            ]
13716         };
13717         
13718         return cfg;
13719     },
13720     setTitle: function(str)
13721     {
13722         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13723     },
13724     setContent: function(str)
13725     {
13726         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13727     },
13728     // as it get's added to the bottom of the page.
13729     onRender : function(ct, position)
13730     {
13731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13732         if(!this.el){
13733             var cfg = Roo.apply({},  this.getAutoCreate());
13734             cfg.id = Roo.id();
13735             
13736             if (this.cls) {
13737                 cfg.cls += ' ' + this.cls;
13738             }
13739             if (this.style) {
13740                 cfg.style = this.style;
13741             }
13742             Roo.log("adding to ")
13743             this.el = Roo.get(document.body).createChild(cfg, position);
13744             Roo.log(this.el);
13745         }
13746         this.initEvents();
13747     },
13748     
13749     initEvents : function()
13750     {
13751         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13752         this.el.enableDisplayMode('block');
13753         this.el.hide();
13754         if (this.over === false) {
13755             return; 
13756         }
13757         if (this.triggers === false) {
13758             return;
13759         }
13760         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13761         var triggers = this.trigger ? this.trigger.split(' ') : [];
13762         Roo.each(triggers, function(trigger) {
13763         
13764             if (trigger == 'click') {
13765                 on_el.on('click', this.toggle, this);
13766             } else if (trigger != 'manual') {
13767                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13768                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13769       
13770                 on_el.on(eventIn  ,this.enter, this);
13771                 on_el.on(eventOut, this.leave, this);
13772             }
13773         }, this);
13774         
13775     },
13776     
13777     
13778     // private
13779     timeout : null,
13780     hoverState : null,
13781     
13782     toggle : function () {
13783         this.hoverState == 'in' ? this.leave() : this.enter();
13784     },
13785     
13786     enter : function () {
13787        
13788     
13789         clearTimeout(this.timeout);
13790     
13791         this.hoverState = 'in'
13792     
13793         if (!this.delay || !this.delay.show) {
13794             this.show();
13795             return 
13796         }
13797         var _t = this;
13798         this.timeout = setTimeout(function () {
13799             if (_t.hoverState == 'in') {
13800                 _t.show();
13801             }
13802         }, this.delay.show)
13803     },
13804     leave : function() {
13805         clearTimeout(this.timeout);
13806     
13807         this.hoverState = 'out'
13808     
13809         if (!this.delay || !this.delay.hide) {
13810             this.hide();
13811             return 
13812         }
13813         var _t = this;
13814         this.timeout = setTimeout(function () {
13815             if (_t.hoverState == 'out') {
13816                 _t.hide();
13817             }
13818         }, this.delay.hide)
13819     },
13820     
13821     show : function (on_el)
13822     {
13823         if (!on_el) {
13824             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13825         }
13826         // set content.
13827         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13828         if (this.html !== false) {
13829             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13830         }
13831         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13832         if (!this.title.length) {
13833             this.el.select('.popover-title',true).hide();
13834         }
13835         
13836         var placement = typeof this.placement == 'function' ?
13837             this.placement.call(this, this.el, on_el) :
13838             this.placement;
13839             
13840         var autoToken = /\s?auto?\s?/i;
13841         var autoPlace = autoToken.test(placement);
13842         if (autoPlace) {
13843             placement = placement.replace(autoToken, '') || 'top';
13844         }
13845         
13846         //this.el.detach()
13847         //this.el.setXY([0,0]);
13848         this.el.show();
13849         this.el.dom.style.display='block';
13850         this.el.addClass(placement);
13851         
13852         //this.el.appendTo(on_el);
13853         
13854         var p = this.getPosition();
13855         var box = this.el.getBox();
13856         
13857         if (autoPlace) {
13858             // fixme..
13859         }
13860         var align = Roo.bootstrap.Popover.alignment[placement]
13861         this.el.alignTo(on_el, align[0],align[1]);
13862         //var arrow = this.el.select('.arrow',true).first();
13863         //arrow.set(align[2], 
13864         
13865         this.el.addClass('in');
13866         this.hoverState = null;
13867         
13868         if (this.el.hasClass('fade')) {
13869             // fade it?
13870         }
13871         
13872     },
13873     hide : function()
13874     {
13875         this.el.setXY([0,0]);
13876         this.el.removeClass('in');
13877         this.el.hide();
13878         
13879     }
13880     
13881 });
13882
13883 Roo.bootstrap.Popover.alignment = {
13884     'left' : ['r-l', [-10,0], 'right'],
13885     'right' : ['l-r', [10,0], 'left'],
13886     'bottom' : ['t-b', [0,10], 'top'],
13887     'top' : [ 'b-t', [0,-10], 'bottom']
13888 };
13889
13890  /*
13891  * - LGPL
13892  *
13893  * Progress
13894  * 
13895  */
13896
13897 /**
13898  * @class Roo.bootstrap.Progress
13899  * @extends Roo.bootstrap.Component
13900  * Bootstrap Progress class
13901  * @cfg {Boolean} striped striped of the progress bar
13902  * @cfg {Boolean} active animated of the progress bar
13903  * 
13904  * 
13905  * @constructor
13906  * Create a new Progress
13907  * @param {Object} config The config object
13908  */
13909
13910 Roo.bootstrap.Progress = function(config){
13911     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13912 };
13913
13914 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13915     
13916     striped : false,
13917     active: false,
13918     
13919     getAutoCreate : function(){
13920         var cfg = {
13921             tag: 'div',
13922             cls: 'progress'
13923         };
13924         
13925         
13926         if(this.striped){
13927             cfg.cls += ' progress-striped';
13928         }
13929       
13930         if(this.active){
13931             cfg.cls += ' active';
13932         }
13933         
13934         
13935         return cfg;
13936     }
13937    
13938 });
13939
13940  
13941
13942  /*
13943  * - LGPL
13944  *
13945  * ProgressBar
13946  * 
13947  */
13948
13949 /**
13950  * @class Roo.bootstrap.ProgressBar
13951  * @extends Roo.bootstrap.Component
13952  * Bootstrap ProgressBar class
13953  * @cfg {Number} aria_valuenow aria-value now
13954  * @cfg {Number} aria_valuemin aria-value min
13955  * @cfg {Number} aria_valuemax aria-value max
13956  * @cfg {String} label label for the progress bar
13957  * @cfg {String} panel (success | info | warning | danger )
13958  * @cfg {String} role role of the progress bar
13959  * @cfg {String} sr_only text
13960  * 
13961  * 
13962  * @constructor
13963  * Create a new ProgressBar
13964  * @param {Object} config The config object
13965  */
13966
13967 Roo.bootstrap.ProgressBar = function(config){
13968     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13969 };
13970
13971 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13972     
13973     aria_valuenow : 0,
13974     aria_valuemin : 0,
13975     aria_valuemax : 100,
13976     label : false,
13977     panel : false,
13978     role : false,
13979     sr_only: false,
13980     
13981     getAutoCreate : function()
13982     {
13983         
13984         var cfg = {
13985             tag: 'div',
13986             cls: 'progress-bar',
13987             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13988         };
13989         
13990         if(this.sr_only){
13991             cfg.cn = {
13992                 tag: 'span',
13993                 cls: 'sr-only',
13994                 html: this.sr_only
13995             }
13996         }
13997         
13998         if(this.role){
13999             cfg.role = this.role;
14000         }
14001         
14002         if(this.aria_valuenow){
14003             cfg['aria-valuenow'] = this.aria_valuenow;
14004         }
14005         
14006         if(this.aria_valuemin){
14007             cfg['aria-valuemin'] = this.aria_valuemin;
14008         }
14009         
14010         if(this.aria_valuemax){
14011             cfg['aria-valuemax'] = this.aria_valuemax;
14012         }
14013         
14014         if(this.label && !this.sr_only){
14015             cfg.html = this.label;
14016         }
14017         
14018         if(this.panel){
14019             cfg.cls += ' progress-bar-' + this.panel;
14020         }
14021         
14022         return cfg;
14023     },
14024     
14025     update : function(aria_valuenow)
14026     {
14027         this.aria_valuenow = aria_valuenow;
14028         
14029         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14030     }
14031    
14032 });
14033
14034  
14035
14036  /*
14037  * - LGPL
14038  *
14039  * column
14040  * 
14041  */
14042
14043 /**
14044  * @class Roo.bootstrap.TabGroup
14045  * @extends Roo.bootstrap.Column
14046  * Bootstrap Column class
14047  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14048  * @cfg {Boolean} carousel true to make the group behave like a carousel
14049  * 
14050  * @constructor
14051  * Create a new TabGroup
14052  * @param {Object} config The config object
14053  */
14054
14055 Roo.bootstrap.TabGroup = function(config){
14056     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14057     if (!this.navId) {
14058         this.navId = Roo.id();
14059     }
14060     this.tabs = [];
14061     Roo.bootstrap.TabGroup.register(this);
14062     
14063 };
14064
14065 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14066     
14067     carousel : false,
14068     transition : false,
14069      
14070     getAutoCreate : function()
14071     {
14072         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14073         
14074         cfg.cls += ' tab-content';
14075         
14076         if (this.carousel) {
14077             cfg.cls += ' carousel slide';
14078             cfg.cn = [{
14079                cls : 'carousel-inner'
14080             }]
14081         }
14082         
14083         
14084         return cfg;
14085     },
14086     getChildContainer : function()
14087     {
14088         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14089     },
14090     
14091     /**
14092     * register a Navigation item
14093     * @param {Roo.bootstrap.NavItem} the navitem to add
14094     */
14095     register : function(item)
14096     {
14097         this.tabs.push( item);
14098         item.navId = this.navId; // not really needed..
14099     
14100     },
14101     
14102     getActivePanel : function()
14103     {
14104         var r = false;
14105         Roo.each(this.tabs, function(t) {
14106             if (t.active) {
14107                 r = t;
14108                 return false;
14109             }
14110             return null;
14111         });
14112         return r;
14113         
14114     },
14115     getPanelByName : function(n)
14116     {
14117         var r = false;
14118         Roo.each(this.tabs, function(t) {
14119             if (t.tabId == n) {
14120                 r = t;
14121                 return false;
14122             }
14123             return null;
14124         });
14125         return r;
14126     },
14127     indexOfPanel : function(p)
14128     {
14129         var r = false;
14130         Roo.each(this.tabs, function(t,i) {
14131             if (t.tabId == p.tabId) {
14132                 r = i;
14133                 return false;
14134             }
14135             return null;
14136         });
14137         return r;
14138     },
14139     /**
14140      * show a specific panel
14141      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14142      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14143      */
14144     showPanel : function (pan)
14145     {
14146         
14147         if (typeof(pan) == 'number') {
14148             pan = this.tabs[pan];
14149         }
14150         if (typeof(pan) == 'string') {
14151             pan = this.getPanelByName(pan);
14152         }
14153         if (pan.tabId == this.getActivePanel().tabId) {
14154             return true;
14155         }
14156         var cur = this.getActivePanel();
14157         
14158         if (false === cur.fireEvent('beforedeactivate')) {
14159             return false;
14160         }
14161         
14162         if (this.carousel) {
14163             this.transition = true;
14164             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14165             var lr = dir == 'next' ? 'left' : 'right';
14166             pan.el.addClass(dir); // or prev
14167             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14168             cur.el.addClass(lr); // or right
14169             pan.el.addClass(lr);
14170             
14171             var _this = this;
14172             cur.el.on('transitionend', function() {
14173                 Roo.log("trans end?");
14174                 
14175                 pan.el.removeClass([lr,dir]);
14176                 pan.setActive(true);
14177                 
14178                 cur.el.removeClass([lr]);
14179                 cur.setActive(false);
14180                 
14181                 _this.transition = false;
14182                 
14183             }, this, { single:  true } );
14184             return true;
14185         }
14186         
14187         cur.setActive(false);
14188         pan.setActive(true);
14189         return true;
14190         
14191     },
14192     showPanelNext : function()
14193     {
14194         var i = this.indexOfPanel(this.getActivePanel());
14195         if (i > this.tabs.length) {
14196             return;
14197         }
14198         this.showPanel(this.tabs[i+1]);
14199     },
14200     showPanelPrev : function()
14201     {
14202         var i = this.indexOfPanel(this.getActivePanel());
14203         if (i  < 1) {
14204             return;
14205         }
14206         this.showPanel(this.tabs[i-1]);
14207     }
14208     
14209     
14210   
14211 });
14212
14213  
14214
14215  
14216  
14217 Roo.apply(Roo.bootstrap.TabGroup, {
14218     
14219     groups: {},
14220      /**
14221     * register a Navigation Group
14222     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14223     */
14224     register : function(navgrp)
14225     {
14226         this.groups[navgrp.navId] = navgrp;
14227         
14228     },
14229     /**
14230     * fetch a Navigation Group based on the navigation ID
14231     * if one does not exist , it will get created.
14232     * @param {string} the navgroup to add
14233     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14234     */
14235     get: function(navId) {
14236         if (typeof(this.groups[navId]) == 'undefined') {
14237             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14238         }
14239         return this.groups[navId] ;
14240     }
14241     
14242     
14243     
14244 });
14245
14246  /*
14247  * - LGPL
14248  *
14249  * TabPanel
14250  * 
14251  */
14252
14253 /**
14254  * @class Roo.bootstrap.TabPanel
14255  * @extends Roo.bootstrap.Component
14256  * Bootstrap TabPanel class
14257  * @cfg {Boolean} active panel active
14258  * @cfg {String} html panel content
14259  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14260  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14261  * 
14262  * 
14263  * @constructor
14264  * Create a new TabPanel
14265  * @param {Object} config The config object
14266  */
14267
14268 Roo.bootstrap.TabPanel = function(config){
14269     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14270     this.addEvents({
14271         /**
14272              * @event changed
14273              * Fires when the active status changes
14274              * @param {Roo.bootstrap.TabPanel} this
14275              * @param {Boolean} state the new state
14276             
14277          */
14278         'changed': true,
14279         /**
14280              * @event beforedeactivate
14281              * Fires before a tab is de-activated - can be used to do validation on a form.
14282              * @param {Roo.bootstrap.TabPanel} this
14283              * @return {Boolean} false if there is an error
14284             
14285          */
14286         'beforedeactivate': true
14287      });
14288     
14289     this.tabId = this.tabId || Roo.id();
14290   
14291 };
14292
14293 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14294     
14295     active: false,
14296     html: false,
14297     tabId: false,
14298     navId : false,
14299     
14300     getAutoCreate : function(){
14301         var cfg = {
14302             tag: 'div',
14303             // item is needed for carousel - not sure if it has any effect otherwise
14304             cls: 'tab-pane item',
14305             html: this.html || ''
14306         };
14307         
14308         if(this.active){
14309             cfg.cls += ' active';
14310         }
14311         
14312         if(this.tabId){
14313             cfg.tabId = this.tabId;
14314         }
14315         
14316         
14317         return cfg;
14318     },
14319     
14320     initEvents:  function()
14321     {
14322         Roo.log('-------- init events on tab panel ---------');
14323         
14324         var p = this.parent();
14325         this.navId = this.navId || p.navId;
14326         
14327         if (typeof(this.navId) != 'undefined') {
14328             // not really needed.. but just in case.. parent should be a NavGroup.
14329             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14330             Roo.log(['register', tg, this]);
14331             tg.register(this);
14332         }
14333     },
14334     
14335     
14336     onRender : function(ct, position)
14337     {
14338        // Roo.log("Call onRender: " + this.xtype);
14339         
14340         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14341         
14342         
14343         
14344         
14345         
14346     },
14347     
14348     setActive: function(state)
14349     {
14350         Roo.log("panel - set active " + this.tabId + "=" + state);
14351         
14352         this.active = state;
14353         if (!state) {
14354             this.el.removeClass('active');
14355             
14356         } else  if (!this.el.hasClass('active')) {
14357             this.el.addClass('active');
14358         }
14359         this.fireEvent('changed', this, state);
14360     }
14361     
14362     
14363 });
14364  
14365
14366  
14367
14368  /*
14369  * - LGPL
14370  *
14371  * DateField
14372  * 
14373  */
14374
14375 /**
14376  * @class Roo.bootstrap.DateField
14377  * @extends Roo.bootstrap.Input
14378  * Bootstrap DateField class
14379  * @cfg {Number} weekStart default 0
14380  * @cfg {Number} weekStart default 0
14381  * @cfg {Number} viewMode default empty, (months|years)
14382  * @cfg {Number} minViewMode default empty, (months|years)
14383  * @cfg {Number} startDate default -Infinity
14384  * @cfg {Number} endDate default Infinity
14385  * @cfg {Boolean} todayHighlight default false
14386  * @cfg {Boolean} todayBtn default false
14387  * @cfg {Boolean} calendarWeeks default false
14388  * @cfg {Object} daysOfWeekDisabled default empty
14389  * 
14390  * @cfg {Boolean} keyboardNavigation default true
14391  * @cfg {String} language default en
14392  * 
14393  * @constructor
14394  * Create a new DateField
14395  * @param {Object} config The config object
14396  */
14397
14398 Roo.bootstrap.DateField = function(config){
14399     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14400      this.addEvents({
14401             /**
14402              * @event show
14403              * Fires when this field show.
14404              * @param {Roo.bootstrap.DateField} this
14405              * @param {Mixed} date The date value
14406              */
14407             show : true,
14408             /**
14409              * @event show
14410              * Fires when this field hide.
14411              * @param {Roo.bootstrap.DateField} this
14412              * @param {Mixed} date The date value
14413              */
14414             hide : true,
14415             /**
14416              * @event select
14417              * Fires when select a date.
14418              * @param {Roo.bootstrap.DateField} this
14419              * @param {Mixed} date The date value
14420              */
14421             select : true
14422         });
14423 };
14424
14425 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14426     
14427     /**
14428      * @cfg {String} format
14429      * The default date format string which can be overriden for localization support.  The format must be
14430      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14431      */
14432     format : "m/d/y",
14433     /**
14434      * @cfg {String} altFormats
14435      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14436      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14437      */
14438     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14439     
14440     weekStart : 0,
14441     
14442     viewMode : '',
14443     
14444     minViewMode : '',
14445     
14446     todayHighlight : false,
14447     
14448     todayBtn: false,
14449     
14450     language: 'en',
14451     
14452     keyboardNavigation: true,
14453     
14454     calendarWeeks: false,
14455     
14456     startDate: -Infinity,
14457     
14458     endDate: Infinity,
14459     
14460     daysOfWeekDisabled: [],
14461     
14462     _events: [],
14463     
14464     UTCDate: function()
14465     {
14466         return new Date(Date.UTC.apply(Date, arguments));
14467     },
14468     
14469     UTCToday: function()
14470     {
14471         var today = new Date();
14472         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14473     },
14474     
14475     getDate: function() {
14476             var d = this.getUTCDate();
14477             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14478     },
14479     
14480     getUTCDate: function() {
14481             return this.date;
14482     },
14483     
14484     setDate: function(d) {
14485             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14486     },
14487     
14488     setUTCDate: function(d) {
14489             this.date = d;
14490             this.setValue(this.formatDate(this.date));
14491     },
14492         
14493     onRender: function(ct, position)
14494     {
14495         
14496         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14497         
14498         this.language = this.language || 'en';
14499         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14500         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14501         
14502         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14503         this.format = this.format || 'm/d/y';
14504         this.isInline = false;
14505         this.isInput = true;
14506         this.component = this.el.select('.add-on', true).first() || false;
14507         this.component = (this.component && this.component.length === 0) ? false : this.component;
14508         this.hasInput = this.component && this.inputEL().length;
14509         
14510         if (typeof(this.minViewMode === 'string')) {
14511             switch (this.minViewMode) {
14512                 case 'months':
14513                     this.minViewMode = 1;
14514                     break;
14515                 case 'years':
14516                     this.minViewMode = 2;
14517                     break;
14518                 default:
14519                     this.minViewMode = 0;
14520                     break;
14521             }
14522         }
14523         
14524         if (typeof(this.viewMode === 'string')) {
14525             switch (this.viewMode) {
14526                 case 'months':
14527                     this.viewMode = 1;
14528                     break;
14529                 case 'years':
14530                     this.viewMode = 2;
14531                     break;
14532                 default:
14533                     this.viewMode = 0;
14534                     break;
14535             }
14536         }
14537                 
14538         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14539         
14540 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14541         
14542         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14543         
14544         this.picker().on('mousedown', this.onMousedown, this);
14545         this.picker().on('click', this.onClick, this);
14546         
14547         this.picker().addClass('datepicker-dropdown');
14548         
14549         this.startViewMode = this.viewMode;
14550         
14551         
14552         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14553             if(!this.calendarWeeks){
14554                 v.remove();
14555                 return;
14556             };
14557             
14558             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14559             v.attr('colspan', function(i, val){
14560                 return parseInt(val) + 1;
14561             });
14562         })
14563                         
14564         
14565         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14566         
14567         this.setStartDate(this.startDate);
14568         this.setEndDate(this.endDate);
14569         
14570         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14571         
14572         this.fillDow();
14573         this.fillMonths();
14574         this.update();
14575         this.showMode();
14576         
14577         if(this.isInline) {
14578             this.show();
14579         }
14580     },
14581     
14582     picker : function()
14583     {
14584         return this.pickerEl;
14585 //        return this.el.select('.datepicker', true).first();
14586     },
14587     
14588     fillDow: function()
14589     {
14590         var dowCnt = this.weekStart;
14591         
14592         var dow = {
14593             tag: 'tr',
14594             cn: [
14595                 
14596             ]
14597         };
14598         
14599         if(this.calendarWeeks){
14600             dow.cn.push({
14601                 tag: 'th',
14602                 cls: 'cw',
14603                 html: '&nbsp;'
14604             })
14605         }
14606         
14607         while (dowCnt < this.weekStart + 7) {
14608             dow.cn.push({
14609                 tag: 'th',
14610                 cls: 'dow',
14611                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14612             });
14613         }
14614         
14615         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14616     },
14617     
14618     fillMonths: function()
14619     {    
14620         var i = 0
14621         var months = this.picker().select('>.datepicker-months td', true).first();
14622         
14623         months.dom.innerHTML = '';
14624         
14625         while (i < 12) {
14626             var month = {
14627                 tag: 'span',
14628                 cls: 'month',
14629                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14630             }
14631             
14632             months.createChild(month);
14633         }
14634         
14635     },
14636     
14637     update: function()
14638     {
14639         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;
14640         
14641         if (this.date < this.startDate) {
14642             this.viewDate = new Date(this.startDate);
14643         } else if (this.date > this.endDate) {
14644             this.viewDate = new Date(this.endDate);
14645         } else {
14646             this.viewDate = new Date(this.date);
14647         }
14648         
14649         this.fill();
14650     },
14651     
14652     fill: function() 
14653     {
14654         var d = new Date(this.viewDate),
14655                 year = d.getUTCFullYear(),
14656                 month = d.getUTCMonth(),
14657                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14658                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14659                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14660                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14661                 currentDate = this.date && this.date.valueOf(),
14662                 today = this.UTCToday();
14663         
14664         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14665         
14666 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14667         
14668 //        this.picker.select('>tfoot th.today').
14669 //                                              .text(dates[this.language].today)
14670 //                                              .toggle(this.todayBtn !== false);
14671     
14672         this.updateNavArrows();
14673         this.fillMonths();
14674                                                 
14675         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14676         
14677         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14678          
14679         prevMonth.setUTCDate(day);
14680         
14681         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14682         
14683         var nextMonth = new Date(prevMonth);
14684         
14685         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14686         
14687         nextMonth = nextMonth.valueOf();
14688         
14689         var fillMonths = false;
14690         
14691         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14692         
14693         while(prevMonth.valueOf() < nextMonth) {
14694             var clsName = '';
14695             
14696             if (prevMonth.getUTCDay() === this.weekStart) {
14697                 if(fillMonths){
14698                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14699                 }
14700                     
14701                 fillMonths = {
14702                     tag: 'tr',
14703                     cn: []
14704                 };
14705                 
14706                 if(this.calendarWeeks){
14707                     // ISO 8601: First week contains first thursday.
14708                     // ISO also states week starts on Monday, but we can be more abstract here.
14709                     var
14710                     // Start of current week: based on weekstart/current date
14711                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14712                     // Thursday of this week
14713                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14714                     // First Thursday of year, year from thursday
14715                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14716                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14717                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14718                     
14719                     fillMonths.cn.push({
14720                         tag: 'td',
14721                         cls: 'cw',
14722                         html: calWeek
14723                     });
14724                 }
14725             }
14726             
14727             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14728                 clsName += ' old';
14729             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14730                 clsName += ' new';
14731             }
14732             if (this.todayHighlight &&
14733                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14734                 prevMonth.getUTCMonth() == today.getMonth() &&
14735                 prevMonth.getUTCDate() == today.getDate()) {
14736                 clsName += ' today';
14737             }
14738             
14739             if (currentDate && prevMonth.valueOf() === currentDate) {
14740                 clsName += ' active';
14741             }
14742             
14743             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14744                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14745                     clsName += ' disabled';
14746             }
14747             
14748             fillMonths.cn.push({
14749                 tag: 'td',
14750                 cls: 'day ' + clsName,
14751                 html: prevMonth.getDate()
14752             })
14753             
14754             prevMonth.setDate(prevMonth.getDate()+1);
14755         }
14756           
14757         var currentYear = this.date && this.date.getUTCFullYear();
14758         var currentMonth = this.date && this.date.getUTCMonth();
14759         
14760         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14761         
14762         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14763             v.removeClass('active');
14764             
14765             if(currentYear === year && k === currentMonth){
14766                 v.addClass('active');
14767             }
14768             
14769             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14770                 v.addClass('disabled');
14771             }
14772             
14773         });
14774         
14775         
14776         year = parseInt(year/10, 10) * 10;
14777         
14778         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14779         
14780         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14781         
14782         year -= 1;
14783         for (var i = -1; i < 11; i++) {
14784             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14785                 tag: 'span',
14786                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14787                 html: year
14788             })
14789             
14790             year += 1;
14791         }
14792     },
14793     
14794     showMode: function(dir) 
14795     {
14796         if (dir) {
14797             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14798         }
14799         Roo.each(this.picker().select('>div',true).elements, function(v){
14800             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14801             v.hide();
14802         });
14803         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14804     },
14805     
14806     place: function()
14807     {
14808         if(this.isInline) return;
14809         
14810         this.picker().removeClass(['bottom', 'top']);
14811         
14812         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14813             /*
14814              * place to the top of element!
14815              *
14816              */
14817             
14818             this.picker().addClass('top');
14819             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14820             
14821             return;
14822         }
14823         
14824         this.picker().addClass('bottom');
14825         
14826         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14827     },
14828     
14829     parseDate : function(value)
14830     {
14831         if(!value || value instanceof Date){
14832             return value;
14833         }
14834         var v = Date.parseDate(value, this.format);
14835         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14836             v = Date.parseDate(value, 'Y-m-d');
14837         }
14838         if(!v && this.altFormats){
14839             if(!this.altFormatsArray){
14840                 this.altFormatsArray = this.altFormats.split("|");
14841             }
14842             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14843                 v = Date.parseDate(value, this.altFormatsArray[i]);
14844             }
14845         }
14846         return v;
14847     },
14848     
14849     formatDate : function(date, fmt)
14850     {
14851         return (!date || !(date instanceof Date)) ?
14852         date : date.dateFormat(fmt || this.format);
14853     },
14854     
14855     onFocus : function()
14856     {
14857         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14858         this.show();
14859     },
14860     
14861     onBlur : function()
14862     {
14863         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14864         
14865         var d = this.inputEl().getValue();
14866         
14867         this.setValue(d);
14868                 
14869         this.hide();
14870     },
14871     
14872     show : function()
14873     {
14874         this.picker().show();
14875         this.update();
14876         this.place();
14877         
14878         this.fireEvent('show', this, this.date);
14879     },
14880     
14881     hide : function()
14882     {
14883         if(this.isInline) return;
14884         this.picker().hide();
14885         this.viewMode = this.startViewMode;
14886         this.showMode();
14887         
14888         this.fireEvent('hide', this, this.date);
14889         
14890     },
14891     
14892     onMousedown: function(e)
14893     {
14894         e.stopPropagation();
14895         e.preventDefault();
14896     },
14897     
14898     keyup: function(e)
14899     {
14900         Roo.bootstrap.DateField.superclass.keyup.call(this);
14901         this.update();
14902     },
14903
14904     setValue: function(v)
14905     {
14906         
14907         // v can be a string or a date..
14908         
14909         
14910         var d = new Date(this.parseDate(v) ).clearTime();
14911         
14912         Roo.log(d);
14913         Roo.log(d);
14914         if(isNaN(d.getTime())){
14915             this.date = this.viewDate = '';
14916             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14917             return;
14918         }
14919         
14920         v = this.formatDate(d);
14921         
14922         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14923         
14924         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14925      
14926         this.update();
14927
14928         this.fireEvent('select', this, this.date);
14929         
14930     },
14931     
14932     getValue: function()
14933     {
14934         return this.formatDate(this.date);
14935     },
14936     
14937     fireKey: function(e)
14938     {
14939         if (!this.picker().isVisible()){
14940             if (e.keyCode == 27) // allow escape to hide and re-show picker
14941                 this.show();
14942             return;
14943         }
14944         
14945         var dateChanged = false,
14946         dir, day, month,
14947         newDate, newViewDate;
14948         
14949         switch(e.keyCode){
14950             case 27: // escape
14951                 this.hide();
14952                 e.preventDefault();
14953                 break;
14954             case 37: // left
14955             case 39: // right
14956                 if (!this.keyboardNavigation) break;
14957                 dir = e.keyCode == 37 ? -1 : 1;
14958                 
14959                 if (e.ctrlKey){
14960                     newDate = this.moveYear(this.date, dir);
14961                     newViewDate = this.moveYear(this.viewDate, dir);
14962                 } else if (e.shiftKey){
14963                     newDate = this.moveMonth(this.date, dir);
14964                     newViewDate = this.moveMonth(this.viewDate, dir);
14965                 } else {
14966                     newDate = new Date(this.date);
14967                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14968                     newViewDate = new Date(this.viewDate);
14969                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14970                 }
14971                 if (this.dateWithinRange(newDate)){
14972                     this.date = newDate;
14973                     this.viewDate = newViewDate;
14974                     this.setValue(this.formatDate(this.date));
14975 //                    this.update();
14976                     e.preventDefault();
14977                     dateChanged = true;
14978                 }
14979                 break;
14980             case 38: // up
14981             case 40: // down
14982                 if (!this.keyboardNavigation) break;
14983                 dir = e.keyCode == 38 ? -1 : 1;
14984                 if (e.ctrlKey){
14985                     newDate = this.moveYear(this.date, dir);
14986                     newViewDate = this.moveYear(this.viewDate, dir);
14987                 } else if (e.shiftKey){
14988                     newDate = this.moveMonth(this.date, dir);
14989                     newViewDate = this.moveMonth(this.viewDate, dir);
14990                 } else {
14991                     newDate = new Date(this.date);
14992                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14993                     newViewDate = new Date(this.viewDate);
14994                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14995                 }
14996                 if (this.dateWithinRange(newDate)){
14997                     this.date = newDate;
14998                     this.viewDate = newViewDate;
14999                     this.setValue(this.formatDate(this.date));
15000 //                    this.update();
15001                     e.preventDefault();
15002                     dateChanged = true;
15003                 }
15004                 break;
15005             case 13: // enter
15006                 this.setValue(this.formatDate(this.date));
15007                 this.hide();
15008                 e.preventDefault();
15009                 break;
15010             case 9: // tab
15011                 this.setValue(this.formatDate(this.date));
15012                 this.hide();
15013                 break;
15014             case 16: // shift
15015             case 17: // ctrl
15016             case 18: // alt
15017                 break;
15018             default :
15019                 this.hide();
15020                 
15021         }
15022     },
15023     
15024     
15025     onClick: function(e) 
15026     {
15027         e.stopPropagation();
15028         e.preventDefault();
15029         
15030         var target = e.getTarget();
15031         
15032         if(target.nodeName.toLowerCase() === 'i'){
15033             target = Roo.get(target).dom.parentNode;
15034         }
15035         
15036         var nodeName = target.nodeName;
15037         var className = target.className;
15038         var html = target.innerHTML;
15039         //Roo.log(nodeName);
15040         
15041         switch(nodeName.toLowerCase()) {
15042             case 'th':
15043                 switch(className) {
15044                     case 'switch':
15045                         this.showMode(1);
15046                         break;
15047                     case 'prev':
15048                     case 'next':
15049                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15050                         switch(this.viewMode){
15051                                 case 0:
15052                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15053                                         break;
15054                                 case 1:
15055                                 case 2:
15056                                         this.viewDate = this.moveYear(this.viewDate, dir);
15057                                         break;
15058                         }
15059                         this.fill();
15060                         break;
15061                     case 'today':
15062                         var date = new Date();
15063                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15064 //                        this.fill()
15065                         this.setValue(this.formatDate(this.date));
15066                         
15067                         this.hide();
15068                         break;
15069                 }
15070                 break;
15071             case 'span':
15072                 if (className.indexOf('disabled') < 0) {
15073                     this.viewDate.setUTCDate(1);
15074                     if (className.indexOf('month') > -1) {
15075                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15076                     } else {
15077                         var year = parseInt(html, 10) || 0;
15078                         this.viewDate.setUTCFullYear(year);
15079                         
15080                     }
15081                     this.showMode(-1);
15082                     this.fill();
15083                 }
15084                 break;
15085                 
15086             case 'td':
15087                 //Roo.log(className);
15088                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15089                     var day = parseInt(html, 10) || 1;
15090                     var year = this.viewDate.getUTCFullYear(),
15091                         month = this.viewDate.getUTCMonth();
15092
15093                     if (className.indexOf('old') > -1) {
15094                         if(month === 0 ){
15095                             month = 11;
15096                             year -= 1;
15097                         }else{
15098                             month -= 1;
15099                         }
15100                     } else if (className.indexOf('new') > -1) {
15101                         if (month == 11) {
15102                             month = 0;
15103                             year += 1;
15104                         } else {
15105                             month += 1;
15106                         }
15107                     }
15108                     //Roo.log([year,month,day]);
15109                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15110                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15111 //                    this.fill();
15112                     //Roo.log(this.formatDate(this.date));
15113                     this.setValue(this.formatDate(this.date));
15114                     this.hide();
15115                 }
15116                 break;
15117         }
15118     },
15119     
15120     setStartDate: function(startDate)
15121     {
15122         this.startDate = startDate || -Infinity;
15123         if (this.startDate !== -Infinity) {
15124             this.startDate = this.parseDate(this.startDate);
15125         }
15126         this.update();
15127         this.updateNavArrows();
15128     },
15129
15130     setEndDate: function(endDate)
15131     {
15132         this.endDate = endDate || Infinity;
15133         if (this.endDate !== Infinity) {
15134             this.endDate = this.parseDate(this.endDate);
15135         }
15136         this.update();
15137         this.updateNavArrows();
15138     },
15139     
15140     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15141     {
15142         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15143         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15144             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15145         }
15146         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15147             return parseInt(d, 10);
15148         });
15149         this.update();
15150         this.updateNavArrows();
15151     },
15152     
15153     updateNavArrows: function() 
15154     {
15155         var d = new Date(this.viewDate),
15156         year = d.getUTCFullYear(),
15157         month = d.getUTCMonth();
15158         
15159         Roo.each(this.picker().select('.prev', true).elements, function(v){
15160             v.show();
15161             switch (this.viewMode) {
15162                 case 0:
15163
15164                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15165                         v.hide();
15166                     }
15167                     break;
15168                 case 1:
15169                 case 2:
15170                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15171                         v.hide();
15172                     }
15173                     break;
15174             }
15175         });
15176         
15177         Roo.each(this.picker().select('.next', true).elements, function(v){
15178             v.show();
15179             switch (this.viewMode) {
15180                 case 0:
15181
15182                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15183                         v.hide();
15184                     }
15185                     break;
15186                 case 1:
15187                 case 2:
15188                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15189                         v.hide();
15190                     }
15191                     break;
15192             }
15193         })
15194     },
15195     
15196     moveMonth: function(date, dir)
15197     {
15198         if (!dir) return date;
15199         var new_date = new Date(date.valueOf()),
15200         day = new_date.getUTCDate(),
15201         month = new_date.getUTCMonth(),
15202         mag = Math.abs(dir),
15203         new_month, test;
15204         dir = dir > 0 ? 1 : -1;
15205         if (mag == 1){
15206             test = dir == -1
15207             // If going back one month, make sure month is not current month
15208             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15209             ? function(){
15210                 return new_date.getUTCMonth() == month;
15211             }
15212             // If going forward one month, make sure month is as expected
15213             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15214             : function(){
15215                 return new_date.getUTCMonth() != new_month;
15216             };
15217             new_month = month + dir;
15218             new_date.setUTCMonth(new_month);
15219             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15220             if (new_month < 0 || new_month > 11)
15221                 new_month = (new_month + 12) % 12;
15222         } else {
15223             // For magnitudes >1, move one month at a time...
15224             for (var i=0; i<mag; i++)
15225                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15226                 new_date = this.moveMonth(new_date, dir);
15227             // ...then reset the day, keeping it in the new month
15228             new_month = new_date.getUTCMonth();
15229             new_date.setUTCDate(day);
15230             test = function(){
15231                 return new_month != new_date.getUTCMonth();
15232             };
15233         }
15234         // Common date-resetting loop -- if date is beyond end of month, make it
15235         // end of month
15236         while (test()){
15237             new_date.setUTCDate(--day);
15238             new_date.setUTCMonth(new_month);
15239         }
15240         return new_date;
15241     },
15242
15243     moveYear: function(date, dir)
15244     {
15245         return this.moveMonth(date, dir*12);
15246     },
15247
15248     dateWithinRange: function(date)
15249     {
15250         return date >= this.startDate && date <= this.endDate;
15251     },
15252
15253     
15254     remove: function() 
15255     {
15256         this.picker().remove();
15257     }
15258    
15259 });
15260
15261 Roo.apply(Roo.bootstrap.DateField,  {
15262     
15263     head : {
15264         tag: 'thead',
15265         cn: [
15266         {
15267             tag: 'tr',
15268             cn: [
15269             {
15270                 tag: 'th',
15271                 cls: 'prev',
15272                 html: '<i class="fa fa-arrow-left"/>'
15273             },
15274             {
15275                 tag: 'th',
15276                 cls: 'switch',
15277                 colspan: '5'
15278             },
15279             {
15280                 tag: 'th',
15281                 cls: 'next',
15282                 html: '<i class="fa fa-arrow-right"/>'
15283             }
15284
15285             ]
15286         }
15287         ]
15288     },
15289     
15290     content : {
15291         tag: 'tbody',
15292         cn: [
15293         {
15294             tag: 'tr',
15295             cn: [
15296             {
15297                 tag: 'td',
15298                 colspan: '7'
15299             }
15300             ]
15301         }
15302         ]
15303     },
15304     
15305     footer : {
15306         tag: 'tfoot',
15307         cn: [
15308         {
15309             tag: 'tr',
15310             cn: [
15311             {
15312                 tag: 'th',
15313                 colspan: '7',
15314                 cls: 'today'
15315             }
15316                     
15317             ]
15318         }
15319         ]
15320     },
15321     
15322     dates:{
15323         en: {
15324             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15325             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15326             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15327             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15328             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15329             today: "Today"
15330         }
15331     },
15332     
15333     modes: [
15334     {
15335         clsName: 'days',
15336         navFnc: 'Month',
15337         navStep: 1
15338     },
15339     {
15340         clsName: 'months',
15341         navFnc: 'FullYear',
15342         navStep: 1
15343     },
15344     {
15345         clsName: 'years',
15346         navFnc: 'FullYear',
15347         navStep: 10
15348     }]
15349 });
15350
15351 Roo.apply(Roo.bootstrap.DateField,  {
15352   
15353     template : {
15354         tag: 'div',
15355         cls: 'datepicker dropdown-menu',
15356         cn: [
15357         {
15358             tag: 'div',
15359             cls: 'datepicker-days',
15360             cn: [
15361             {
15362                 tag: 'table',
15363                 cls: 'table-condensed',
15364                 cn:[
15365                 Roo.bootstrap.DateField.head,
15366                 {
15367                     tag: 'tbody'
15368                 },
15369                 Roo.bootstrap.DateField.footer
15370                 ]
15371             }
15372             ]
15373         },
15374         {
15375             tag: 'div',
15376             cls: 'datepicker-months',
15377             cn: [
15378             {
15379                 tag: 'table',
15380                 cls: 'table-condensed',
15381                 cn:[
15382                 Roo.bootstrap.DateField.head,
15383                 Roo.bootstrap.DateField.content,
15384                 Roo.bootstrap.DateField.footer
15385                 ]
15386             }
15387             ]
15388         },
15389         {
15390             tag: 'div',
15391             cls: 'datepicker-years',
15392             cn: [
15393             {
15394                 tag: 'table',
15395                 cls: 'table-condensed',
15396                 cn:[
15397                 Roo.bootstrap.DateField.head,
15398                 Roo.bootstrap.DateField.content,
15399                 Roo.bootstrap.DateField.footer
15400                 ]
15401             }
15402             ]
15403         }
15404         ]
15405     }
15406 });
15407
15408  
15409
15410  /*
15411  * - LGPL
15412  *
15413  * TimeField
15414  * 
15415  */
15416
15417 /**
15418  * @class Roo.bootstrap.TimeField
15419  * @extends Roo.bootstrap.Input
15420  * Bootstrap DateField class
15421  * 
15422  * 
15423  * @constructor
15424  * Create a new TimeField
15425  * @param {Object} config The config object
15426  */
15427
15428 Roo.bootstrap.TimeField = function(config){
15429     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15430     this.addEvents({
15431             /**
15432              * @event show
15433              * Fires when this field show.
15434              * @param {Roo.bootstrap.DateField} this
15435              * @param {Mixed} date The date value
15436              */
15437             show : true,
15438             /**
15439              * @event show
15440              * Fires when this field hide.
15441              * @param {Roo.bootstrap.DateField} this
15442              * @param {Mixed} date The date value
15443              */
15444             hide : true,
15445             /**
15446              * @event select
15447              * Fires when select a date.
15448              * @param {Roo.bootstrap.DateField} this
15449              * @param {Mixed} date The date value
15450              */
15451             select : true
15452         });
15453 };
15454
15455 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15456     
15457     /**
15458      * @cfg {String} format
15459      * The default time format string which can be overriden for localization support.  The format must be
15460      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15461      */
15462     format : "H:i",
15463        
15464     onRender: function(ct, position)
15465     {
15466         
15467         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15468                 
15469         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15470         
15471         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15472         
15473         this.pop = this.picker().select('>.datepicker-time',true).first();
15474         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15475         
15476         this.picker().on('mousedown', this.onMousedown, this);
15477         this.picker().on('click', this.onClick, this);
15478         
15479         this.picker().addClass('datepicker-dropdown');
15480     
15481         this.fillTime();
15482         this.update();
15483             
15484         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15485         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15486         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15487         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15488         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15489         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15490
15491     },
15492     
15493     fireKey: function(e){
15494         if (!this.picker().isVisible()){
15495             if (e.keyCode == 27) // allow escape to hide and re-show picker
15496                 this.show();
15497             return;
15498         }
15499
15500         e.preventDefault();
15501         
15502         switch(e.keyCode){
15503             case 27: // escape
15504                 this.hide();
15505                 break;
15506             case 37: // left
15507             case 39: // right
15508                 this.onTogglePeriod();
15509                 break;
15510             case 38: // up
15511                 this.onIncrementMinutes();
15512                 break;
15513             case 40: // down
15514                 this.onDecrementMinutes();
15515                 break;
15516             case 13: // enter
15517             case 9: // tab
15518                 this.setTime();
15519                 break;
15520         }
15521     },
15522     
15523     onClick: function(e) {
15524         e.stopPropagation();
15525         e.preventDefault();
15526     },
15527     
15528     picker : function()
15529     {
15530         return this.el.select('.datepicker', true).first();
15531     },
15532     
15533     fillTime: function()
15534     {    
15535         var time = this.pop.select('tbody', true).first();
15536         
15537         time.dom.innerHTML = '';
15538         
15539         time.createChild({
15540             tag: 'tr',
15541             cn: [
15542                 {
15543                     tag: 'td',
15544                     cn: [
15545                         {
15546                             tag: 'a',
15547                             href: '#',
15548                             cls: 'btn',
15549                             cn: [
15550                                 {
15551                                     tag: 'span',
15552                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15553                                 }
15554                             ]
15555                         } 
15556                     ]
15557                 },
15558                 {
15559                     tag: 'td',
15560                     cls: 'separator'
15561                 },
15562                 {
15563                     tag: 'td',
15564                     cn: [
15565                         {
15566                             tag: 'a',
15567                             href: '#',
15568                             cls: 'btn',
15569                             cn: [
15570                                 {
15571                                     tag: 'span',
15572                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15573                                 }
15574                             ]
15575                         }
15576                     ]
15577                 },
15578                 {
15579                     tag: 'td',
15580                     cls: 'separator'
15581                 }
15582             ]
15583         });
15584         
15585         time.createChild({
15586             tag: 'tr',
15587             cn: [
15588                 {
15589                     tag: 'td',
15590                     cn: [
15591                         {
15592                             tag: 'span',
15593                             cls: 'timepicker-hour',
15594                             html: '00'
15595                         }  
15596                     ]
15597                 },
15598                 {
15599                     tag: 'td',
15600                     cls: 'separator',
15601                     html: ':'
15602                 },
15603                 {
15604                     tag: 'td',
15605                     cn: [
15606                         {
15607                             tag: 'span',
15608                             cls: 'timepicker-minute',
15609                             html: '00'
15610                         }  
15611                     ]
15612                 },
15613                 {
15614                     tag: 'td',
15615                     cls: 'separator'
15616                 },
15617                 {
15618                     tag: 'td',
15619                     cn: [
15620                         {
15621                             tag: 'button',
15622                             type: 'button',
15623                             cls: 'btn btn-primary period',
15624                             html: 'AM'
15625                             
15626                         }
15627                     ]
15628                 }
15629             ]
15630         });
15631         
15632         time.createChild({
15633             tag: 'tr',
15634             cn: [
15635                 {
15636                     tag: 'td',
15637                     cn: [
15638                         {
15639                             tag: 'a',
15640                             href: '#',
15641                             cls: 'btn',
15642                             cn: [
15643                                 {
15644                                     tag: 'span',
15645                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15646                                 }
15647                             ]
15648                         }
15649                     ]
15650                 },
15651                 {
15652                     tag: 'td',
15653                     cls: 'separator'
15654                 },
15655                 {
15656                     tag: 'td',
15657                     cn: [
15658                         {
15659                             tag: 'a',
15660                             href: '#',
15661                             cls: 'btn',
15662                             cn: [
15663                                 {
15664                                     tag: 'span',
15665                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15666                                 }
15667                             ]
15668                         }
15669                     ]
15670                 },
15671                 {
15672                     tag: 'td',
15673                     cls: 'separator'
15674                 }
15675             ]
15676         });
15677         
15678     },
15679     
15680     update: function()
15681     {
15682         
15683         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15684         
15685         this.fill();
15686     },
15687     
15688     fill: function() 
15689     {
15690         var hours = this.time.getHours();
15691         var minutes = this.time.getMinutes();
15692         var period = 'AM';
15693         
15694         if(hours > 11){
15695             period = 'PM';
15696         }
15697         
15698         if(hours == 0){
15699             hours = 12;
15700         }
15701         
15702         
15703         if(hours > 12){
15704             hours = hours - 12;
15705         }
15706         
15707         if(hours < 10){
15708             hours = '0' + hours;
15709         }
15710         
15711         if(minutes < 10){
15712             minutes = '0' + minutes;
15713         }
15714         
15715         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15716         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15717         this.pop.select('button', true).first().dom.innerHTML = period;
15718         
15719     },
15720     
15721     place: function()
15722     {   
15723         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15724         
15725         var cls = ['bottom'];
15726         
15727         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15728             cls.pop();
15729             cls.push('top');
15730         }
15731         
15732         cls.push('right');
15733         
15734         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15735             cls.pop();
15736             cls.push('left');
15737         }
15738         
15739         this.picker().addClass(cls.join('-'));
15740         
15741         var _this = this;
15742         
15743         Roo.each(cls, function(c){
15744             if(c == 'bottom'){
15745                 _this.picker().setTop(_this.inputEl().getHeight());
15746                 return;
15747             }
15748             if(c == 'top'){
15749                 _this.picker().setTop(0 - _this.picker().getHeight());
15750                 return;
15751             }
15752             
15753             if(c == 'left'){
15754                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15755                 return;
15756             }
15757             if(c == 'right'){
15758                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15759                 return;
15760             }
15761         });
15762         
15763     },
15764   
15765     onFocus : function()
15766     {
15767         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15768         this.show();
15769     },
15770     
15771     onBlur : function()
15772     {
15773         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15774         this.hide();
15775     },
15776     
15777     show : function()
15778     {
15779         this.picker().show();
15780         this.pop.show();
15781         this.update();
15782         this.place();
15783         
15784         this.fireEvent('show', this, this.date);
15785     },
15786     
15787     hide : function()
15788     {
15789         this.picker().hide();
15790         this.pop.hide();
15791         
15792         this.fireEvent('hide', this, this.date);
15793     },
15794     
15795     setTime : function()
15796     {
15797         this.hide();
15798         this.setValue(this.time.format(this.format));
15799         
15800         this.fireEvent('select', this, this.date);
15801         
15802         
15803     },
15804     
15805     onMousedown: function(e){
15806         e.stopPropagation();
15807         e.preventDefault();
15808     },
15809     
15810     onIncrementHours: function()
15811     {
15812         Roo.log('onIncrementHours');
15813         this.time = this.time.add(Date.HOUR, 1);
15814         this.update();
15815         
15816     },
15817     
15818     onDecrementHours: function()
15819     {
15820         Roo.log('onDecrementHours');
15821         this.time = this.time.add(Date.HOUR, -1);
15822         this.update();
15823     },
15824     
15825     onIncrementMinutes: function()
15826     {
15827         Roo.log('onIncrementMinutes');
15828         this.time = this.time.add(Date.MINUTE, 1);
15829         this.update();
15830     },
15831     
15832     onDecrementMinutes: function()
15833     {
15834         Roo.log('onDecrementMinutes');
15835         this.time = this.time.add(Date.MINUTE, -1);
15836         this.update();
15837     },
15838     
15839     onTogglePeriod: function()
15840     {
15841         Roo.log('onTogglePeriod');
15842         this.time = this.time.add(Date.HOUR, 12);
15843         this.update();
15844     }
15845     
15846    
15847 });
15848
15849 Roo.apply(Roo.bootstrap.TimeField,  {
15850     
15851     content : {
15852         tag: 'tbody',
15853         cn: [
15854             {
15855                 tag: 'tr',
15856                 cn: [
15857                 {
15858                     tag: 'td',
15859                     colspan: '7'
15860                 }
15861                 ]
15862             }
15863         ]
15864     },
15865     
15866     footer : {
15867         tag: 'tfoot',
15868         cn: [
15869             {
15870                 tag: 'tr',
15871                 cn: [
15872                 {
15873                     tag: 'th',
15874                     colspan: '7',
15875                     cls: '',
15876                     cn: [
15877                         {
15878                             tag: 'button',
15879                             cls: 'btn btn-info ok',
15880                             html: 'OK'
15881                         }
15882                     ]
15883                 }
15884
15885                 ]
15886             }
15887         ]
15888     }
15889 });
15890
15891 Roo.apply(Roo.bootstrap.TimeField,  {
15892   
15893     template : {
15894         tag: 'div',
15895         cls: 'datepicker dropdown-menu',
15896         cn: [
15897             {
15898                 tag: 'div',
15899                 cls: 'datepicker-time',
15900                 cn: [
15901                 {
15902                     tag: 'table',
15903                     cls: 'table-condensed',
15904                     cn:[
15905                     Roo.bootstrap.TimeField.content,
15906                     Roo.bootstrap.TimeField.footer
15907                     ]
15908                 }
15909                 ]
15910             }
15911         ]
15912     }
15913 });
15914
15915  
15916
15917  /*
15918  * - LGPL
15919  *
15920  * CheckBox
15921  * 
15922  */
15923
15924 /**
15925  * @class Roo.bootstrap.CheckBox
15926  * @extends Roo.bootstrap.Input
15927  * Bootstrap CheckBox class
15928  * 
15929  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15930  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15931  * @cfg {String} boxLabel The text that appears beside the checkbox
15932  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15933  * @cfg {Boolean} checked initnal the element
15934  * 
15935  * 
15936  * @constructor
15937  * Create a new CheckBox
15938  * @param {Object} config The config object
15939  */
15940
15941 Roo.bootstrap.CheckBox = function(config){
15942     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15943    
15944         this.addEvents({
15945             /**
15946             * @event check
15947             * Fires when the element is checked or unchecked.
15948             * @param {Roo.bootstrap.CheckBox} this This input
15949             * @param {Boolean} checked The new checked value
15950             */
15951            check : true
15952         });
15953 };
15954
15955 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15956     
15957     inputType: 'checkbox',
15958     inputValue: 1,
15959     valueOff: 0,
15960     boxLabel: false,
15961     checked: false,
15962     weight : false,
15963     
15964     getAutoCreate : function()
15965     {
15966         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15967         
15968         var id = Roo.id();
15969         
15970         var cfg = {};
15971         
15972         cfg.cls = 'form-group checkbox' //input-group
15973         
15974         
15975         
15976         
15977         var input =  {
15978             tag: 'input',
15979             id : id,
15980             type : this.inputType,
15981             value : (!this.checked) ? this.valueOff : this.inputValue,
15982             cls : 'roo-checkbox', //'form-box',
15983             placeholder : this.placeholder || ''
15984             
15985         };
15986         
15987         if (this.weight) { // Validity check?
15988             cfg.cls += " checkbox-" + this.weight;
15989         }
15990         
15991         if (this.disabled) {
15992             input.disabled=true;
15993         }
15994         
15995         if(this.checked){
15996             input.checked = this.checked;
15997         }
15998         
15999         if (this.name) {
16000             input.name = this.name;
16001         }
16002         
16003         if (this.size) {
16004             input.cls += ' input-' + this.size;
16005         }
16006         
16007         var settings=this;
16008         ['xs','sm','md','lg'].map(function(size){
16009             if (settings[size]) {
16010                 cfg.cls += ' col-' + size + '-' + settings[size];
16011             }
16012         });
16013         
16014        
16015         
16016         var inputblock = input;
16017         
16018         
16019         
16020         
16021         if (this.before || this.after) {
16022             
16023             inputblock = {
16024                 cls : 'input-group',
16025                 cn :  [] 
16026             };
16027             if (this.before) {
16028                 inputblock.cn.push({
16029                     tag :'span',
16030                     cls : 'input-group-addon',
16031                     html : this.before
16032                 });
16033             }
16034             inputblock.cn.push(input);
16035             if (this.after) {
16036                 inputblock.cn.push({
16037                     tag :'span',
16038                     cls : 'input-group-addon',
16039                     html : this.after
16040                 });
16041             }
16042             
16043         };
16044         
16045         if (align ==='left' && this.fieldLabel.length) {
16046                 Roo.log("left and has label");
16047                 cfg.cn = [
16048                     
16049                     {
16050                         tag: 'label',
16051                         'for' :  id,
16052                         cls : 'control-label col-md-' + this.labelWidth,
16053                         html : this.fieldLabel
16054                         
16055                     },
16056                     {
16057                         cls : "col-md-" + (12 - this.labelWidth), 
16058                         cn: [
16059                             inputblock
16060                         ]
16061                     }
16062                     
16063                 ];
16064         } else if ( this.fieldLabel.length) {
16065                 Roo.log(" label");
16066                 cfg.cn = [
16067                    
16068                     {
16069                         tag: this.boxLabel ? 'span' : 'label',
16070                         'for': id,
16071                         cls: 'control-label box-input-label',
16072                         //cls : 'input-group-addon',
16073                         html : this.fieldLabel
16074                         
16075                     },
16076                     
16077                     inputblock
16078                     
16079                 ];
16080
16081         } else {
16082             
16083                 Roo.log(" no label && no align");
16084                 cfg.cn = [  inputblock ] ;
16085                 
16086                 
16087         };
16088          if(this.boxLabel){
16089             cfg.cn.push( {
16090                 tag: 'label',
16091                 'for': id,
16092                 cls: 'box-label',
16093                 html: this.boxLabel
16094                 
16095             });
16096         }
16097         
16098         
16099        
16100         return cfg;
16101         
16102     },
16103     
16104     /**
16105      * return the real input element.
16106      */
16107     inputEl: function ()
16108     {
16109         return this.el.select('input.roo-checkbox',true).first();
16110     },
16111     
16112     label: function()
16113     {
16114         return this.el.select('label.control-label',true).first();
16115     },
16116     
16117     initEvents : function()
16118     {
16119 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16120         
16121         this.inputEl().on('click', this.onClick,  this);
16122         
16123     },
16124     
16125     onClick : function()
16126     {   
16127         this.setChecked(!this.checked);
16128     },
16129     
16130     setChecked : function(state,suppressEvent)
16131     {
16132         this.checked = state;
16133         
16134         this.inputEl().dom.checked = state;
16135         
16136         if(suppressEvent !== true){
16137             this.fireEvent('check', this, state);
16138         }
16139         
16140         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16141         
16142     },
16143     
16144     setValue : function(v,suppressEvent)
16145     {
16146         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16147     }
16148     
16149 });
16150
16151  
16152 /*
16153  * - LGPL
16154  *
16155  * Radio
16156  * 
16157  */
16158
16159 /**
16160  * @class Roo.bootstrap.Radio
16161  * @extends Roo.bootstrap.CheckBox
16162  * Bootstrap Radio class
16163
16164  * @constructor
16165  * Create a new Radio
16166  * @param {Object} config The config object
16167  */
16168
16169 Roo.bootstrap.Radio = function(config){
16170     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16171    
16172 };
16173
16174 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16175     
16176     inputType: 'radio',
16177     inputValue: '',
16178     valueOff: '',
16179     
16180     getAutoCreate : function()
16181     {
16182         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16183         
16184         var id = Roo.id();
16185         
16186         var cfg = {};
16187         
16188         cfg.cls = 'form-group radio' //input-group
16189         
16190         var input =  {
16191             tag: 'input',
16192             id : id,
16193             type : this.inputType,
16194             value : (!this.checked) ? this.valueOff : this.inputValue,
16195             cls : 'roo-radio',
16196             placeholder : this.placeholder || ''
16197             
16198         };
16199           if (this.weight) { // Validity check?
16200             cfg.cls += " radio-" + this.weight;
16201         }
16202         if (this.disabled) {
16203             input.disabled=true;
16204         }
16205         
16206         if(this.checked){
16207             input.checked = this.checked;
16208         }
16209         
16210         if (this.name) {
16211             input.name = this.name;
16212         }
16213         
16214         if (this.size) {
16215             input.cls += ' input-' + this.size;
16216         }
16217         
16218         var settings=this;
16219         ['xs','sm','md','lg'].map(function(size){
16220             if (settings[size]) {
16221                 cfg.cls += ' col-' + size + '-' + settings[size];
16222             }
16223         });
16224         
16225         var inputblock = input;
16226         
16227         if (this.before || this.after) {
16228             
16229             inputblock = {
16230                 cls : 'input-group',
16231                 cn :  [] 
16232             };
16233             if (this.before) {
16234                 inputblock.cn.push({
16235                     tag :'span',
16236                     cls : 'input-group-addon',
16237                     html : this.before
16238                 });
16239             }
16240             inputblock.cn.push(input);
16241             if (this.after) {
16242                 inputblock.cn.push({
16243                     tag :'span',
16244                     cls : 'input-group-addon',
16245                     html : this.after
16246                 });
16247             }
16248             
16249         };
16250         
16251         if (align ==='left' && this.fieldLabel.length) {
16252                 Roo.log("left and has label");
16253                 cfg.cn = [
16254                     
16255                     {
16256                         tag: 'label',
16257                         'for' :  id,
16258                         cls : 'control-label col-md-' + this.labelWidth,
16259                         html : this.fieldLabel
16260                         
16261                     },
16262                     {
16263                         cls : "col-md-" + (12 - this.labelWidth), 
16264                         cn: [
16265                             inputblock
16266                         ]
16267                     }
16268                     
16269                 ];
16270         } else if ( this.fieldLabel.length) {
16271                 Roo.log(" label");
16272                  cfg.cn = [
16273                    
16274                     {
16275                         tag: 'label',
16276                         'for': id,
16277                         cls: 'control-label box-input-label',
16278                         //cls : 'input-group-addon',
16279                         html : this.fieldLabel
16280                         
16281                     },
16282                     
16283                     inputblock
16284                     
16285                 ];
16286
16287         } else {
16288             
16289                    Roo.log(" no label && no align");
16290                 cfg.cn = [
16291                     
16292                         inputblock
16293                     
16294                 ];
16295                 
16296                 
16297         };
16298         
16299         if(this.boxLabel){
16300             cfg.cn.push({
16301                 tag: 'label',
16302                 'for': id,
16303                 cls: 'box-label',
16304                 html: this.boxLabel
16305             })
16306         }
16307         
16308         return cfg;
16309         
16310     },
16311     inputEl: function ()
16312     {
16313         return this.el.select('input.roo-radio',true).first();
16314     },
16315     onClick : function()
16316     {   
16317         this.setChecked(true);
16318     },
16319     
16320     setChecked : function(state,suppressEvent)
16321     {
16322         if(state){
16323             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16324                 v.dom.checked = false;
16325             });
16326         }
16327         
16328         this.checked = state;
16329         this.inputEl().dom.checked = state;
16330         
16331         if(suppressEvent !== true){
16332             this.fireEvent('check', this, state);
16333         }
16334         
16335         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16336         
16337     },
16338     
16339     getGroupValue : function()
16340     {
16341         var value = ''
16342         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16343             if(v.dom.checked == true){
16344                 value = v.dom.value;
16345             }
16346         });
16347         
16348         return value;
16349     },
16350     
16351     /**
16352      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16353      * @return {Mixed} value The field value
16354      */
16355     getValue : function(){
16356         return this.getGroupValue();
16357     }
16358     
16359 });
16360
16361  
16362 //<script type="text/javascript">
16363
16364 /*
16365  * Based  Ext JS Library 1.1.1
16366  * Copyright(c) 2006-2007, Ext JS, LLC.
16367  * LGPL
16368  *
16369  */
16370  
16371 /**
16372  * @class Roo.HtmlEditorCore
16373  * @extends Roo.Component
16374  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16375  *
16376  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16377  */
16378
16379 Roo.HtmlEditorCore = function(config){
16380     
16381     
16382     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16383     
16384     
16385     this.addEvents({
16386         /**
16387          * @event initialize
16388          * Fires when the editor is fully initialized (including the iframe)
16389          * @param {Roo.HtmlEditorCore} this
16390          */
16391         initialize: true,
16392         /**
16393          * @event activate
16394          * Fires when the editor is first receives the focus. Any insertion must wait
16395          * until after this event.
16396          * @param {Roo.HtmlEditorCore} this
16397          */
16398         activate: true,
16399          /**
16400          * @event beforesync
16401          * Fires before the textarea is updated with content from the editor iframe. Return false
16402          * to cancel the sync.
16403          * @param {Roo.HtmlEditorCore} this
16404          * @param {String} html
16405          */
16406         beforesync: true,
16407          /**
16408          * @event beforepush
16409          * Fires before the iframe editor is updated with content from the textarea. Return false
16410          * to cancel the push.
16411          * @param {Roo.HtmlEditorCore} this
16412          * @param {String} html
16413          */
16414         beforepush: true,
16415          /**
16416          * @event sync
16417          * Fires when the textarea is updated with content from the editor iframe.
16418          * @param {Roo.HtmlEditorCore} this
16419          * @param {String} html
16420          */
16421         sync: true,
16422          /**
16423          * @event push
16424          * Fires when the iframe editor is updated with content from the textarea.
16425          * @param {Roo.HtmlEditorCore} this
16426          * @param {String} html
16427          */
16428         push: true,
16429         
16430         /**
16431          * @event editorevent
16432          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16433          * @param {Roo.HtmlEditorCore} this
16434          */
16435         editorevent: true
16436     });
16437     
16438     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16439     
16440     // defaults : white / black...
16441     this.applyBlacklists();
16442     
16443     
16444     
16445 };
16446
16447
16448 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16449
16450
16451      /**
16452      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16453      */
16454     
16455     owner : false,
16456     
16457      /**
16458      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16459      *                        Roo.resizable.
16460      */
16461     resizable : false,
16462      /**
16463      * @cfg {Number} height (in pixels)
16464      */   
16465     height: 300,
16466    /**
16467      * @cfg {Number} width (in pixels)
16468      */   
16469     width: 500,
16470     
16471     /**
16472      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16473      * 
16474      */
16475     stylesheets: false,
16476     
16477     // id of frame..
16478     frameId: false,
16479     
16480     // private properties
16481     validationEvent : false,
16482     deferHeight: true,
16483     initialized : false,
16484     activated : false,
16485     sourceEditMode : false,
16486     onFocus : Roo.emptyFn,
16487     iframePad:3,
16488     hideMode:'offsets',
16489     
16490     clearUp: true,
16491     
16492     // blacklist + whitelisted elements..
16493     black: false,
16494     white: false,
16495      
16496     
16497
16498     /**
16499      * Protected method that will not generally be called directly. It
16500      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16501      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16502      */
16503     getDocMarkup : function(){
16504         // body styles..
16505         var st = '';
16506         Roo.log(this.stylesheets);
16507         
16508         // inherit styels from page...?? 
16509         if (this.stylesheets === false) {
16510             
16511             Roo.get(document.head).select('style').each(function(node) {
16512                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16513             });
16514             
16515             Roo.get(document.head).select('link').each(function(node) { 
16516                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16517             });
16518             
16519         } else if (!this.stylesheets.length) {
16520                 // simple..
16521                 st = '<style type="text/css">' +
16522                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16523                    '</style>';
16524         } else {
16525             Roo.each(this.stylesheets, function(s) {
16526                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16527             });
16528             
16529         }
16530         
16531         st +=  '<style type="text/css">' +
16532             'IMG { cursor: pointer } ' +
16533         '</style>';
16534
16535         
16536         return '<html><head>' + st  +
16537             //<style type="text/css">' +
16538             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16539             //'</style>' +
16540             ' </head><body class="roo-htmleditor-body"></body></html>';
16541     },
16542
16543     // private
16544     onRender : function(ct, position)
16545     {
16546         var _t = this;
16547         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16548         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16549         
16550         
16551         this.el.dom.style.border = '0 none';
16552         this.el.dom.setAttribute('tabIndex', -1);
16553         this.el.addClass('x-hidden hide');
16554         
16555         
16556         
16557         if(Roo.isIE){ // fix IE 1px bogus margin
16558             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16559         }
16560        
16561         
16562         this.frameId = Roo.id();
16563         
16564          
16565         
16566         var iframe = this.owner.wrap.createChild({
16567             tag: 'iframe',
16568             cls: 'form-control', // bootstrap..
16569             id: this.frameId,
16570             name: this.frameId,
16571             frameBorder : 'no',
16572             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16573         }, this.el
16574         );
16575         
16576         
16577         this.iframe = iframe.dom;
16578
16579          this.assignDocWin();
16580         
16581         this.doc.designMode = 'on';
16582        
16583         this.doc.open();
16584         this.doc.write(this.getDocMarkup());
16585         this.doc.close();
16586
16587         
16588         var task = { // must defer to wait for browser to be ready
16589             run : function(){
16590                 //console.log("run task?" + this.doc.readyState);
16591                 this.assignDocWin();
16592                 if(this.doc.body || this.doc.readyState == 'complete'){
16593                     try {
16594                         this.doc.designMode="on";
16595                     } catch (e) {
16596                         return;
16597                     }
16598                     Roo.TaskMgr.stop(task);
16599                     this.initEditor.defer(10, this);
16600                 }
16601             },
16602             interval : 10,
16603             duration: 10000,
16604             scope: this
16605         };
16606         Roo.TaskMgr.start(task);
16607
16608         
16609          
16610     },
16611
16612     // private
16613     onResize : function(w, h)
16614     {
16615          Roo.log('resize: ' +w + ',' + h );
16616         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16617         if(!this.iframe){
16618             return;
16619         }
16620         if(typeof w == 'number'){
16621             
16622             this.iframe.style.width = w + 'px';
16623         }
16624         if(typeof h == 'number'){
16625             
16626             this.iframe.style.height = h + 'px';
16627             if(this.doc){
16628                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16629             }
16630         }
16631         
16632     },
16633
16634     /**
16635      * Toggles the editor between standard and source edit mode.
16636      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16637      */
16638     toggleSourceEdit : function(sourceEditMode){
16639         
16640         this.sourceEditMode = sourceEditMode === true;
16641         
16642         if(this.sourceEditMode){
16643  
16644             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16645             
16646         }else{
16647             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16648             //this.iframe.className = '';
16649             this.deferFocus();
16650         }
16651         //this.setSize(this.owner.wrap.getSize());
16652         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16653     },
16654
16655     
16656   
16657
16658     /**
16659      * Protected method that will not generally be called directly. If you need/want
16660      * custom HTML cleanup, this is the method you should override.
16661      * @param {String} html The HTML to be cleaned
16662      * return {String} The cleaned HTML
16663      */
16664     cleanHtml : function(html){
16665         html = String(html);
16666         if(html.length > 5){
16667             if(Roo.isSafari){ // strip safari nonsense
16668                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16669             }
16670         }
16671         if(html == '&nbsp;'){
16672             html = '';
16673         }
16674         return html;
16675     },
16676
16677     /**
16678      * HTML Editor -> Textarea
16679      * Protected method that will not generally be called directly. Syncs the contents
16680      * of the editor iframe with the textarea.
16681      */
16682     syncValue : function(){
16683         if(this.initialized){
16684             var bd = (this.doc.body || this.doc.documentElement);
16685             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16686             var html = bd.innerHTML;
16687             if(Roo.isSafari){
16688                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16689                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16690                 if(m && m[1]){
16691                     html = '<div style="'+m[0]+'">' + html + '</div>';
16692                 }
16693             }
16694             html = this.cleanHtml(html);
16695             // fix up the special chars.. normaly like back quotes in word...
16696             // however we do not want to do this with chinese..
16697             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16698                 var cc = b.charCodeAt();
16699                 if (
16700                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16701                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16702                     (cc >= 0xf900 && cc < 0xfb00 )
16703                 ) {
16704                         return b;
16705                 }
16706                 return "&#"+cc+";" 
16707             });
16708             if(this.owner.fireEvent('beforesync', this, html) !== false){
16709                 this.el.dom.value = html;
16710                 this.owner.fireEvent('sync', this, html);
16711             }
16712         }
16713     },
16714
16715     /**
16716      * Protected method that will not generally be called directly. Pushes the value of the textarea
16717      * into the iframe editor.
16718      */
16719     pushValue : function(){
16720         if(this.initialized){
16721             var v = this.el.dom.value.trim();
16722             
16723 //            if(v.length < 1){
16724 //                v = '&#160;';
16725 //            }
16726             
16727             if(this.owner.fireEvent('beforepush', this, v) !== false){
16728                 var d = (this.doc.body || this.doc.documentElement);
16729                 d.innerHTML = v;
16730                 this.cleanUpPaste();
16731                 this.el.dom.value = d.innerHTML;
16732                 this.owner.fireEvent('push', this, v);
16733             }
16734         }
16735     },
16736
16737     // private
16738     deferFocus : function(){
16739         this.focus.defer(10, this);
16740     },
16741
16742     // doc'ed in Field
16743     focus : function(){
16744         if(this.win && !this.sourceEditMode){
16745             this.win.focus();
16746         }else{
16747             this.el.focus();
16748         }
16749     },
16750     
16751     assignDocWin: function()
16752     {
16753         var iframe = this.iframe;
16754         
16755          if(Roo.isIE){
16756             this.doc = iframe.contentWindow.document;
16757             this.win = iframe.contentWindow;
16758         } else {
16759 //            if (!Roo.get(this.frameId)) {
16760 //                return;
16761 //            }
16762 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16763 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16764             
16765             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16766                 return;
16767             }
16768             
16769             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16770             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16771         }
16772     },
16773     
16774     // private
16775     initEditor : function(){
16776         //console.log("INIT EDITOR");
16777         this.assignDocWin();
16778         
16779         
16780         
16781         this.doc.designMode="on";
16782         this.doc.open();
16783         this.doc.write(this.getDocMarkup());
16784         this.doc.close();
16785         
16786         var dbody = (this.doc.body || this.doc.documentElement);
16787         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16788         // this copies styles from the containing element into thsi one..
16789         // not sure why we need all of this..
16790         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16791         
16792         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16793         //ss['background-attachment'] = 'fixed'; // w3c
16794         dbody.bgProperties = 'fixed'; // ie
16795         //Roo.DomHelper.applyStyles(dbody, ss);
16796         Roo.EventManager.on(this.doc, {
16797             //'mousedown': this.onEditorEvent,
16798             'mouseup': this.onEditorEvent,
16799             'dblclick': this.onEditorEvent,
16800             'click': this.onEditorEvent,
16801             'keyup': this.onEditorEvent,
16802             buffer:100,
16803             scope: this
16804         });
16805         if(Roo.isGecko){
16806             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16807         }
16808         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16809             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16810         }
16811         this.initialized = true;
16812
16813         this.owner.fireEvent('initialize', this);
16814         this.pushValue();
16815     },
16816
16817     // private
16818     onDestroy : function(){
16819         
16820         
16821         
16822         if(this.rendered){
16823             
16824             //for (var i =0; i < this.toolbars.length;i++) {
16825             //    // fixme - ask toolbars for heights?
16826             //    this.toolbars[i].onDestroy();
16827            // }
16828             
16829             //this.wrap.dom.innerHTML = '';
16830             //this.wrap.remove();
16831         }
16832     },
16833
16834     // private
16835     onFirstFocus : function(){
16836         
16837         this.assignDocWin();
16838         
16839         
16840         this.activated = true;
16841          
16842     
16843         if(Roo.isGecko){ // prevent silly gecko errors
16844             this.win.focus();
16845             var s = this.win.getSelection();
16846             if(!s.focusNode || s.focusNode.nodeType != 3){
16847                 var r = s.getRangeAt(0);
16848                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16849                 r.collapse(true);
16850                 this.deferFocus();
16851             }
16852             try{
16853                 this.execCmd('useCSS', true);
16854                 this.execCmd('styleWithCSS', false);
16855             }catch(e){}
16856         }
16857         this.owner.fireEvent('activate', this);
16858     },
16859
16860     // private
16861     adjustFont: function(btn){
16862         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16863         //if(Roo.isSafari){ // safari
16864         //    adjust *= 2;
16865        // }
16866         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16867         if(Roo.isSafari){ // safari
16868             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16869             v =  (v < 10) ? 10 : v;
16870             v =  (v > 48) ? 48 : v;
16871             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16872             
16873         }
16874         
16875         
16876         v = Math.max(1, v+adjust);
16877         
16878         this.execCmd('FontSize', v  );
16879     },
16880
16881     onEditorEvent : function(e){
16882         this.owner.fireEvent('editorevent', this, e);
16883       //  this.updateToolbar();
16884         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16885     },
16886
16887     insertTag : function(tg)
16888     {
16889         // could be a bit smarter... -> wrap the current selected tRoo..
16890         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16891             
16892             range = this.createRange(this.getSelection());
16893             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16894             wrappingNode.appendChild(range.extractContents());
16895             range.insertNode(wrappingNode);
16896
16897             return;
16898             
16899             
16900             
16901         }
16902         this.execCmd("formatblock",   tg);
16903         
16904     },
16905     
16906     insertText : function(txt)
16907     {
16908         
16909         
16910         var range = this.createRange();
16911         range.deleteContents();
16912                //alert(Sender.getAttribute('label'));
16913                
16914         range.insertNode(this.doc.createTextNode(txt));
16915     } ,
16916     
16917      
16918
16919     /**
16920      * Executes a Midas editor command on the editor document and performs necessary focus and
16921      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16922      * @param {String} cmd The Midas command
16923      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16924      */
16925     relayCmd : function(cmd, value){
16926         this.win.focus();
16927         this.execCmd(cmd, value);
16928         this.owner.fireEvent('editorevent', this);
16929         //this.updateToolbar();
16930         this.owner.deferFocus();
16931     },
16932
16933     /**
16934      * Executes a Midas editor command directly on the editor document.
16935      * For visual commands, you should use {@link #relayCmd} instead.
16936      * <b>This should only be called after the editor is initialized.</b>
16937      * @param {String} cmd The Midas command
16938      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16939      */
16940     execCmd : function(cmd, value){
16941         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16942         this.syncValue();
16943     },
16944  
16945  
16946    
16947     /**
16948      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16949      * to insert tRoo.
16950      * @param {String} text | dom node.. 
16951      */
16952     insertAtCursor : function(text)
16953     {
16954         
16955         
16956         
16957         if(!this.activated){
16958             return;
16959         }
16960         /*
16961         if(Roo.isIE){
16962             this.win.focus();
16963             var r = this.doc.selection.createRange();
16964             if(r){
16965                 r.collapse(true);
16966                 r.pasteHTML(text);
16967                 this.syncValue();
16968                 this.deferFocus();
16969             
16970             }
16971             return;
16972         }
16973         */
16974         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16975             this.win.focus();
16976             
16977             
16978             // from jquery ui (MIT licenced)
16979             var range, node;
16980             var win = this.win;
16981             
16982             if (win.getSelection && win.getSelection().getRangeAt) {
16983                 range = win.getSelection().getRangeAt(0);
16984                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16985                 range.insertNode(node);
16986             } else if (win.document.selection && win.document.selection.createRange) {
16987                 // no firefox support
16988                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16989                 win.document.selection.createRange().pasteHTML(txt);
16990             } else {
16991                 // no firefox support
16992                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16993                 this.execCmd('InsertHTML', txt);
16994             } 
16995             
16996             this.syncValue();
16997             
16998             this.deferFocus();
16999         }
17000     },
17001  // private
17002     mozKeyPress : function(e){
17003         if(e.ctrlKey){
17004             var c = e.getCharCode(), cmd;
17005           
17006             if(c > 0){
17007                 c = String.fromCharCode(c).toLowerCase();
17008                 switch(c){
17009                     case 'b':
17010                         cmd = 'bold';
17011                         break;
17012                     case 'i':
17013                         cmd = 'italic';
17014                         break;
17015                     
17016                     case 'u':
17017                         cmd = 'underline';
17018                         break;
17019                     
17020                     case 'v':
17021                         this.cleanUpPaste.defer(100, this);
17022                         return;
17023                         
17024                 }
17025                 if(cmd){
17026                     this.win.focus();
17027                     this.execCmd(cmd);
17028                     this.deferFocus();
17029                     e.preventDefault();
17030                 }
17031                 
17032             }
17033         }
17034     },
17035
17036     // private
17037     fixKeys : function(){ // load time branching for fastest keydown performance
17038         if(Roo.isIE){
17039             return function(e){
17040                 var k = e.getKey(), r;
17041                 if(k == e.TAB){
17042                     e.stopEvent();
17043                     r = this.doc.selection.createRange();
17044                     if(r){
17045                         r.collapse(true);
17046                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17047                         this.deferFocus();
17048                     }
17049                     return;
17050                 }
17051                 
17052                 if(k == e.ENTER){
17053                     r = this.doc.selection.createRange();
17054                     if(r){
17055                         var target = r.parentElement();
17056                         if(!target || target.tagName.toLowerCase() != 'li'){
17057                             e.stopEvent();
17058                             r.pasteHTML('<br />');
17059                             r.collapse(false);
17060                             r.select();
17061                         }
17062                     }
17063                 }
17064                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17065                     this.cleanUpPaste.defer(100, this);
17066                     return;
17067                 }
17068                 
17069                 
17070             };
17071         }else if(Roo.isOpera){
17072             return function(e){
17073                 var k = e.getKey();
17074                 if(k == e.TAB){
17075                     e.stopEvent();
17076                     this.win.focus();
17077                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17078                     this.deferFocus();
17079                 }
17080                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17081                     this.cleanUpPaste.defer(100, this);
17082                     return;
17083                 }
17084                 
17085             };
17086         }else if(Roo.isSafari){
17087             return function(e){
17088                 var k = e.getKey();
17089                 
17090                 if(k == e.TAB){
17091                     e.stopEvent();
17092                     this.execCmd('InsertText','\t');
17093                     this.deferFocus();
17094                     return;
17095                 }
17096                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17097                     this.cleanUpPaste.defer(100, this);
17098                     return;
17099                 }
17100                 
17101              };
17102         }
17103     }(),
17104     
17105     getAllAncestors: function()
17106     {
17107         var p = this.getSelectedNode();
17108         var a = [];
17109         if (!p) {
17110             a.push(p); // push blank onto stack..
17111             p = this.getParentElement();
17112         }
17113         
17114         
17115         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17116             a.push(p);
17117             p = p.parentNode;
17118         }
17119         a.push(this.doc.body);
17120         return a;
17121     },
17122     lastSel : false,
17123     lastSelNode : false,
17124     
17125     
17126     getSelection : function() 
17127     {
17128         this.assignDocWin();
17129         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17130     },
17131     
17132     getSelectedNode: function() 
17133     {
17134         // this may only work on Gecko!!!
17135         
17136         // should we cache this!!!!
17137         
17138         
17139         
17140          
17141         var range = this.createRange(this.getSelection()).cloneRange();
17142         
17143         if (Roo.isIE) {
17144             var parent = range.parentElement();
17145             while (true) {
17146                 var testRange = range.duplicate();
17147                 testRange.moveToElementText(parent);
17148                 if (testRange.inRange(range)) {
17149                     break;
17150                 }
17151                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17152                     break;
17153                 }
17154                 parent = parent.parentElement;
17155             }
17156             return parent;
17157         }
17158         
17159         // is ancestor a text element.
17160         var ac =  range.commonAncestorContainer;
17161         if (ac.nodeType == 3) {
17162             ac = ac.parentNode;
17163         }
17164         
17165         var ar = ac.childNodes;
17166          
17167         var nodes = [];
17168         var other_nodes = [];
17169         var has_other_nodes = false;
17170         for (var i=0;i<ar.length;i++) {
17171             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17172                 continue;
17173             }
17174             // fullly contained node.
17175             
17176             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17177                 nodes.push(ar[i]);
17178                 continue;
17179             }
17180             
17181             // probably selected..
17182             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17183                 other_nodes.push(ar[i]);
17184                 continue;
17185             }
17186             // outer..
17187             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17188                 continue;
17189             }
17190             
17191             
17192             has_other_nodes = true;
17193         }
17194         if (!nodes.length && other_nodes.length) {
17195             nodes= other_nodes;
17196         }
17197         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17198             return false;
17199         }
17200         
17201         return nodes[0];
17202     },
17203     createRange: function(sel)
17204     {
17205         // this has strange effects when using with 
17206         // top toolbar - not sure if it's a great idea.
17207         //this.editor.contentWindow.focus();
17208         if (typeof sel != "undefined") {
17209             try {
17210                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17211             } catch(e) {
17212                 return this.doc.createRange();
17213             }
17214         } else {
17215             return this.doc.createRange();
17216         }
17217     },
17218     getParentElement: function()
17219     {
17220         
17221         this.assignDocWin();
17222         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17223         
17224         var range = this.createRange(sel);
17225          
17226         try {
17227             var p = range.commonAncestorContainer;
17228             while (p.nodeType == 3) { // text node
17229                 p = p.parentNode;
17230             }
17231             return p;
17232         } catch (e) {
17233             return null;
17234         }
17235     
17236     },
17237     /***
17238      *
17239      * Range intersection.. the hard stuff...
17240      *  '-1' = before
17241      *  '0' = hits..
17242      *  '1' = after.
17243      *         [ -- selected range --- ]
17244      *   [fail]                        [fail]
17245      *
17246      *    basically..
17247      *      if end is before start or  hits it. fail.
17248      *      if start is after end or hits it fail.
17249      *
17250      *   if either hits (but other is outside. - then it's not 
17251      *   
17252      *    
17253      **/
17254     
17255     
17256     // @see http://www.thismuchiknow.co.uk/?p=64.
17257     rangeIntersectsNode : function(range, node)
17258     {
17259         var nodeRange = node.ownerDocument.createRange();
17260         try {
17261             nodeRange.selectNode(node);
17262         } catch (e) {
17263             nodeRange.selectNodeContents(node);
17264         }
17265     
17266         var rangeStartRange = range.cloneRange();
17267         rangeStartRange.collapse(true);
17268     
17269         var rangeEndRange = range.cloneRange();
17270         rangeEndRange.collapse(false);
17271     
17272         var nodeStartRange = nodeRange.cloneRange();
17273         nodeStartRange.collapse(true);
17274     
17275         var nodeEndRange = nodeRange.cloneRange();
17276         nodeEndRange.collapse(false);
17277     
17278         return rangeStartRange.compareBoundaryPoints(
17279                  Range.START_TO_START, nodeEndRange) == -1 &&
17280                rangeEndRange.compareBoundaryPoints(
17281                  Range.START_TO_START, nodeStartRange) == 1;
17282         
17283          
17284     },
17285     rangeCompareNode : function(range, node)
17286     {
17287         var nodeRange = node.ownerDocument.createRange();
17288         try {
17289             nodeRange.selectNode(node);
17290         } catch (e) {
17291             nodeRange.selectNodeContents(node);
17292         }
17293         
17294         
17295         range.collapse(true);
17296     
17297         nodeRange.collapse(true);
17298      
17299         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17300         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17301          
17302         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17303         
17304         var nodeIsBefore   =  ss == 1;
17305         var nodeIsAfter    = ee == -1;
17306         
17307         if (nodeIsBefore && nodeIsAfter)
17308             return 0; // outer
17309         if (!nodeIsBefore && nodeIsAfter)
17310             return 1; //right trailed.
17311         
17312         if (nodeIsBefore && !nodeIsAfter)
17313             return 2;  // left trailed.
17314         // fully contined.
17315         return 3;
17316     },
17317
17318     // private? - in a new class?
17319     cleanUpPaste :  function()
17320     {
17321         // cleans up the whole document..
17322         Roo.log('cleanuppaste');
17323         
17324         this.cleanUpChildren(this.doc.body);
17325         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17326         if (clean != this.doc.body.innerHTML) {
17327             this.doc.body.innerHTML = clean;
17328         }
17329         
17330     },
17331     
17332     cleanWordChars : function(input) {// change the chars to hex code
17333         var he = Roo.HtmlEditorCore;
17334         
17335         var output = input;
17336         Roo.each(he.swapCodes, function(sw) { 
17337             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17338             
17339             output = output.replace(swapper, sw[1]);
17340         });
17341         
17342         return output;
17343     },
17344     
17345     
17346     cleanUpChildren : function (n)
17347     {
17348         if (!n.childNodes.length) {
17349             return;
17350         }
17351         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17352            this.cleanUpChild(n.childNodes[i]);
17353         }
17354     },
17355     
17356     
17357         
17358     
17359     cleanUpChild : function (node)
17360     {
17361         var ed = this;
17362         //console.log(node);
17363         if (node.nodeName == "#text") {
17364             // clean up silly Windows -- stuff?
17365             return; 
17366         }
17367         if (node.nodeName == "#comment") {
17368             node.parentNode.removeChild(node);
17369             // clean up silly Windows -- stuff?
17370             return; 
17371         }
17372         var lcname = node.tagName.toLowerCase();
17373         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17374         // whitelist of tags..
17375         
17376         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17377             // remove node.
17378             node.parentNode.removeChild(node);
17379             return;
17380             
17381         }
17382         
17383         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17384         
17385         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17386         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17387         
17388         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17389         //    remove_keep_children = true;
17390         //}
17391         
17392         if (remove_keep_children) {
17393             this.cleanUpChildren(node);
17394             // inserts everything just before this node...
17395             while (node.childNodes.length) {
17396                 var cn = node.childNodes[0];
17397                 node.removeChild(cn);
17398                 node.parentNode.insertBefore(cn, node);
17399             }
17400             node.parentNode.removeChild(node);
17401             return;
17402         }
17403         
17404         if (!node.attributes || !node.attributes.length) {
17405             this.cleanUpChildren(node);
17406             return;
17407         }
17408         
17409         function cleanAttr(n,v)
17410         {
17411             
17412             if (v.match(/^\./) || v.match(/^\//)) {
17413                 return;
17414             }
17415             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17416                 return;
17417             }
17418             if (v.match(/^#/)) {
17419                 return;
17420             }
17421 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17422             node.removeAttribute(n);
17423             
17424         }
17425         
17426         var cwhite = this.cwhite;
17427         var cblack = this.cblack;
17428             
17429         function cleanStyle(n,v)
17430         {
17431             if (v.match(/expression/)) { //XSS?? should we even bother..
17432                 node.removeAttribute(n);
17433                 return;
17434             }
17435             
17436             var parts = v.split(/;/);
17437             var clean = [];
17438             
17439             Roo.each(parts, function(p) {
17440                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17441                 if (!p.length) {
17442                     return true;
17443                 }
17444                 var l = p.split(':').shift().replace(/\s+/g,'');
17445                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17446                 
17447                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17448 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17449                     //node.removeAttribute(n);
17450                     return true;
17451                 }
17452                 //Roo.log()
17453                 // only allow 'c whitelisted system attributes'
17454                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17455 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17456                     //node.removeAttribute(n);
17457                     return true;
17458                 }
17459                 
17460                 
17461                  
17462                 
17463                 clean.push(p);
17464                 return true;
17465             });
17466             if (clean.length) { 
17467                 node.setAttribute(n, clean.join(';'));
17468             } else {
17469                 node.removeAttribute(n);
17470             }
17471             
17472         }
17473         
17474         
17475         for (var i = node.attributes.length-1; i > -1 ; i--) {
17476             var a = node.attributes[i];
17477             //console.log(a);
17478             
17479             if (a.name.toLowerCase().substr(0,2)=='on')  {
17480                 node.removeAttribute(a.name);
17481                 continue;
17482             }
17483             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17484                 node.removeAttribute(a.name);
17485                 continue;
17486             }
17487             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17488                 cleanAttr(a.name,a.value); // fixme..
17489                 continue;
17490             }
17491             if (a.name == 'style') {
17492                 cleanStyle(a.name,a.value);
17493                 continue;
17494             }
17495             /// clean up MS crap..
17496             // tecnically this should be a list of valid class'es..
17497             
17498             
17499             if (a.name == 'class') {
17500                 if (a.value.match(/^Mso/)) {
17501                     node.className = '';
17502                 }
17503                 
17504                 if (a.value.match(/body/)) {
17505                     node.className = '';
17506                 }
17507                 continue;
17508             }
17509             
17510             // style cleanup!?
17511             // class cleanup?
17512             
17513         }
17514         
17515         
17516         this.cleanUpChildren(node);
17517         
17518         
17519     },
17520     /**
17521      * Clean up MS wordisms...
17522      */
17523     cleanWord : function(node)
17524     {
17525         var _t = this;
17526         var cleanWordChildren = function()
17527         {
17528             if (!node.childNodes.length) {
17529                 return;
17530             }
17531             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17532                _t.cleanWord(node.childNodes[i]);
17533             }
17534         }
17535         
17536         
17537         if (!node) {
17538             this.cleanWord(this.doc.body);
17539             return;
17540         }
17541         if (node.nodeName == "#text") {
17542             // clean up silly Windows -- stuff?
17543             return; 
17544         }
17545         if (node.nodeName == "#comment") {
17546             node.parentNode.removeChild(node);
17547             // clean up silly Windows -- stuff?
17548             return; 
17549         }
17550         
17551         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17552             node.parentNode.removeChild(node);
17553             return;
17554         }
17555         
17556         // remove - but keep children..
17557         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17558             while (node.childNodes.length) {
17559                 var cn = node.childNodes[0];
17560                 node.removeChild(cn);
17561                 node.parentNode.insertBefore(cn, node);
17562             }
17563             node.parentNode.removeChild(node);
17564             cleanWordChildren();
17565             return;
17566         }
17567         // clean styles
17568         if (node.className.length) {
17569             
17570             var cn = node.className.split(/\W+/);
17571             var cna = [];
17572             Roo.each(cn, function(cls) {
17573                 if (cls.match(/Mso[a-zA-Z]+/)) {
17574                     return;
17575                 }
17576                 cna.push(cls);
17577             });
17578             node.className = cna.length ? cna.join(' ') : '';
17579             if (!cna.length) {
17580                 node.removeAttribute("class");
17581             }
17582         }
17583         
17584         if (node.hasAttribute("lang")) {
17585             node.removeAttribute("lang");
17586         }
17587         
17588         if (node.hasAttribute("style")) {
17589             
17590             var styles = node.getAttribute("style").split(";");
17591             var nstyle = [];
17592             Roo.each(styles, function(s) {
17593                 if (!s.match(/:/)) {
17594                     return;
17595                 }
17596                 var kv = s.split(":");
17597                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17598                     return;
17599                 }
17600                 // what ever is left... we allow.
17601                 nstyle.push(s);
17602             });
17603             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17604             if (!nstyle.length) {
17605                 node.removeAttribute('style');
17606             }
17607         }
17608         
17609         cleanWordChildren();
17610         
17611         
17612     },
17613     domToHTML : function(currentElement, depth, nopadtext) {
17614         
17615         depth = depth || 0;
17616         nopadtext = nopadtext || false;
17617     
17618         if (!currentElement) {
17619             return this.domToHTML(this.doc.body);
17620         }
17621         
17622         //Roo.log(currentElement);
17623         var j;
17624         var allText = false;
17625         var nodeName = currentElement.nodeName;
17626         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17627         
17628         if  (nodeName == '#text') {
17629             return currentElement.nodeValue;
17630         }
17631         
17632         
17633         var ret = '';
17634         if (nodeName != 'BODY') {
17635              
17636             var i = 0;
17637             // Prints the node tagName, such as <A>, <IMG>, etc
17638             if (tagName) {
17639                 var attr = [];
17640                 for(i = 0; i < currentElement.attributes.length;i++) {
17641                     // quoting?
17642                     var aname = currentElement.attributes.item(i).name;
17643                     if (!currentElement.attributes.item(i).value.length) {
17644                         continue;
17645                     }
17646                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17647                 }
17648                 
17649                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17650             } 
17651             else {
17652                 
17653                 // eack
17654             }
17655         } else {
17656             tagName = false;
17657         }
17658         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17659             return ret;
17660         }
17661         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17662             nopadtext = true;
17663         }
17664         
17665         
17666         // Traverse the tree
17667         i = 0;
17668         var currentElementChild = currentElement.childNodes.item(i);
17669         var allText = true;
17670         var innerHTML  = '';
17671         lastnode = '';
17672         while (currentElementChild) {
17673             // Formatting code (indent the tree so it looks nice on the screen)
17674             var nopad = nopadtext;
17675             if (lastnode == 'SPAN') {
17676                 nopad  = true;
17677             }
17678             // text
17679             if  (currentElementChild.nodeName == '#text') {
17680                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17681                 if (!nopad && toadd.length > 80) {
17682                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17683                 }
17684                 innerHTML  += toadd;
17685                 
17686                 i++;
17687                 currentElementChild = currentElement.childNodes.item(i);
17688                 lastNode = '';
17689                 continue;
17690             }
17691             allText = false;
17692             
17693             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17694                 
17695             // Recursively traverse the tree structure of the child node
17696             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17697             lastnode = currentElementChild.nodeName;
17698             i++;
17699             currentElementChild=currentElement.childNodes.item(i);
17700         }
17701         
17702         ret += innerHTML;
17703         
17704         if (!allText) {
17705                 // The remaining code is mostly for formatting the tree
17706             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17707         }
17708         
17709         
17710         if (tagName) {
17711             ret+= "</"+tagName+">";
17712         }
17713         return ret;
17714         
17715     },
17716         
17717     applyBlacklists : function()
17718     {
17719         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17720         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17721         
17722         this.white = [];
17723         this.black = [];
17724         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17725             if (b.indexOf(tag) > -1) {
17726                 return;
17727             }
17728             this.white.push(tag);
17729             
17730         }, this);
17731         
17732         Roo.each(w, function(tag) {
17733             if (b.indexOf(tag) > -1) {
17734                 return;
17735             }
17736             if (this.white.indexOf(tag) > -1) {
17737                 return;
17738             }
17739             this.white.push(tag);
17740             
17741         }, this);
17742         
17743         
17744         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17745             if (w.indexOf(tag) > -1) {
17746                 return;
17747             }
17748             this.black.push(tag);
17749             
17750         }, this);
17751         
17752         Roo.each(b, function(tag) {
17753             if (w.indexOf(tag) > -1) {
17754                 return;
17755             }
17756             if (this.black.indexOf(tag) > -1) {
17757                 return;
17758             }
17759             this.black.push(tag);
17760             
17761         }, this);
17762         
17763         
17764         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17765         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17766         
17767         this.cwhite = [];
17768         this.cblack = [];
17769         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17770             if (b.indexOf(tag) > -1) {
17771                 return;
17772             }
17773             this.cwhite.push(tag);
17774             
17775         }, this);
17776         
17777         Roo.each(w, function(tag) {
17778             if (b.indexOf(tag) > -1) {
17779                 return;
17780             }
17781             if (this.cwhite.indexOf(tag) > -1) {
17782                 return;
17783             }
17784             this.cwhite.push(tag);
17785             
17786         }, this);
17787         
17788         
17789         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17790             if (w.indexOf(tag) > -1) {
17791                 return;
17792             }
17793             this.cblack.push(tag);
17794             
17795         }, this);
17796         
17797         Roo.each(b, function(tag) {
17798             if (w.indexOf(tag) > -1) {
17799                 return;
17800             }
17801             if (this.cblack.indexOf(tag) > -1) {
17802                 return;
17803             }
17804             this.cblack.push(tag);
17805             
17806         }, this);
17807     }
17808     
17809     // hide stuff that is not compatible
17810     /**
17811      * @event blur
17812      * @hide
17813      */
17814     /**
17815      * @event change
17816      * @hide
17817      */
17818     /**
17819      * @event focus
17820      * @hide
17821      */
17822     /**
17823      * @event specialkey
17824      * @hide
17825      */
17826     /**
17827      * @cfg {String} fieldClass @hide
17828      */
17829     /**
17830      * @cfg {String} focusClass @hide
17831      */
17832     /**
17833      * @cfg {String} autoCreate @hide
17834      */
17835     /**
17836      * @cfg {String} inputType @hide
17837      */
17838     /**
17839      * @cfg {String} invalidClass @hide
17840      */
17841     /**
17842      * @cfg {String} invalidText @hide
17843      */
17844     /**
17845      * @cfg {String} msgFx @hide
17846      */
17847     /**
17848      * @cfg {String} validateOnBlur @hide
17849      */
17850 });
17851
17852 Roo.HtmlEditorCore.white = [
17853         'area', 'br', 'img', 'input', 'hr', 'wbr',
17854         
17855        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17856        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17857        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17858        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17859        'table',   'ul',         'xmp', 
17860        
17861        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17862       'thead',   'tr', 
17863      
17864       'dir', 'menu', 'ol', 'ul', 'dl',
17865        
17866       'embed',  'object'
17867 ];
17868
17869
17870 Roo.HtmlEditorCore.black = [
17871     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17872         'applet', // 
17873         'base',   'basefont', 'bgsound', 'blink',  'body', 
17874         'frame',  'frameset', 'head',    'html',   'ilayer', 
17875         'iframe', 'layer',  'link',     'meta',    'object',   
17876         'script', 'style' ,'title',  'xml' // clean later..
17877 ];
17878 Roo.HtmlEditorCore.clean = [
17879     'script', 'style', 'title', 'xml'
17880 ];
17881 Roo.HtmlEditorCore.remove = [
17882     'font'
17883 ];
17884 // attributes..
17885
17886 Roo.HtmlEditorCore.ablack = [
17887     'on'
17888 ];
17889     
17890 Roo.HtmlEditorCore.aclean = [ 
17891     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17892 ];
17893
17894 // protocols..
17895 Roo.HtmlEditorCore.pwhite= [
17896         'http',  'https',  'mailto'
17897 ];
17898
17899 // white listed style attributes.
17900 Roo.HtmlEditorCore.cwhite= [
17901       //  'text-align', /// default is to allow most things..
17902       
17903          
17904 //        'font-size'//??
17905 ];
17906
17907 // black listed style attributes.
17908 Roo.HtmlEditorCore.cblack= [
17909       //  'font-size' -- this can be set by the project 
17910 ];
17911
17912
17913 Roo.HtmlEditorCore.swapCodes   =[ 
17914     [    8211, "--" ], 
17915     [    8212, "--" ], 
17916     [    8216,  "'" ],  
17917     [    8217, "'" ],  
17918     [    8220, '"' ],  
17919     [    8221, '"' ],  
17920     [    8226, "*" ],  
17921     [    8230, "..." ]
17922 ]; 
17923
17924     /*
17925  * - LGPL
17926  *
17927  * HtmlEditor
17928  * 
17929  */
17930
17931 /**
17932  * @class Roo.bootstrap.HtmlEditor
17933  * @extends Roo.bootstrap.TextArea
17934  * Bootstrap HtmlEditor class
17935
17936  * @constructor
17937  * Create a new HtmlEditor
17938  * @param {Object} config The config object
17939  */
17940
17941 Roo.bootstrap.HtmlEditor = function(config){
17942     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17943     if (!this.toolbars) {
17944         this.toolbars = [];
17945     }
17946     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17947     this.addEvents({
17948             /**
17949              * @event initialize
17950              * Fires when the editor is fully initialized (including the iframe)
17951              * @param {HtmlEditor} this
17952              */
17953             initialize: true,
17954             /**
17955              * @event activate
17956              * Fires when the editor is first receives the focus. Any insertion must wait
17957              * until after this event.
17958              * @param {HtmlEditor} this
17959              */
17960             activate: true,
17961              /**
17962              * @event beforesync
17963              * Fires before the textarea is updated with content from the editor iframe. Return false
17964              * to cancel the sync.
17965              * @param {HtmlEditor} this
17966              * @param {String} html
17967              */
17968             beforesync: true,
17969              /**
17970              * @event beforepush
17971              * Fires before the iframe editor is updated with content from the textarea. Return false
17972              * to cancel the push.
17973              * @param {HtmlEditor} this
17974              * @param {String} html
17975              */
17976             beforepush: true,
17977              /**
17978              * @event sync
17979              * Fires when the textarea is updated with content from the editor iframe.
17980              * @param {HtmlEditor} this
17981              * @param {String} html
17982              */
17983             sync: true,
17984              /**
17985              * @event push
17986              * Fires when the iframe editor is updated with content from the textarea.
17987              * @param {HtmlEditor} this
17988              * @param {String} html
17989              */
17990             push: true,
17991              /**
17992              * @event editmodechange
17993              * Fires when the editor switches edit modes
17994              * @param {HtmlEditor} this
17995              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17996              */
17997             editmodechange: true,
17998             /**
17999              * @event editorevent
18000              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18001              * @param {HtmlEditor} this
18002              */
18003             editorevent: true,
18004             /**
18005              * @event firstfocus
18006              * Fires when on first focus - needed by toolbars..
18007              * @param {HtmlEditor} this
18008              */
18009             firstfocus: true,
18010             /**
18011              * @event autosave
18012              * Auto save the htmlEditor value as a file into Events
18013              * @param {HtmlEditor} this
18014              */
18015             autosave: true,
18016             /**
18017              * @event savedpreview
18018              * preview the saved version of htmlEditor
18019              * @param {HtmlEditor} this
18020              */
18021             savedpreview: true
18022         });
18023 };
18024
18025
18026 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18027     
18028     
18029       /**
18030      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18031      */
18032     toolbars : false,
18033    
18034      /**
18035      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18036      *                        Roo.resizable.
18037      */
18038     resizable : false,
18039      /**
18040      * @cfg {Number} height (in pixels)
18041      */   
18042     height: 300,
18043    /**
18044      * @cfg {Number} width (in pixels)
18045      */   
18046     width: false,
18047     
18048     /**
18049      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18050      * 
18051      */
18052     stylesheets: false,
18053     
18054     // id of frame..
18055     frameId: false,
18056     
18057     // private properties
18058     validationEvent : false,
18059     deferHeight: true,
18060     initialized : false,
18061     activated : false,
18062     
18063     onFocus : Roo.emptyFn,
18064     iframePad:3,
18065     hideMode:'offsets',
18066     
18067     
18068     tbContainer : false,
18069     
18070     toolbarContainer :function() {
18071         return this.wrap.select('.x-html-editor-tb',true).first();
18072     },
18073
18074     /**
18075      * Protected method that will not generally be called directly. It
18076      * is called when the editor creates its toolbar. Override this method if you need to
18077      * add custom toolbar buttons.
18078      * @param {HtmlEditor} editor
18079      */
18080     createToolbar : function(){
18081         
18082         Roo.log("create toolbars");
18083         
18084         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18085         this.toolbars[0].render(this.toolbarContainer());
18086         
18087         return;
18088         
18089 //        if (!editor.toolbars || !editor.toolbars.length) {
18090 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18091 //        }
18092 //        
18093 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18094 //            editor.toolbars[i] = Roo.factory(
18095 //                    typeof(editor.toolbars[i]) == 'string' ?
18096 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18097 //                Roo.bootstrap.HtmlEditor);
18098 //            editor.toolbars[i].init(editor);
18099 //        }
18100     },
18101
18102      
18103     // private
18104     onRender : function(ct, position)
18105     {
18106        // Roo.log("Call onRender: " + this.xtype);
18107         var _t = this;
18108         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18109       
18110         this.wrap = this.inputEl().wrap({
18111             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18112         });
18113         
18114         this.editorcore.onRender(ct, position);
18115          
18116         if (this.resizable) {
18117             this.resizeEl = new Roo.Resizable(this.wrap, {
18118                 pinned : true,
18119                 wrap: true,
18120                 dynamic : true,
18121                 minHeight : this.height,
18122                 height: this.height,
18123                 handles : this.resizable,
18124                 width: this.width,
18125                 listeners : {
18126                     resize : function(r, w, h) {
18127                         _t.onResize(w,h); // -something
18128                     }
18129                 }
18130             });
18131             
18132         }
18133         this.createToolbar(this);
18134        
18135         
18136         if(!this.width && this.resizable){
18137             this.setSize(this.wrap.getSize());
18138         }
18139         if (this.resizeEl) {
18140             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18141             // should trigger onReize..
18142         }
18143         
18144     },
18145
18146     // private
18147     onResize : function(w, h)
18148     {
18149         Roo.log('resize: ' +w + ',' + h );
18150         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18151         var ew = false;
18152         var eh = false;
18153         
18154         if(this.inputEl() ){
18155             if(typeof w == 'number'){
18156                 var aw = w - this.wrap.getFrameWidth('lr');
18157                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18158                 ew = aw;
18159             }
18160             if(typeof h == 'number'){
18161                  var tbh = -11;  // fixme it needs to tool bar size!
18162                 for (var i =0; i < this.toolbars.length;i++) {
18163                     // fixme - ask toolbars for heights?
18164                     tbh += this.toolbars[i].el.getHeight();
18165                     //if (this.toolbars[i].footer) {
18166                     //    tbh += this.toolbars[i].footer.el.getHeight();
18167                     //}
18168                 }
18169               
18170                 
18171                 
18172                 
18173                 
18174                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18175                 ah -= 5; // knock a few pixes off for look..
18176                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18177                 var eh = ah;
18178             }
18179         }
18180         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18181         this.editorcore.onResize(ew,eh);
18182         
18183     },
18184
18185     /**
18186      * Toggles the editor between standard and source edit mode.
18187      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18188      */
18189     toggleSourceEdit : function(sourceEditMode)
18190     {
18191         this.editorcore.toggleSourceEdit(sourceEditMode);
18192         
18193         if(this.editorcore.sourceEditMode){
18194             Roo.log('editor - showing textarea');
18195             
18196 //            Roo.log('in');
18197 //            Roo.log(this.syncValue());
18198             this.syncValue();
18199             this.inputEl().removeClass(['hide', 'x-hidden']);
18200             this.inputEl().dom.removeAttribute('tabIndex');
18201             this.inputEl().focus();
18202         }else{
18203             Roo.log('editor - hiding textarea');
18204 //            Roo.log('out')
18205 //            Roo.log(this.pushValue()); 
18206             this.pushValue();
18207             
18208             this.inputEl().addClass(['hide', 'x-hidden']);
18209             this.inputEl().dom.setAttribute('tabIndex', -1);
18210             //this.deferFocus();
18211         }
18212          
18213         if(this.resizable){
18214             this.setSize(this.wrap.getSize());
18215         }
18216         
18217         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18218     },
18219  
18220     // private (for BoxComponent)
18221     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18222
18223     // private (for BoxComponent)
18224     getResizeEl : function(){
18225         return this.wrap;
18226     },
18227
18228     // private (for BoxComponent)
18229     getPositionEl : function(){
18230         return this.wrap;
18231     },
18232
18233     // private
18234     initEvents : function(){
18235         this.originalValue = this.getValue();
18236     },
18237
18238 //    /**
18239 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18240 //     * @method
18241 //     */
18242 //    markInvalid : Roo.emptyFn,
18243 //    /**
18244 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18245 //     * @method
18246 //     */
18247 //    clearInvalid : Roo.emptyFn,
18248
18249     setValue : function(v){
18250         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18251         this.editorcore.pushValue();
18252     },
18253
18254      
18255     // private
18256     deferFocus : function(){
18257         this.focus.defer(10, this);
18258     },
18259
18260     // doc'ed in Field
18261     focus : function(){
18262         this.editorcore.focus();
18263         
18264     },
18265       
18266
18267     // private
18268     onDestroy : function(){
18269         
18270         
18271         
18272         if(this.rendered){
18273             
18274             for (var i =0; i < this.toolbars.length;i++) {
18275                 // fixme - ask toolbars for heights?
18276                 this.toolbars[i].onDestroy();
18277             }
18278             
18279             this.wrap.dom.innerHTML = '';
18280             this.wrap.remove();
18281         }
18282     },
18283
18284     // private
18285     onFirstFocus : function(){
18286         //Roo.log("onFirstFocus");
18287         this.editorcore.onFirstFocus();
18288          for (var i =0; i < this.toolbars.length;i++) {
18289             this.toolbars[i].onFirstFocus();
18290         }
18291         
18292     },
18293     
18294     // private
18295     syncValue : function()
18296     {   
18297         this.editorcore.syncValue();
18298     },
18299     
18300     pushValue : function()
18301     {   
18302         this.editorcore.pushValue();
18303     }
18304      
18305     
18306     // hide stuff that is not compatible
18307     /**
18308      * @event blur
18309      * @hide
18310      */
18311     /**
18312      * @event change
18313      * @hide
18314      */
18315     /**
18316      * @event focus
18317      * @hide
18318      */
18319     /**
18320      * @event specialkey
18321      * @hide
18322      */
18323     /**
18324      * @cfg {String} fieldClass @hide
18325      */
18326     /**
18327      * @cfg {String} focusClass @hide
18328      */
18329     /**
18330      * @cfg {String} autoCreate @hide
18331      */
18332     /**
18333      * @cfg {String} inputType @hide
18334      */
18335     /**
18336      * @cfg {String} invalidClass @hide
18337      */
18338     /**
18339      * @cfg {String} invalidText @hide
18340      */
18341     /**
18342      * @cfg {String} msgFx @hide
18343      */
18344     /**
18345      * @cfg {String} validateOnBlur @hide
18346      */
18347 });
18348  
18349     
18350    
18351    
18352    
18353       
18354 Roo.namespace('Roo.bootstrap.htmleditor');
18355 /**
18356  * @class Roo.bootstrap.HtmlEditorToolbar1
18357  * Basic Toolbar
18358  * 
18359  * Usage:
18360  *
18361  new Roo.bootstrap.HtmlEditor({
18362     ....
18363     toolbars : [
18364         new Roo.bootstrap.HtmlEditorToolbar1({
18365             disable : { fonts: 1 , format: 1, ..., ... , ...],
18366             btns : [ .... ]
18367         })
18368     }
18369      
18370  * 
18371  * @cfg {Object} disable List of elements to disable..
18372  * @cfg {Array} btns List of additional buttons.
18373  * 
18374  * 
18375  * NEEDS Extra CSS? 
18376  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18377  */
18378  
18379 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18380 {
18381     
18382     Roo.apply(this, config);
18383     
18384     // default disabled, based on 'good practice'..
18385     this.disable = this.disable || {};
18386     Roo.applyIf(this.disable, {
18387         fontSize : true,
18388         colors : true,
18389         specialElements : true
18390     });
18391     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18392     
18393     this.editor = config.editor;
18394     this.editorcore = config.editor.editorcore;
18395     
18396     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18397     
18398     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18399     // dont call parent... till later.
18400 }
18401 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18402      
18403     bar : true,
18404     
18405     editor : false,
18406     editorcore : false,
18407     
18408     
18409     formats : [
18410         "p" ,  
18411         "h1","h2","h3","h4","h5","h6", 
18412         "pre", "code", 
18413         "abbr", "acronym", "address", "cite", "samp", "var",
18414         'div','span'
18415     ],
18416     
18417     onRender : function(ct, position)
18418     {
18419        // Roo.log("Call onRender: " + this.xtype);
18420         
18421        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18422        Roo.log(this.el);
18423        this.el.dom.style.marginBottom = '0';
18424        var _this = this;
18425        var editorcore = this.editorcore;
18426        var editor= this.editor;
18427        
18428        var children = [];
18429        var btn = function(id,cmd , toggle, handler){
18430        
18431             var  event = toggle ? 'toggle' : 'click';
18432        
18433             var a = {
18434                 size : 'sm',
18435                 xtype: 'Button',
18436                 xns: Roo.bootstrap,
18437                 glyphicon : id,
18438                 cmd : id || cmd,
18439                 enableToggle:toggle !== false,
18440                 //html : 'submit'
18441                 pressed : toggle ? false : null,
18442                 listeners : {}
18443             }
18444             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18445                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18446             }
18447             children.push(a);
18448             return a;
18449        }
18450         
18451         var style = {
18452                 xtype: 'Button',
18453                 size : 'sm',
18454                 xns: Roo.bootstrap,
18455                 glyphicon : 'font',
18456                 //html : 'submit'
18457                 menu : {
18458                     xtype: 'Menu',
18459                     xns: Roo.bootstrap,
18460                     items:  []
18461                 }
18462         };
18463         Roo.each(this.formats, function(f) {
18464             style.menu.items.push({
18465                 xtype :'MenuItem',
18466                 xns: Roo.bootstrap,
18467                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18468                 tagname : f,
18469                 listeners : {
18470                     click : function()
18471                     {
18472                         editorcore.insertTag(this.tagname);
18473                         editor.focus();
18474                     }
18475                 }
18476                 
18477             });
18478         });
18479          children.push(style);   
18480             
18481             
18482         btn('bold',false,true);
18483         btn('italic',false,true);
18484         btn('align-left', 'justifyleft',true);
18485         btn('align-center', 'justifycenter',true);
18486         btn('align-right' , 'justifyright',true);
18487         btn('link', false, false, function(btn) {
18488             //Roo.log("create link?");
18489             var url = prompt(this.createLinkText, this.defaultLinkValue);
18490             if(url && url != 'http:/'+'/'){
18491                 this.editorcore.relayCmd('createlink', url);
18492             }
18493         }),
18494         btn('list','insertunorderedlist',true);
18495         btn('pencil', false,true, function(btn){
18496                 Roo.log(this);
18497                 
18498                 this.toggleSourceEdit(btn.pressed);
18499         });
18500         /*
18501         var cog = {
18502                 xtype: 'Button',
18503                 size : 'sm',
18504                 xns: Roo.bootstrap,
18505                 glyphicon : 'cog',
18506                 //html : 'submit'
18507                 menu : {
18508                     xtype: 'Menu',
18509                     xns: Roo.bootstrap,
18510                     items:  []
18511                 }
18512         };
18513         
18514         cog.menu.items.push({
18515             xtype :'MenuItem',
18516             xns: Roo.bootstrap,
18517             html : Clean styles,
18518             tagname : f,
18519             listeners : {
18520                 click : function()
18521                 {
18522                     editorcore.insertTag(this.tagname);
18523                     editor.focus();
18524                 }
18525             }
18526             
18527         });
18528        */
18529         
18530          
18531        this.xtype = 'NavSimplebar';
18532         
18533         for(var i=0;i< children.length;i++) {
18534             
18535             this.buttons.add(this.addxtypeChild(children[i]));
18536             
18537         }
18538         
18539         editor.on('editorevent', this.updateToolbar, this);
18540     },
18541     onBtnClick : function(id)
18542     {
18543        this.editorcore.relayCmd(id);
18544        this.editorcore.focus();
18545     },
18546     
18547     /**
18548      * Protected method that will not generally be called directly. It triggers
18549      * a toolbar update by reading the markup state of the current selection in the editor.
18550      */
18551     updateToolbar: function(){
18552
18553         if(!this.editorcore.activated){
18554             this.editor.onFirstFocus(); // is this neeed?
18555             return;
18556         }
18557
18558         var btns = this.buttons; 
18559         var doc = this.editorcore.doc;
18560         btns.get('bold').setActive(doc.queryCommandState('bold'));
18561         btns.get('italic').setActive(doc.queryCommandState('italic'));
18562         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18563         
18564         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18565         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18566         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18567         
18568         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18569         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18570          /*
18571         
18572         var ans = this.editorcore.getAllAncestors();
18573         if (this.formatCombo) {
18574             
18575             
18576             var store = this.formatCombo.store;
18577             this.formatCombo.setValue("");
18578             for (var i =0; i < ans.length;i++) {
18579                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18580                     // select it..
18581                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18582                     break;
18583                 }
18584             }
18585         }
18586         
18587         
18588         
18589         // hides menus... - so this cant be on a menu...
18590         Roo.bootstrap.MenuMgr.hideAll();
18591         */
18592         Roo.bootstrap.MenuMgr.hideAll();
18593         //this.editorsyncValue();
18594     },
18595     onFirstFocus: function() {
18596         this.buttons.each(function(item){
18597            item.enable();
18598         });
18599     },
18600     toggleSourceEdit : function(sourceEditMode){
18601         
18602           
18603         if(sourceEditMode){
18604             Roo.log("disabling buttons");
18605            this.buttons.each( function(item){
18606                 if(item.cmd != 'pencil'){
18607                     item.disable();
18608                 }
18609             });
18610           
18611         }else{
18612             Roo.log("enabling buttons");
18613             if(this.editorcore.initialized){
18614                 this.buttons.each( function(item){
18615                     item.enable();
18616                 });
18617             }
18618             
18619         }
18620         Roo.log("calling toggole on editor");
18621         // tell the editor that it's been pressed..
18622         this.editor.toggleSourceEdit(sourceEditMode);
18623        
18624     }
18625 });
18626
18627
18628
18629
18630
18631 /**
18632  * @class Roo.bootstrap.Table.AbstractSelectionModel
18633  * @extends Roo.util.Observable
18634  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18635  * implemented by descendant classes.  This class should not be directly instantiated.
18636  * @constructor
18637  */
18638 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18639     this.locked = false;
18640     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18641 };
18642
18643
18644 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18645     /** @ignore Called by the grid automatically. Do not call directly. */
18646     init : function(grid){
18647         this.grid = grid;
18648         this.initEvents();
18649     },
18650
18651     /**
18652      * Locks the selections.
18653      */
18654     lock : function(){
18655         this.locked = true;
18656     },
18657
18658     /**
18659      * Unlocks the selections.
18660      */
18661     unlock : function(){
18662         this.locked = false;
18663     },
18664
18665     /**
18666      * Returns true if the selections are locked.
18667      * @return {Boolean}
18668      */
18669     isLocked : function(){
18670         return this.locked;
18671     }
18672 });
18673 /**
18674  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18675  * @class Roo.bootstrap.Table.RowSelectionModel
18676  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18677  * It supports multiple selections and keyboard selection/navigation. 
18678  * @constructor
18679  * @param {Object} config
18680  */
18681
18682 Roo.bootstrap.Table.RowSelectionModel = function(config){
18683     Roo.apply(this, config);
18684     this.selections = new Roo.util.MixedCollection(false, function(o){
18685         return o.id;
18686     });
18687
18688     this.last = false;
18689     this.lastActive = false;
18690
18691     this.addEvents({
18692         /**
18693              * @event selectionchange
18694              * Fires when the selection changes
18695              * @param {SelectionModel} this
18696              */
18697             "selectionchange" : true,
18698         /**
18699              * @event afterselectionchange
18700              * Fires after the selection changes (eg. by key press or clicking)
18701              * @param {SelectionModel} this
18702              */
18703             "afterselectionchange" : true,
18704         /**
18705              * @event beforerowselect
18706              * Fires when a row is selected being selected, return false to cancel.
18707              * @param {SelectionModel} this
18708              * @param {Number} rowIndex The selected index
18709              * @param {Boolean} keepExisting False if other selections will be cleared
18710              */
18711             "beforerowselect" : true,
18712         /**
18713              * @event rowselect
18714              * Fires when a row is selected.
18715              * @param {SelectionModel} this
18716              * @param {Number} rowIndex The selected index
18717              * @param {Roo.data.Record} r The record
18718              */
18719             "rowselect" : true,
18720         /**
18721              * @event rowdeselect
18722              * Fires when a row is deselected.
18723              * @param {SelectionModel} this
18724              * @param {Number} rowIndex The selected index
18725              */
18726         "rowdeselect" : true
18727     });
18728     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18729     this.locked = false;
18730 };
18731
18732 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18733     /**
18734      * @cfg {Boolean} singleSelect
18735      * True to allow selection of only one row at a time (defaults to false)
18736      */
18737     singleSelect : false,
18738
18739     // private
18740     initEvents : function(){
18741
18742         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18743             this.grid.on("mousedown", this.handleMouseDown, this);
18744         }else{ // allow click to work like normal
18745             this.grid.on("rowclick", this.handleDragableRowClick, this);
18746         }
18747
18748         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18749             "up" : function(e){
18750                 if(!e.shiftKey){
18751                     this.selectPrevious(e.shiftKey);
18752                 }else if(this.last !== false && this.lastActive !== false){
18753                     var last = this.last;
18754                     this.selectRange(this.last,  this.lastActive-1);
18755                     this.grid.getView().focusRow(this.lastActive);
18756                     if(last !== false){
18757                         this.last = last;
18758                     }
18759                 }else{
18760                     this.selectFirstRow();
18761                 }
18762                 this.fireEvent("afterselectionchange", this);
18763             },
18764             "down" : function(e){
18765                 if(!e.shiftKey){
18766                     this.selectNext(e.shiftKey);
18767                 }else if(this.last !== false && this.lastActive !== false){
18768                     var last = this.last;
18769                     this.selectRange(this.last,  this.lastActive+1);
18770                     this.grid.getView().focusRow(this.lastActive);
18771                     if(last !== false){
18772                         this.last = last;
18773                     }
18774                 }else{
18775                     this.selectFirstRow();
18776                 }
18777                 this.fireEvent("afterselectionchange", this);
18778             },
18779             scope: this
18780         });
18781
18782         var view = this.grid.view;
18783         view.on("refresh", this.onRefresh, this);
18784         view.on("rowupdated", this.onRowUpdated, this);
18785         view.on("rowremoved", this.onRemove, this);
18786     },
18787
18788     // private
18789     onRefresh : function(){
18790         var ds = this.grid.dataSource, i, v = this.grid.view;
18791         var s = this.selections;
18792         s.each(function(r){
18793             if((i = ds.indexOfId(r.id)) != -1){
18794                 v.onRowSelect(i);
18795             }else{
18796                 s.remove(r);
18797             }
18798         });
18799     },
18800
18801     // private
18802     onRemove : function(v, index, r){
18803         this.selections.remove(r);
18804     },
18805
18806     // private
18807     onRowUpdated : function(v, index, r){
18808         if(this.isSelected(r)){
18809             v.onRowSelect(index);
18810         }
18811     },
18812
18813     /**
18814      * Select records.
18815      * @param {Array} records The records to select
18816      * @param {Boolean} keepExisting (optional) True to keep existing selections
18817      */
18818     selectRecords : function(records, keepExisting){
18819         if(!keepExisting){
18820             this.clearSelections();
18821         }
18822         var ds = this.grid.dataSource;
18823         for(var i = 0, len = records.length; i < len; i++){
18824             this.selectRow(ds.indexOf(records[i]), true);
18825         }
18826     },
18827
18828     /**
18829      * Gets the number of selected rows.
18830      * @return {Number}
18831      */
18832     getCount : function(){
18833         return this.selections.length;
18834     },
18835
18836     /**
18837      * Selects the first row in the grid.
18838      */
18839     selectFirstRow : function(){
18840         this.selectRow(0);
18841     },
18842
18843     /**
18844      * Select the last row.
18845      * @param {Boolean} keepExisting (optional) True to keep existing selections
18846      */
18847     selectLastRow : function(keepExisting){
18848         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18849     },
18850
18851     /**
18852      * Selects the row immediately following the last selected row.
18853      * @param {Boolean} keepExisting (optional) True to keep existing selections
18854      */
18855     selectNext : function(keepExisting){
18856         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18857             this.selectRow(this.last+1, keepExisting);
18858             this.grid.getView().focusRow(this.last);
18859         }
18860     },
18861
18862     /**
18863      * Selects the row that precedes the last selected row.
18864      * @param {Boolean} keepExisting (optional) True to keep existing selections
18865      */
18866     selectPrevious : function(keepExisting){
18867         if(this.last){
18868             this.selectRow(this.last-1, keepExisting);
18869             this.grid.getView().focusRow(this.last);
18870         }
18871     },
18872
18873     /**
18874      * Returns the selected records
18875      * @return {Array} Array of selected records
18876      */
18877     getSelections : function(){
18878         return [].concat(this.selections.items);
18879     },
18880
18881     /**
18882      * Returns the first selected record.
18883      * @return {Record}
18884      */
18885     getSelected : function(){
18886         return this.selections.itemAt(0);
18887     },
18888
18889
18890     /**
18891      * Clears all selections.
18892      */
18893     clearSelections : function(fast){
18894         if(this.locked) return;
18895         if(fast !== true){
18896             var ds = this.grid.dataSource;
18897             var s = this.selections;
18898             s.each(function(r){
18899                 this.deselectRow(ds.indexOfId(r.id));
18900             }, this);
18901             s.clear();
18902         }else{
18903             this.selections.clear();
18904         }
18905         this.last = false;
18906     },
18907
18908
18909     /**
18910      * Selects all rows.
18911      */
18912     selectAll : function(){
18913         if(this.locked) return;
18914         this.selections.clear();
18915         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18916             this.selectRow(i, true);
18917         }
18918     },
18919
18920     /**
18921      * Returns True if there is a selection.
18922      * @return {Boolean}
18923      */
18924     hasSelection : function(){
18925         return this.selections.length > 0;
18926     },
18927
18928     /**
18929      * Returns True if the specified row is selected.
18930      * @param {Number/Record} record The record or index of the record to check
18931      * @return {Boolean}
18932      */
18933     isSelected : function(index){
18934         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18935         return (r && this.selections.key(r.id) ? true : false);
18936     },
18937
18938     /**
18939      * Returns True if the specified record id is selected.
18940      * @param {String} id The id of record to check
18941      * @return {Boolean}
18942      */
18943     isIdSelected : function(id){
18944         return (this.selections.key(id) ? true : false);
18945     },
18946
18947     // private
18948     handleMouseDown : function(e, t){
18949         var view = this.grid.getView(), rowIndex;
18950         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18951             return;
18952         };
18953         if(e.shiftKey && this.last !== false){
18954             var last = this.last;
18955             this.selectRange(last, rowIndex, e.ctrlKey);
18956             this.last = last; // reset the last
18957             view.focusRow(rowIndex);
18958         }else{
18959             var isSelected = this.isSelected(rowIndex);
18960             if(e.button !== 0 && isSelected){
18961                 view.focusRow(rowIndex);
18962             }else if(e.ctrlKey && isSelected){
18963                 this.deselectRow(rowIndex);
18964             }else if(!isSelected){
18965                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18966                 view.focusRow(rowIndex);
18967             }
18968         }
18969         this.fireEvent("afterselectionchange", this);
18970     },
18971     // private
18972     handleDragableRowClick :  function(grid, rowIndex, e) 
18973     {
18974         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18975             this.selectRow(rowIndex, false);
18976             grid.view.focusRow(rowIndex);
18977              this.fireEvent("afterselectionchange", this);
18978         }
18979     },
18980     
18981     /**
18982      * Selects multiple rows.
18983      * @param {Array} rows Array of the indexes of the row to select
18984      * @param {Boolean} keepExisting (optional) True to keep existing selections
18985      */
18986     selectRows : function(rows, keepExisting){
18987         if(!keepExisting){
18988             this.clearSelections();
18989         }
18990         for(var i = 0, len = rows.length; i < len; i++){
18991             this.selectRow(rows[i], true);
18992         }
18993     },
18994
18995     /**
18996      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18997      * @param {Number} startRow The index of the first row in the range
18998      * @param {Number} endRow The index of the last row in the range
18999      * @param {Boolean} keepExisting (optional) True to retain existing selections
19000      */
19001     selectRange : function(startRow, endRow, keepExisting){
19002         if(this.locked) return;
19003         if(!keepExisting){
19004             this.clearSelections();
19005         }
19006         if(startRow <= endRow){
19007             for(var i = startRow; i <= endRow; i++){
19008                 this.selectRow(i, true);
19009             }
19010         }else{
19011             for(var i = startRow; i >= endRow; i--){
19012                 this.selectRow(i, true);
19013             }
19014         }
19015     },
19016
19017     /**
19018      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19019      * @param {Number} startRow The index of the first row in the range
19020      * @param {Number} endRow The index of the last row in the range
19021      */
19022     deselectRange : function(startRow, endRow, preventViewNotify){
19023         if(this.locked) return;
19024         for(var i = startRow; i <= endRow; i++){
19025             this.deselectRow(i, preventViewNotify);
19026         }
19027     },
19028
19029     /**
19030      * Selects a row.
19031      * @param {Number} row The index of the row to select
19032      * @param {Boolean} keepExisting (optional) True to keep existing selections
19033      */
19034     selectRow : function(index, keepExisting, preventViewNotify){
19035         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19036         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19037             if(!keepExisting || this.singleSelect){
19038                 this.clearSelections();
19039             }
19040             var r = this.grid.dataSource.getAt(index);
19041             this.selections.add(r);
19042             this.last = this.lastActive = index;
19043             if(!preventViewNotify){
19044                 this.grid.getView().onRowSelect(index);
19045             }
19046             this.fireEvent("rowselect", this, index, r);
19047             this.fireEvent("selectionchange", this);
19048         }
19049     },
19050
19051     /**
19052      * Deselects a row.
19053      * @param {Number} row The index of the row to deselect
19054      */
19055     deselectRow : function(index, preventViewNotify){
19056         if(this.locked) return;
19057         if(this.last == index){
19058             this.last = false;
19059         }
19060         if(this.lastActive == index){
19061             this.lastActive = false;
19062         }
19063         var r = this.grid.dataSource.getAt(index);
19064         this.selections.remove(r);
19065         if(!preventViewNotify){
19066             this.grid.getView().onRowDeselect(index);
19067         }
19068         this.fireEvent("rowdeselect", this, index);
19069         this.fireEvent("selectionchange", this);
19070     },
19071
19072     // private
19073     restoreLast : function(){
19074         if(this._last){
19075             this.last = this._last;
19076         }
19077     },
19078
19079     // private
19080     acceptsNav : function(row, col, cm){
19081         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19082     },
19083
19084     // private
19085     onEditorKey : function(field, e){
19086         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19087         if(k == e.TAB){
19088             e.stopEvent();
19089             ed.completeEdit();
19090             if(e.shiftKey){
19091                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19092             }else{
19093                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19094             }
19095         }else if(k == e.ENTER && !e.ctrlKey){
19096             e.stopEvent();
19097             ed.completeEdit();
19098             if(e.shiftKey){
19099                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19100             }else{
19101                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19102             }
19103         }else if(k == e.ESC){
19104             ed.cancelEdit();
19105         }
19106         if(newCell){
19107             g.startEditing(newCell[0], newCell[1]);
19108         }
19109     }
19110 });/*
19111  * Based on:
19112  * Ext JS Library 1.1.1
19113  * Copyright(c) 2006-2007, Ext JS, LLC.
19114  *
19115  * Originally Released Under LGPL - original licence link has changed is not relivant.
19116  *
19117  * Fork - LGPL
19118  * <script type="text/javascript">
19119  */
19120  
19121 /**
19122  * @class Roo.bootstrap.PagingToolbar
19123  * @extends Roo.Row
19124  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19125  * @constructor
19126  * Create a new PagingToolbar
19127  * @param {Object} config The config object
19128  */
19129 Roo.bootstrap.PagingToolbar = function(config)
19130 {
19131     // old args format still supported... - xtype is prefered..
19132         // created from xtype...
19133     var ds = config.dataSource;
19134     this.toolbarItems = [];
19135     if (config.items) {
19136         this.toolbarItems = config.items;
19137 //        config.items = [];
19138     }
19139     
19140     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19141     this.ds = ds;
19142     this.cursor = 0;
19143     if (ds) { 
19144         this.bind(ds);
19145     }
19146     
19147     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19148     
19149 };
19150
19151 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19152     /**
19153      * @cfg {Roo.data.Store} dataSource
19154      * The underlying data store providing the paged data
19155      */
19156     /**
19157      * @cfg {String/HTMLElement/Element} container
19158      * container The id or element that will contain the toolbar
19159      */
19160     /**
19161      * @cfg {Boolean} displayInfo
19162      * True to display the displayMsg (defaults to false)
19163      */
19164     /**
19165      * @cfg {Number} pageSize
19166      * The number of records to display per page (defaults to 20)
19167      */
19168     pageSize: 20,
19169     /**
19170      * @cfg {String} displayMsg
19171      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19172      */
19173     displayMsg : 'Displaying {0} - {1} of {2}',
19174     /**
19175      * @cfg {String} emptyMsg
19176      * The message to display when no records are found (defaults to "No data to display")
19177      */
19178     emptyMsg : 'No data to display',
19179     /**
19180      * Customizable piece of the default paging text (defaults to "Page")
19181      * @type String
19182      */
19183     beforePageText : "Page",
19184     /**
19185      * Customizable piece of the default paging text (defaults to "of %0")
19186      * @type String
19187      */
19188     afterPageText : "of {0}",
19189     /**
19190      * Customizable piece of the default paging text (defaults to "First Page")
19191      * @type String
19192      */
19193     firstText : "First Page",
19194     /**
19195      * Customizable piece of the default paging text (defaults to "Previous Page")
19196      * @type String
19197      */
19198     prevText : "Previous Page",
19199     /**
19200      * Customizable piece of the default paging text (defaults to "Next Page")
19201      * @type String
19202      */
19203     nextText : "Next Page",
19204     /**
19205      * Customizable piece of the default paging text (defaults to "Last Page")
19206      * @type String
19207      */
19208     lastText : "Last Page",
19209     /**
19210      * Customizable piece of the default paging text (defaults to "Refresh")
19211      * @type String
19212      */
19213     refreshText : "Refresh",
19214
19215     buttons : false,
19216     // private
19217     onRender : function(ct, position) 
19218     {
19219         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19220         this.navgroup.parentId = this.id;
19221         this.navgroup.onRender(this.el, null);
19222         // add the buttons to the navgroup
19223         
19224         if(this.displayInfo){
19225             Roo.log(this.el.select('ul.navbar-nav',true).first());
19226             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19227             this.displayEl = this.el.select('.x-paging-info', true).first();
19228 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19229 //            this.displayEl = navel.el.select('span',true).first();
19230         }
19231         
19232         var _this = this;
19233         
19234         if(this.buttons){
19235             Roo.each(_this.buttons, function(e){
19236                Roo.factory(e).onRender(_this.el, null);
19237             });
19238         }
19239             
19240         Roo.each(_this.toolbarItems, function(e) {
19241             _this.navgroup.addItem(e);
19242         });
19243         
19244         this.first = this.navgroup.addItem({
19245             tooltip: this.firstText,
19246             cls: "prev",
19247             icon : 'fa fa-backward',
19248             disabled: true,
19249             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19250         });
19251         
19252         this.prev =  this.navgroup.addItem({
19253             tooltip: this.prevText,
19254             cls: "prev",
19255             icon : 'fa fa-step-backward',
19256             disabled: true,
19257             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19258         });
19259     //this.addSeparator();
19260         
19261         
19262         var field = this.navgroup.addItem( {
19263             tagtype : 'span',
19264             cls : 'x-paging-position',
19265             
19266             html : this.beforePageText  +
19267                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19268                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19269          } ); //?? escaped?
19270         
19271         this.field = field.el.select('input', true).first();
19272         this.field.on("keydown", this.onPagingKeydown, this);
19273         this.field.on("focus", function(){this.dom.select();});
19274     
19275     
19276         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19277         //this.field.setHeight(18);
19278         //this.addSeparator();
19279         this.next = this.navgroup.addItem({
19280             tooltip: this.nextText,
19281             cls: "next",
19282             html : ' <i class="fa fa-step-forward">',
19283             disabled: true,
19284             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19285         });
19286         this.last = this.navgroup.addItem({
19287             tooltip: this.lastText,
19288             icon : 'fa fa-forward',
19289             cls: "next",
19290             disabled: true,
19291             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19292         });
19293     //this.addSeparator();
19294         this.loading = this.navgroup.addItem({
19295             tooltip: this.refreshText,
19296             icon: 'fa fa-refresh',
19297             
19298             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19299         });
19300
19301     },
19302
19303     // private
19304     updateInfo : function(){
19305         if(this.displayEl){
19306             var count = this.ds.getCount();
19307             var msg = count == 0 ?
19308                 this.emptyMsg :
19309                 String.format(
19310                     this.displayMsg,
19311                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19312                 );
19313             this.displayEl.update(msg);
19314         }
19315     },
19316
19317     // private
19318     onLoad : function(ds, r, o){
19319        this.cursor = o.params ? o.params.start : 0;
19320        var d = this.getPageData(),
19321             ap = d.activePage,
19322             ps = d.pages;
19323         
19324        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19325        this.field.dom.value = ap;
19326        this.first.setDisabled(ap == 1);
19327        this.prev.setDisabled(ap == 1);
19328        this.next.setDisabled(ap == ps);
19329        this.last.setDisabled(ap == ps);
19330        this.loading.enable();
19331        this.updateInfo();
19332     },
19333
19334     // private
19335     getPageData : function(){
19336         var total = this.ds.getTotalCount();
19337         return {
19338             total : total,
19339             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19340             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19341         };
19342     },
19343
19344     // private
19345     onLoadError : function(){
19346         this.loading.enable();
19347     },
19348
19349     // private
19350     onPagingKeydown : function(e){
19351         var k = e.getKey();
19352         var d = this.getPageData();
19353         if(k == e.RETURN){
19354             var v = this.field.dom.value, pageNum;
19355             if(!v || isNaN(pageNum = parseInt(v, 10))){
19356                 this.field.dom.value = d.activePage;
19357                 return;
19358             }
19359             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19360             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19361             e.stopEvent();
19362         }
19363         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))
19364         {
19365           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19366           this.field.dom.value = pageNum;
19367           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19368           e.stopEvent();
19369         }
19370         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19371         {
19372           var v = this.field.dom.value, pageNum; 
19373           var increment = (e.shiftKey) ? 10 : 1;
19374           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19375             increment *= -1;
19376           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19377             this.field.dom.value = d.activePage;
19378             return;
19379           }
19380           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19381           {
19382             this.field.dom.value = parseInt(v, 10) + increment;
19383             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19384             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19385           }
19386           e.stopEvent();
19387         }
19388     },
19389
19390     // private
19391     beforeLoad : function(){
19392         if(this.loading){
19393             this.loading.disable();
19394         }
19395     },
19396
19397     // private
19398     onClick : function(which){
19399         var ds = this.ds;
19400         if (!ds) {
19401             return;
19402         }
19403         switch(which){
19404             case "first":
19405                 ds.load({params:{start: 0, limit: this.pageSize}});
19406             break;
19407             case "prev":
19408                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19409             break;
19410             case "next":
19411                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19412             break;
19413             case "last":
19414                 var total = ds.getTotalCount();
19415                 var extra = total % this.pageSize;
19416                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19417                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19418             break;
19419             case "refresh":
19420                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19421             break;
19422         }
19423     },
19424
19425     /**
19426      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19427      * @param {Roo.data.Store} store The data store to unbind
19428      */
19429     unbind : function(ds){
19430         ds.un("beforeload", this.beforeLoad, this);
19431         ds.un("load", this.onLoad, this);
19432         ds.un("loadexception", this.onLoadError, this);
19433         ds.un("remove", this.updateInfo, this);
19434         ds.un("add", this.updateInfo, this);
19435         this.ds = undefined;
19436     },
19437
19438     /**
19439      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19440      * @param {Roo.data.Store} store The data store to bind
19441      */
19442     bind : function(ds){
19443         ds.on("beforeload", this.beforeLoad, this);
19444         ds.on("load", this.onLoad, this);
19445         ds.on("loadexception", this.onLoadError, this);
19446         ds.on("remove", this.updateInfo, this);
19447         ds.on("add", this.updateInfo, this);
19448         this.ds = ds;
19449     }
19450 });/*
19451  * - LGPL
19452  *
19453  * element
19454  * 
19455  */
19456
19457 /**
19458  * @class Roo.bootstrap.MessageBar
19459  * @extends Roo.bootstrap.Component
19460  * Bootstrap MessageBar class
19461  * @cfg {String} html contents of the MessageBar
19462  * @cfg {String} weight (info | success | warning | danger) default info
19463  * @cfg {String} beforeClass insert the bar before the given class
19464  * @cfg {Boolean} closable (true | false) default false
19465  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19466  * 
19467  * @constructor
19468  * Create a new Element
19469  * @param {Object} config The config object
19470  */
19471
19472 Roo.bootstrap.MessageBar = function(config){
19473     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19474 };
19475
19476 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19477     
19478     html: '',
19479     weight: 'info',
19480     closable: false,
19481     fixed: false,
19482     beforeClass: 'bootstrap-sticky-wrap',
19483     
19484     getAutoCreate : function(){
19485         
19486         var cfg = {
19487             tag: 'div',
19488             cls: 'alert alert-dismissable alert-' + this.weight,
19489             cn: [
19490                 {
19491                     tag: 'span',
19492                     cls: 'message',
19493                     html: this.html || ''
19494                 }
19495             ]
19496         }
19497         
19498         if(this.fixed){
19499             cfg.cls += ' alert-messages-fixed';
19500         }
19501         
19502         if(this.closable){
19503             cfg.cn.push({
19504                 tag: 'button',
19505                 cls: 'close',
19506                 html: 'x'
19507             });
19508         }
19509         
19510         return cfg;
19511     },
19512     
19513     onRender : function(ct, position)
19514     {
19515         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19516         
19517         if(!this.el){
19518             var cfg = Roo.apply({},  this.getAutoCreate());
19519             cfg.id = Roo.id();
19520             
19521             if (this.cls) {
19522                 cfg.cls += ' ' + this.cls;
19523             }
19524             if (this.style) {
19525                 cfg.style = this.style;
19526             }
19527             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19528             
19529             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19530         }
19531         
19532         this.el.select('>button.close').on('click', this.hide, this);
19533         
19534     },
19535     
19536     show : function()
19537     {
19538         if (!this.rendered) {
19539             this.render();
19540         }
19541         
19542         this.el.show();
19543         
19544         this.fireEvent('show', this);
19545         
19546     },
19547     
19548     hide : function()
19549     {
19550         if (!this.rendered) {
19551             this.render();
19552         }
19553         
19554         this.el.hide();
19555         
19556         this.fireEvent('hide', this);
19557     },
19558     
19559     update : function()
19560     {
19561 //        var e = this.el.dom.firstChild;
19562 //        
19563 //        if(this.closable){
19564 //            e = e.nextSibling;
19565 //        }
19566 //        
19567 //        e.data = this.html || '';
19568
19569         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19570     }
19571    
19572 });
19573
19574  
19575
19576      /*
19577  * - LGPL
19578  *
19579  * Graph
19580  * 
19581  */
19582
19583
19584 /**
19585  * @class Roo.bootstrap.Graph
19586  * @extends Roo.bootstrap.Component
19587  * Bootstrap Graph class
19588 > Prameters
19589  -sm {number} sm 4
19590  -md {number} md 5
19591  @cfg {String} graphtype  bar | vbar | pie
19592  @cfg {number} g_x coodinator | centre x (pie)
19593  @cfg {number} g_y coodinator | centre y (pie)
19594  @cfg {number} g_r radius (pie)
19595  @cfg {number} g_height height of the chart (respected by all elements in the set)
19596  @cfg {number} g_width width of the chart (respected by all elements in the set)
19597  @cfg {Object} title The title of the chart
19598     
19599  -{Array}  values
19600  -opts (object) options for the chart 
19601      o {
19602      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19603      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19604      o vgutter (number)
19605      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.
19606      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19607      o to
19608      o stretch (boolean)
19609      o }
19610  -opts (object) options for the pie
19611      o{
19612      o cut
19613      o startAngle (number)
19614      o endAngle (number)
19615      } 
19616  *
19617  * @constructor
19618  * Create a new Input
19619  * @param {Object} config The config object
19620  */
19621
19622 Roo.bootstrap.Graph = function(config){
19623     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19624     
19625     this.addEvents({
19626         // img events
19627         /**
19628          * @event click
19629          * The img click event for the img.
19630          * @param {Roo.EventObject} e
19631          */
19632         "click" : true
19633     });
19634 };
19635
19636 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19637     
19638     sm: 4,
19639     md: 5,
19640     graphtype: 'bar',
19641     g_height: 250,
19642     g_width: 400,
19643     g_x: 50,
19644     g_y: 50,
19645     g_r: 30,
19646     opts:{
19647         //g_colors: this.colors,
19648         g_type: 'soft',
19649         g_gutter: '20%'
19650
19651     },
19652     title : false,
19653
19654     getAutoCreate : function(){
19655         
19656         var cfg = {
19657             tag: 'div',
19658             html : null
19659         }
19660         
19661         
19662         return  cfg;
19663     },
19664
19665     onRender : function(ct,position){
19666         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19667         this.raphael = Raphael(this.el.dom);
19668         
19669                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19670                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19671                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19672                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19673                 /*
19674                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19675                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19676                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19677                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19678                 
19679                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19680                 r.barchart(330, 10, 300, 220, data1);
19681                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19682                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19683                 */
19684                 
19685                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19686                 // r.barchart(30, 30, 560, 250,  xdata, {
19687                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19688                 //     axis : "0 0 1 1",
19689                 //     axisxlabels :  xdata
19690                 //     //yvalues : cols,
19691                    
19692                 // });
19693 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19694 //        
19695 //        this.load(null,xdata,{
19696 //                axis : "0 0 1 1",
19697 //                axisxlabels :  xdata
19698 //                });
19699
19700     },
19701
19702     load : function(graphtype,xdata,opts){
19703         this.raphael.clear();
19704         if(!graphtype) {
19705             graphtype = this.graphtype;
19706         }
19707         if(!opts){
19708             opts = this.opts;
19709         }
19710         var r = this.raphael,
19711             fin = function () {
19712                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19713             },
19714             fout = function () {
19715                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19716             },
19717             pfin = function() {
19718                 this.sector.stop();
19719                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19720
19721                 if (this.label) {
19722                     this.label[0].stop();
19723                     this.label[0].attr({ r: 7.5 });
19724                     this.label[1].attr({ "font-weight": 800 });
19725                 }
19726             },
19727             pfout = function() {
19728                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19729
19730                 if (this.label) {
19731                     this.label[0].animate({ r: 5 }, 500, "bounce");
19732                     this.label[1].attr({ "font-weight": 400 });
19733                 }
19734             };
19735
19736         switch(graphtype){
19737             case 'bar':
19738                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19739                 break;
19740             case 'hbar':
19741                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19742                 break;
19743             case 'pie':
19744 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19745 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19746 //            
19747                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19748                 
19749                 break;
19750
19751         }
19752         
19753         if(this.title){
19754             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19755         }
19756         
19757     },
19758     
19759     setTitle: function(o)
19760     {
19761         this.title = o;
19762     },
19763     
19764     initEvents: function() {
19765         
19766         if(!this.href){
19767             this.el.on('click', this.onClick, this);
19768         }
19769     },
19770     
19771     onClick : function(e)
19772     {
19773         Roo.log('img onclick');
19774         this.fireEvent('click', this, e);
19775     }
19776    
19777 });
19778
19779  
19780 /*
19781  * - LGPL
19782  *
19783  * numberBox
19784  * 
19785  */
19786 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19787
19788 /**
19789  * @class Roo.bootstrap.dash.NumberBox
19790  * @extends Roo.bootstrap.Component
19791  * Bootstrap NumberBox class
19792  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19793  * @cfg {String} headline Box headline
19794  * @cfg {String} content Box content
19795  * @cfg {String} icon Box icon
19796  * @cfg {String} footer Footer text
19797  * @cfg {String} fhref Footer href
19798  * 
19799  * @constructor
19800  * Create a new NumberBox
19801  * @param {Object} config The config object
19802  */
19803
19804
19805 Roo.bootstrap.dash.NumberBox = function(config){
19806     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19807     
19808 };
19809
19810 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19811     
19812     bgcolor : 'aqua',
19813     headline : '',
19814     content : '',
19815     icon : '',
19816     footer : '',
19817     fhref : '',
19818     ficon : '',
19819     
19820     getAutoCreate : function(){
19821         
19822         var cfg = {
19823             tag : 'div',
19824             cls : 'small-box bg-' + this.bgcolor,
19825             cn : [
19826                 {
19827                     tag : 'div',
19828                     cls : 'inner',
19829                     cn :[
19830                         {
19831                             tag : 'h3',
19832                             cls : 'roo-headline',
19833                             html : this.headline
19834                         },
19835                         {
19836                             tag : 'p',
19837                             cls : 'roo-content',
19838                             html : this.content
19839                         }
19840                     ]
19841                 }
19842             ]
19843         }
19844         
19845         if(this.icon){
19846             cfg.cn.push({
19847                 tag : 'div',
19848                 cls : 'icon',
19849                 cn :[
19850                     {
19851                         tag : 'i',
19852                         cls : 'ion ' + this.icon
19853                     }
19854                 ]
19855             });
19856         }
19857         
19858         if(this.footer){
19859             var footer = {
19860                 tag : 'a',
19861                 cls : 'small-box-footer',
19862                 href : this.fhref || '#',
19863                 html : this.footer
19864             };
19865             
19866             cfg.cn.push(footer);
19867             
19868         }
19869         
19870         return  cfg;
19871     },
19872
19873     onRender : function(ct,position){
19874         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19875
19876
19877        
19878                 
19879     },
19880
19881     setHeadline: function (value)
19882     {
19883         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19884     },
19885     
19886     setFooter: function (value, href)
19887     {
19888         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19889         
19890         if(href){
19891             this.el.select('a.small-box-footer',true).first().attr('href', href);
19892         }
19893         
19894     },
19895
19896     setContent: function (value)
19897     {
19898         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19899     },
19900
19901     initEvents: function() 
19902     {   
19903         
19904     }
19905     
19906 });
19907
19908  
19909 /*
19910  * - LGPL
19911  *
19912  * TabBox
19913  * 
19914  */
19915 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19916
19917 /**
19918  * @class Roo.bootstrap.dash.TabBox
19919  * @extends Roo.bootstrap.Component
19920  * Bootstrap TabBox class
19921  * @cfg {String} title Title of the TabBox
19922  * @cfg {String} icon Icon of the TabBox
19923  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19924  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19925  * 
19926  * @constructor
19927  * Create a new TabBox
19928  * @param {Object} config The config object
19929  */
19930
19931
19932 Roo.bootstrap.dash.TabBox = function(config){
19933     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19934     this.addEvents({
19935         // raw events
19936         /**
19937          * @event addpane
19938          * When a pane is added
19939          * @param {Roo.bootstrap.dash.TabPane} pane
19940          */
19941         "addpane" : true,
19942         /**
19943          * @event activatepane
19944          * When a pane is activated
19945          * @param {Roo.bootstrap.dash.TabPane} pane
19946          */
19947         "activatepane" : true
19948         
19949          
19950     });
19951     
19952     this.panes = [];
19953 };
19954
19955 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19956
19957     title : '',
19958     icon : false,
19959     showtabs : true,
19960     tabScrollable : false,
19961     
19962     getChildContainer : function()
19963     {
19964         return this.el.select('.tab-content', true).first();
19965     },
19966     
19967     getAutoCreate : function(){
19968         
19969         var header = {
19970             tag: 'li',
19971             cls: 'pull-left header',
19972             html: this.title,
19973             cn : []
19974         };
19975         
19976         if(this.icon){
19977             header.cn.push({
19978                 tag: 'i',
19979                 cls: 'fa ' + this.icon
19980             });
19981         }
19982         
19983         var h = {
19984             tag: 'ul',
19985             cls: 'nav nav-tabs pull-right',
19986             cn: [
19987                 header
19988             ]
19989         };
19990         
19991         if(this.tabScrollable){
19992             h = {
19993                 tag: 'div',
19994                 cls: 'tab-header',
19995                 cn: [
19996                     {
19997                         tag: 'ul',
19998                         cls: 'nav nav-tabs pull-right',
19999                         cn: [
20000                             header
20001                         ]
20002                     }
20003                 ]
20004             }
20005         }
20006         
20007         var cfg = {
20008             tag: 'div',
20009             cls: 'nav-tabs-custom',
20010             cn: [
20011                 h,
20012                 {
20013                     tag: 'div',
20014                     cls: 'tab-content no-padding',
20015                     cn: []
20016                 }
20017             ]
20018         }
20019
20020         return  cfg;
20021     },
20022     initEvents : function()
20023     {
20024         //Roo.log('add add pane handler');
20025         this.on('addpane', this.onAddPane, this);
20026     },
20027      /**
20028      * Updates the box title
20029      * @param {String} html to set the title to.
20030      */
20031     setTitle : function(value)
20032     {
20033         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20034     },
20035     onAddPane : function(pane)
20036     {
20037         this.panes.push(pane);
20038         //Roo.log('addpane');
20039         //Roo.log(pane);
20040         // tabs are rendere left to right..
20041         if(!this.showtabs){
20042             return;
20043         }
20044         
20045         var ctr = this.el.select('.nav-tabs', true).first();
20046          
20047          
20048         var existing = ctr.select('.nav-tab',true);
20049         var qty = existing.getCount();;
20050         
20051         
20052         var tab = ctr.createChild({
20053             tag : 'li',
20054             cls : 'nav-tab' + (qty ? '' : ' active'),
20055             cn : [
20056                 {
20057                     tag : 'a',
20058                     href:'#',
20059                     html : pane.title
20060                 }
20061             ]
20062         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20063         pane.tab = tab;
20064         
20065         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20066         if (!qty) {
20067             pane.el.addClass('active');
20068         }
20069         
20070                 
20071     },
20072     onTabClick : function(ev,un,ob,pane)
20073     {
20074         //Roo.log('tab - prev default');
20075         ev.preventDefault();
20076         
20077         
20078         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20079         pane.tab.addClass('active');
20080         //Roo.log(pane.title);
20081         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20082         // technically we should have a deactivate event.. but maybe add later.
20083         // and it should not de-activate the selected tab...
20084         this.fireEvent('activatepane', pane);
20085         pane.el.addClass('active');
20086         pane.fireEvent('activate');
20087         
20088         
20089     },
20090     
20091     getActivePane : function()
20092     {
20093         var r = false;
20094         Roo.each(this.panes, function(p) {
20095             if(p.el.hasClass('active')){
20096                 r = p;
20097                 return false;
20098             }
20099             
20100             return;
20101         });
20102         
20103         return r;
20104     }
20105     
20106     
20107 });
20108
20109  
20110 /*
20111  * - LGPL
20112  *
20113  * Tab pane
20114  * 
20115  */
20116 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20117 /**
20118  * @class Roo.bootstrap.TabPane
20119  * @extends Roo.bootstrap.Component
20120  * Bootstrap TabPane class
20121  * @cfg {Boolean} active (false | true) Default false
20122  * @cfg {String} title title of panel
20123
20124  * 
20125  * @constructor
20126  * Create a new TabPane
20127  * @param {Object} config The config object
20128  */
20129
20130 Roo.bootstrap.dash.TabPane = function(config){
20131     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20132     
20133     this.addEvents({
20134         // raw events
20135         /**
20136          * @event activate
20137          * When a pane is activated
20138          * @param {Roo.bootstrap.dash.TabPane} pane
20139          */
20140         "activate" : true
20141          
20142     });
20143 };
20144
20145 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20146     
20147     active : false,
20148     title : '',
20149     
20150     // the tabBox that this is attached to.
20151     tab : false,
20152      
20153     getAutoCreate : function() 
20154     {
20155         var cfg = {
20156             tag: 'div',
20157             cls: 'tab-pane'
20158         }
20159         
20160         if(this.active){
20161             cfg.cls += ' active';
20162         }
20163         
20164         return cfg;
20165     },
20166     initEvents  : function()
20167     {
20168         //Roo.log('trigger add pane handler');
20169         this.parent().fireEvent('addpane', this)
20170     },
20171     
20172      /**
20173      * Updates the tab title 
20174      * @param {String} html to set the title to.
20175      */
20176     setTitle: function(str)
20177     {
20178         if (!this.tab) {
20179             return;
20180         }
20181         this.title = str;
20182         this.tab.select('a', true).first().dom.innerHTML = str;
20183         
20184     }
20185     
20186     
20187     
20188 });
20189
20190  
20191
20192
20193  /*
20194  * - LGPL
20195  *
20196  * menu
20197  * 
20198  */
20199 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20200
20201 /**
20202  * @class Roo.bootstrap.menu.Menu
20203  * @extends Roo.bootstrap.Component
20204  * Bootstrap Menu class - container for Menu
20205  * @cfg {String} html Text of the menu
20206  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20207  * @cfg {String} icon Font awesome icon
20208  * @cfg {String} pos Menu align to (top | bottom) default bottom
20209  * 
20210  * 
20211  * @constructor
20212  * Create a new Menu
20213  * @param {Object} config The config object
20214  */
20215
20216
20217 Roo.bootstrap.menu.Menu = function(config){
20218     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20219     
20220     this.addEvents({
20221         /**
20222          * @event beforeshow
20223          * Fires before this menu is displayed
20224          * @param {Roo.bootstrap.menu.Menu} this
20225          */
20226         beforeshow : true,
20227         /**
20228          * @event beforehide
20229          * Fires before this menu is hidden
20230          * @param {Roo.bootstrap.menu.Menu} this
20231          */
20232         beforehide : true,
20233         /**
20234          * @event show
20235          * Fires after this menu is displayed
20236          * @param {Roo.bootstrap.menu.Menu} this
20237          */
20238         show : true,
20239         /**
20240          * @event hide
20241          * Fires after this menu is hidden
20242          * @param {Roo.bootstrap.menu.Menu} this
20243          */
20244         hide : true,
20245         /**
20246          * @event click
20247          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20248          * @param {Roo.bootstrap.menu.Menu} this
20249          * @param {Roo.EventObject} e
20250          */
20251         click : true
20252     });
20253     
20254 };
20255
20256 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20257     
20258     submenu : false,
20259     html : '',
20260     weight : 'default',
20261     icon : false,
20262     pos : 'bottom',
20263     
20264     
20265     getChildContainer : function() {
20266         if(this.isSubMenu){
20267             return this.el;
20268         }
20269         
20270         return this.el.select('ul.dropdown-menu', true).first();  
20271     },
20272     
20273     getAutoCreate : function()
20274     {
20275         var text = [
20276             {
20277                 tag : 'span',
20278                 cls : 'roo-menu-text',
20279                 html : this.html
20280             }
20281         ];
20282         
20283         if(this.icon){
20284             text.unshift({
20285                 tag : 'i',
20286                 cls : 'fa ' + this.icon
20287             })
20288         }
20289         
20290         
20291         var cfg = {
20292             tag : 'div',
20293             cls : 'btn-group',
20294             cn : [
20295                 {
20296                     tag : 'button',
20297                     cls : 'dropdown-button btn btn-' + this.weight,
20298                     cn : text
20299                 },
20300                 {
20301                     tag : 'button',
20302                     cls : 'dropdown-toggle btn btn-' + this.weight,
20303                     cn : [
20304                         {
20305                             tag : 'span',
20306                             cls : 'caret'
20307                         }
20308                     ]
20309                 },
20310                 {
20311                     tag : 'ul',
20312                     cls : 'dropdown-menu'
20313                 }
20314             ]
20315             
20316         };
20317         
20318         if(this.pos == 'top'){
20319             cfg.cls += ' dropup';
20320         }
20321         
20322         if(this.isSubMenu){
20323             cfg = {
20324                 tag : 'ul',
20325                 cls : 'dropdown-menu'
20326             }
20327         }
20328         
20329         return cfg;
20330     },
20331     
20332     onRender : function(ct, position)
20333     {
20334         this.isSubMenu = ct.hasClass('dropdown-submenu');
20335         
20336         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20337     },
20338     
20339     initEvents : function() 
20340     {
20341         if(this.isSubMenu){
20342             return;
20343         }
20344         
20345         this.hidden = true;
20346         
20347         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20348         this.triggerEl.on('click', this.onTriggerPress, this);
20349         
20350         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20351         this.buttonEl.on('click', this.onClick, this);
20352         
20353     },
20354     
20355     list : function()
20356     {
20357         if(this.isSubMenu){
20358             return this.el;
20359         }
20360         
20361         return this.el.select('ul.dropdown-menu', true).first();
20362     },
20363     
20364     onClick : function(e)
20365     {
20366         this.fireEvent("click", this, e);
20367     },
20368     
20369     onTriggerPress  : function(e)
20370     {   
20371         if (this.isVisible()) {
20372             this.hide();
20373         } else {
20374             this.show();
20375         }
20376     },
20377     
20378     isVisible : function(){
20379         return !this.hidden;
20380     },
20381     
20382     show : function()
20383     {
20384         this.fireEvent("beforeshow", this);
20385         
20386         this.hidden = false;
20387         this.el.addClass('open');
20388         
20389         Roo.get(document).on("mouseup", this.onMouseUp, this);
20390         
20391         this.fireEvent("show", this);
20392         
20393         
20394     },
20395     
20396     hide : function()
20397     {
20398         this.fireEvent("beforehide", this);
20399         
20400         this.hidden = true;
20401         this.el.removeClass('open');
20402         
20403         Roo.get(document).un("mouseup", this.onMouseUp);
20404         
20405         this.fireEvent("hide", this);
20406     },
20407     
20408     onMouseUp : function()
20409     {
20410         this.hide();
20411     }
20412     
20413 });
20414
20415  
20416  /*
20417  * - LGPL
20418  *
20419  * menu item
20420  * 
20421  */
20422 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20423
20424 /**
20425  * @class Roo.bootstrap.menu.Item
20426  * @extends Roo.bootstrap.Component
20427  * Bootstrap MenuItem class
20428  * @cfg {Boolean} submenu (true | false) default false
20429  * @cfg {String} html text of the item
20430  * @cfg {String} href the link
20431  * @cfg {Boolean} disable (true | false) default false
20432  * @cfg {Boolean} preventDefault (true | false) default true
20433  * @cfg {String} icon Font awesome icon
20434  * @cfg {String} pos Submenu align to (left | right) default right 
20435  * 
20436  * 
20437  * @constructor
20438  * Create a new Item
20439  * @param {Object} config The config object
20440  */
20441
20442
20443 Roo.bootstrap.menu.Item = function(config){
20444     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20445     this.addEvents({
20446         /**
20447          * @event mouseover
20448          * Fires when the mouse is hovering over this menu
20449          * @param {Roo.bootstrap.menu.Item} this
20450          * @param {Roo.EventObject} e
20451          */
20452         mouseover : true,
20453         /**
20454          * @event mouseout
20455          * Fires when the mouse exits this menu
20456          * @param {Roo.bootstrap.menu.Item} this
20457          * @param {Roo.EventObject} e
20458          */
20459         mouseout : true,
20460         // raw events
20461         /**
20462          * @event click
20463          * The raw click event for the entire grid.
20464          * @param {Roo.EventObject} e
20465          */
20466         click : true
20467     });
20468 };
20469
20470 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20471     
20472     submenu : false,
20473     href : '',
20474     html : '',
20475     preventDefault: true,
20476     disable : false,
20477     icon : false,
20478     pos : 'right',
20479     
20480     getAutoCreate : function()
20481     {
20482         var text = [
20483             {
20484                 tag : 'span',
20485                 cls : 'roo-menu-item-text',
20486                 html : this.html
20487             }
20488         ];
20489         
20490         if(this.icon){
20491             text.unshift({
20492                 tag : 'i',
20493                 cls : 'fa ' + this.icon
20494             })
20495         }
20496         
20497         var cfg = {
20498             tag : 'li',
20499             cn : [
20500                 {
20501                     tag : 'a',
20502                     href : this.href || '#',
20503                     cn : text
20504                 }
20505             ]
20506         };
20507         
20508         if(this.disable){
20509             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20510         }
20511         
20512         if(this.submenu){
20513             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20514             
20515             if(this.pos == 'left'){
20516                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20517             }
20518         }
20519         
20520         return cfg;
20521     },
20522     
20523     initEvents : function() 
20524     {
20525         this.el.on('mouseover', this.onMouseOver, this);
20526         this.el.on('mouseout', this.onMouseOut, this);
20527         
20528         this.el.select('a', true).first().on('click', this.onClick, this);
20529         
20530     },
20531     
20532     onClick : function(e)
20533     {
20534         if(this.preventDefault){
20535             e.preventDefault();
20536         }
20537         
20538         this.fireEvent("click", this, e);
20539     },
20540     
20541     onMouseOver : function(e)
20542     {
20543         if(this.submenu && this.pos == 'left'){
20544             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20545         }
20546         
20547         this.fireEvent("mouseover", this, e);
20548     },
20549     
20550     onMouseOut : function(e)
20551     {
20552         this.fireEvent("mouseout", this, e);
20553     }
20554 });
20555
20556  
20557
20558  /*
20559  * - LGPL
20560  *
20561  * menu separator
20562  * 
20563  */
20564 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20565
20566 /**
20567  * @class Roo.bootstrap.menu.Separator
20568  * @extends Roo.bootstrap.Component
20569  * Bootstrap Separator class
20570  * 
20571  * @constructor
20572  * Create a new Separator
20573  * @param {Object} config The config object
20574  */
20575
20576
20577 Roo.bootstrap.menu.Separator = function(config){
20578     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20579 };
20580
20581 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20582     
20583     getAutoCreate : function(){
20584         var cfg = {
20585             tag : 'li',
20586             cls: 'divider'
20587         };
20588         
20589         return cfg;
20590     }
20591    
20592 });
20593
20594  
20595
20596