Roo/form/ComboBoxArray.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * 
21  * @constructor
22  * Do not use directly - it does not do anything..
23  * @param {Object} config The config object
24  */
25
26
27
28 Roo.bootstrap.Component = function(config){
29     Roo.bootstrap.Component.superclass.constructor.call(this, config);
30 };
31
32 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
33     
34     
35     allowDomMove : false, // to stop relocations in parent onRender...
36     
37     cls : false,
38     
39     style : false,
40     
41     autoCreate : false,
42     
43     tooltip : null,
44     /**
45      * Initialize Events for the element
46      */
47     initEvents : function() { },
48     
49     xattr : false,
50     
51     parentId : false,
52     
53     can_build_overlaid : true,
54     
55     dataId : false,
56     
57     name : false,
58     
59     parent: function() {
60         // returns the parent component..
61         return Roo.ComponentMgr.get(this.parentId)
62         
63         
64     },
65     
66     // private
67     onRender : function(ct, position)
68     {
69        // Roo.log("Call onRender: " + this.xtype);
70         
71         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
72         
73         if(this.el){
74             if (this.el.attr('xtype')) {
75                 this.el.attr('xtypex', this.el.attr('xtype'));
76                 this.el.dom.removeAttribute('xtype');
77                 
78                 this.initEvents();
79             }
80             
81             return;
82         }
83         
84          
85         
86         var cfg = Roo.apply({},  this.getAutoCreate());
87         cfg.id = Roo.id();
88         
89         // fill in the extra attributes 
90         if (this.xattr && typeof(this.xattr) =='object') {
91             for (var i in this.xattr) {
92                 cfg[i] = this.xattr[i];
93             }
94         }
95         
96         if(this.dataId){
97             cfg.dataId = this.dataId;
98         }
99         
100         if (this.cls) {
101             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
102         }
103         
104         if (this.style) { // fixme needs to support more complex style data.
105             cfg.style = this.style;
106         }
107         
108         if(this.name){
109             cfg.name = this.name;
110         }
111         
112        
113         
114         this.el = ct.createChild(cfg, position);
115         
116         if (this.tooltip) {
117             this.tooltipEl().attr('tooltip', this.tooltip);
118         }
119         
120         if(this.tabIndex !== undefined){
121             this.el.dom.setAttribute('tabIndex', this.tabIndex);
122         }
123         this.initEvents();
124         
125         
126     },
127     /**
128      * Fetch the element to add children to
129      * @return {Roo.Element} defaults to this.el
130      */
131     getChildContainer : function()
132     {
133         return this.el;
134     },
135     /**
136      * Fetch the element to display the tooltip on.
137      * @return {Roo.Element} defaults to this.el
138      */
139     tooltipEl : function()
140     {
141         return this.el;
142     },
143         
144     addxtype  : function(tree,cntr)
145     {
146         var cn = this;
147         
148         cn = Roo.factory(tree);
149            
150         cn.parentType = this.xtype; //??
151         cn.parentId = this.id;
152         
153         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
154         
155         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
156         
157         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
158         
159         var build_from_html =  Roo.XComponent.build_from_html;
160           
161         var is_body  = (tree.xtype == 'Body') ;
162           
163         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
164           
165         var self_cntr_el = Roo.get(this[cntr](false));
166         
167         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
168             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
169                 return this.addxtypeChild(tree,cntr);
170             }
171             
172             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
173                 
174             if(echild){
175                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
176             }
177             
178             Roo.log('skipping render');
179             return cn;
180             
181         }
182         
183         var ret = false;
184         
185         while (true) {
186             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
187             
188             if (!echild) {
189                 break;
190             }
191             
192             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
193                 break;
194             }
195             
196             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
197         }
198         return ret;
199     },
200     
201     addxtypeChild : function (tree, cntr)
202     {
203         Roo.debug && Roo.log('addxtypeChild:' + cntr);
204         var cn = this;
205         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
206         
207         
208         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
209                     (typeof(tree['flexy:foreach']) != 'undefined');
210           
211         
212         
213          skip_children = false;
214         // render the element if it's not BODY.
215         if (tree.xtype != 'Body') {
216            
217             cn = Roo.factory(tree);
218            
219             cn.parentType = this.xtype; //??
220             cn.parentId = this.id;
221             
222             var build_from_html =  Roo.XComponent.build_from_html;
223             
224             
225             // does the container contain child eleemnts with 'xtype' attributes.
226             // that match this xtype..
227             // note - when we render we create these as well..
228             // so we should check to see if body has xtype set.
229             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
230                
231                 var self_cntr_el = Roo.get(this[cntr](false));
232                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233                 
234                 
235                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
236                 // and are not displayed -this causes this to use up the wrong element when matching.
237                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
238                 
239                 
240                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
241                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
242                   
243                   
244                   
245                     cn.el = echild;
246                   //  Roo.log("GOT");
247                     //echild.dom.removeAttribute('xtype');
248                 } else {
249                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
250                     Roo.debug && Roo.log(self_cntr_el);
251                     Roo.debug && Roo.log(echild);
252                     Roo.debug && Roo.log(cn);
253                 }
254             }
255            
256             
257            
258             // if object has flexy:if - then it may or may not be rendered.
259             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
260                 // skip a flexy if element.
261                 Roo.debug && Roo.log('skipping render');
262                 Roo.debug && Roo.log(tree);
263                 if (!cn.el) {
264                     Roo.debug && Roo.log('skipping all children');
265                     skip_children = true;
266                 }
267                 
268              } else {
269                  
270                 // actually if flexy:foreach is found, we really want to create 
271                 // multiple copies here...
272                 //Roo.log('render');
273                 //Roo.log(this[cntr]());
274                 cn.render(this[cntr](true));
275              }
276             // then add the element..
277         }
278         
279         
280         // handle the kids..
281         
282         var nitems = [];
283         /*
284         if (typeof (tree.menu) != 'undefined') {
285             tree.menu.parentType = cn.xtype;
286             tree.menu.triggerEl = cn.el;
287             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
288             
289         }
290         */
291         if (!tree.items || !tree.items.length) {
292             cn.items = nitems;
293             return cn;
294         }
295         var items = tree.items;
296         delete tree.items;
297         
298         //Roo.log(items.length);
299             // add the items..
300         if (!skip_children) {    
301             for(var i =0;i < items.length;i++) {
302                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
303             }
304         }
305         
306         cn.items = nitems;
307         
308         return cn;
309     }
310     
311     
312     
313     
314 });
315
316  /*
317  * - LGPL
318  *
319  * Body
320  * 
321  */
322
323 /**
324  * @class Roo.bootstrap.Body
325  * @extends Roo.bootstrap.Component
326  * Bootstrap Body class
327  * 
328  * @constructor
329  * Create a new body
330  * @param {Object} config The config object
331  */
332
333 Roo.bootstrap.Body = function(config){
334     Roo.bootstrap.Body.superclass.constructor.call(this, config);
335     this.el = Roo.get(document.body);
336     if (this.cls && this.cls.length) {
337         Roo.get(document.body).addClass(this.cls);
338     }
339 };
340
341 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
342       
343         autoCreate : {
344         cls: 'container'
345     },
346     onRender : function(ct, position)
347     {
348        /* Roo.log("Roo.bootstrap.Body - onRender");
349         if (this.cls && this.cls.length) {
350             Roo.get(document.body).addClass(this.cls);
351         }
352         // style??? xttr???
353         */
354     }
355     
356     
357  
358    
359 });
360
361  /*
362  * - LGPL
363  *
364  * button group
365  * 
366  */
367
368
369 /**
370  * @class Roo.bootstrap.ButtonGroup
371  * @extends Roo.bootstrap.Component
372  * Bootstrap ButtonGroup class
373  * @cfg {String} size lg | sm | xs (default empty normal)
374  * @cfg {String} align vertical | justified  (default none)
375  * @cfg {String} direction up | down (default down)
376  * @cfg {Boolean} toolbar false | true
377  * @cfg {Boolean} btn true | false
378  * 
379  * 
380  * @constructor
381  * Create a new Input
382  * @param {Object} config The config object
383  */
384
385 Roo.bootstrap.ButtonGroup = function(config){
386     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
387 };
388
389 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
390     
391     size: '',
392     align: '',
393     direction: '',
394     toolbar: false,
395     btn: true,
396
397     getAutoCreate : function(){
398         var cfg = {
399             cls: 'btn-group',
400             html : null
401         }
402         
403         cfg.html = this.html || cfg.html;
404         
405         if (this.toolbar) {
406             cfg = {
407                 cls: 'btn-toolbar',
408                 html: null
409             }
410             
411             return cfg;
412         }
413         
414         if (['vertical','justified'].indexOf(this.align)!==-1) {
415             cfg.cls = 'btn-group-' + this.align;
416             
417             if (this.align == 'justified') {
418                 console.log(this.items);
419             }
420         }
421         
422         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
423             cfg.cls += ' btn-group-' + this.size;
424         }
425         
426         if (this.direction == 'up') {
427             cfg.cls += ' dropup' ;
428         }
429         
430         return cfg;
431     }
432    
433 });
434
435  /*
436  * - LGPL
437  *
438  * button
439  * 
440  */
441
442 /**
443  * @class Roo.bootstrap.Button
444  * @extends Roo.bootstrap.Component
445  * Bootstrap Button class
446  * @cfg {String} html The button content
447  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
448  * @cfg {String} size empty | lg | sm | xs
449  * @cfg {String} tag empty | a | input | submit
450  * @cfg {String} href empty or href
451  * @cfg {Boolean} disabled false | true
452  * @cfg {Boolean} isClose false | true
453  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
454  * @cfg {String} badge text for badge
455  * @cfg {String} theme default (or empty) | glow
456  * @cfg {Boolean} inverse false | true
457  * @cfg {Boolean} toggle false | true
458  * @cfg {String} ontext text for on toggle state
459  * @cfg {String} offtext text for off toggle state
460  * @cfg {Boolean} defaulton true | false
461  * @cfg {Boolean} preventDefault (true | false) default true
462  * @cfg {Boolean} removeClass true | false remove the standard class..
463  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
464  * 
465  * @constructor
466  * Create a new button
467  * @param {Object} config The config object
468  */
469
470
471 Roo.bootstrap.Button = function(config){
472     Roo.bootstrap.Button.superclass.constructor.call(this, config);
473     this.addEvents({
474         // raw events
475         /**
476          * @event click
477          * When a butotn is pressed
478          * @param {Roo.EventObject} e
479          */
480         "click" : true,
481          /**
482          * @event toggle
483          * After the button has been toggles
484          * @param {Roo.EventObject} e
485          * @param {boolean} pressed (also available as button.pressed)
486          */
487         "toggle" : true
488     });
489 };
490
491 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
492     html: false,
493     active: false,
494     weight: '',
495     size: '',
496     tag: 'button',
497     href: '',
498     disabled: false,
499     isClose: false,
500     glyphicon: '',
501     badge: '',
502     theme: 'default',
503     inverse: false,
504     
505     toggle: false,
506     ontext: 'ON',
507     offtext: 'OFF',
508     defaulton: true,
509     preventDefault: true,
510     removeClass: false,
511     name: false,
512     target: false,
513     
514     
515     pressed : null,
516      
517     
518     getAutoCreate : function(){
519         
520         var cfg = {
521             tag : 'button',
522             cls : 'roo-button',
523             html: ''
524         };
525         
526         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
527             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
528             this.tag = 'button';
529         } else {
530             cfg.tag = this.tag;
531         }
532         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
533         
534         if (this.toggle == true) {
535             cfg={
536                 tag: 'div',
537                 cls: 'slider-frame roo-button',
538                 cn: [
539                     {
540                         tag: 'span',
541                         'data-on-text':'ON',
542                         'data-off-text':'OFF',
543                         cls: 'slider-button',
544                         html: this.offtext
545                     }
546                 ]
547             };
548             
549             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
550                 cfg.cls += ' '+this.weight;
551             }
552             
553             return cfg;
554         }
555         
556         if (this.isClose) {
557             cfg.cls += ' close';
558             
559             cfg["aria-hidden"] = true;
560             
561             cfg.html = "&times;";
562             
563             return cfg;
564         }
565         
566          
567         if (this.theme==='default') {
568             cfg.cls = 'btn roo-button';
569             
570             //if (this.parentType != 'Navbar') {
571             this.weight = this.weight.length ?  this.weight : 'default';
572             //}
573             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
574                 
575                 cfg.cls += ' btn-' + this.weight;
576             }
577         } else if (this.theme==='glow') {
578             
579             cfg.tag = 'a';
580             cfg.cls = 'btn-glow roo-button';
581             
582             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
583                 
584                 cfg.cls += ' ' + this.weight;
585             }
586         }
587    
588         
589         if (this.inverse) {
590             this.cls += ' inverse';
591         }
592         
593         
594         if (this.active) {
595             cfg.cls += ' active';
596         }
597         
598         if (this.disabled) {
599             cfg.disabled = 'disabled';
600         }
601         
602         if (this.items) {
603             Roo.log('changing to ul' );
604             cfg.tag = 'ul';
605             this.glyphicon = 'caret';
606         }
607         
608         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
609          
610         //gsRoo.log(this.parentType);
611         if (this.parentType === 'Navbar' && !this.parent().bar) {
612             Roo.log('changing to li?');
613             
614             cfg.tag = 'li';
615             
616             cfg.cls = '';
617             cfg.cn =  [{
618                 tag : 'a',
619                 cls : 'roo-button',
620                 html : this.html,
621                 href : this.href || '#'
622             }];
623             if (this.menu) {
624                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
625                 cfg.cls += ' dropdown';
626             }   
627             
628             delete cfg.html;
629             
630         }
631         
632        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
633         
634         if (this.glyphicon) {
635             cfg.html = ' ' + cfg.html;
636             
637             cfg.cn = [
638                 {
639                     tag: 'span',
640                     cls: 'glyphicon glyphicon-' + this.glyphicon
641                 }
642             ];
643         }
644         
645         if (this.badge) {
646             cfg.html += ' ';
647             
648             cfg.tag = 'a';
649             
650 //            cfg.cls='btn roo-button';
651             
652             cfg.href=this.href;
653             
654             var value = cfg.html;
655             
656             if(this.glyphicon){
657                 value = {
658                             tag: 'span',
659                             cls: 'glyphicon glyphicon-' + this.glyphicon,
660                             html: this.html
661                         };
662                 
663             }
664             
665             cfg.cn = [
666                 value,
667                 {
668                     tag: 'span',
669                     cls: 'badge',
670                     html: this.badge
671                 }
672             ];
673             
674             cfg.html='';
675         }
676         
677         if (this.menu) {
678             cfg.cls += ' dropdown';
679             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
680         }
681         
682         if (cfg.tag !== 'a' && this.href !== '') {
683             throw "Tag must be a to set href.";
684         } else if (this.href.length > 0) {
685             cfg.href = this.href;
686         }
687         
688         if(this.removeClass){
689             cfg.cls = '';
690         }
691         
692         if(this.target){
693             cfg.target = this.target;
694         }
695         
696         return cfg;
697     },
698     initEvents: function() {
699        // Roo.log('init events?');
700 //        Roo.log(this.el.dom);
701         // add the menu...
702         
703         if (typeof (this.menu) != 'undefined') {
704             this.menu.parentType = this.xtype;
705             this.menu.triggerEl = this.el;
706             this.addxtype(Roo.apply({}, this.menu));
707         }
708
709
710        if (this.el.hasClass('roo-button')) {
711             this.el.on('click', this.onClick, this);
712        } else {
713             this.el.select('.roo-button').on('click', this.onClick, this);
714        }
715        
716        if(this.removeClass){
717            this.el.on('click', this.onClick, this);
718        }
719        
720        this.el.enableDisplayMode();
721         
722     },
723     onClick : function(e)
724     {
725         if (this.disabled) {
726             return;
727         }
728         
729         Roo.log('button on click ');
730         if(this.preventDefault){
731             e.preventDefault();
732         }
733         if (this.pressed === true || this.pressed === false) {
734             this.pressed = !this.pressed;
735             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
736             this.fireEvent('toggle', this, e, this.pressed);
737         }
738         
739         
740         this.fireEvent('click', this, e);
741     },
742     
743     /**
744      * Enables this button
745      */
746     enable : function()
747     {
748         this.disabled = false;
749         this.el.removeClass('disabled');
750     },
751     
752     /**
753      * Disable this button
754      */
755     disable : function()
756     {
757         this.disabled = true;
758         this.el.addClass('disabled');
759     },
760      /**
761      * sets the active state on/off, 
762      * @param {Boolean} state (optional) Force a particular state
763      */
764     setActive : function(v) {
765         
766         this.el[v ? 'addClass' : 'removeClass']('active');
767     },
768      /**
769      * toggles the current active state 
770      */
771     toggleActive : function()
772     {
773        var active = this.el.hasClass('active');
774        this.setActive(!active);
775        
776         
777     },
778     setText : function(str)
779     {
780         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
781     },
782     getText : function()
783     {
784         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
785     },
786     hide: function() {
787        
788      
789         this.el.hide();   
790     },
791     show: function() {
792        
793         this.el.show();   
794     }
795     
796     
797 });
798
799  /*
800  * - LGPL
801  *
802  * column
803  * 
804  */
805
806 /**
807  * @class Roo.bootstrap.Column
808  * @extends Roo.bootstrap.Component
809  * Bootstrap Column class
810  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
811  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
812  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
813  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
814  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
815  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
816  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
817  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
818  *
819  * 
820  * @cfg {Boolean} hidden (true|false) hide the element
821  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
822  * @cfg {String} fa (ban|check|...) font awesome icon
823  * @cfg {Number} fasize (1|2|....) font awsome size
824
825  * @cfg {String} icon (info-sign|check|...) glyphicon name
826
827  * @cfg {String} html content of column.
828  * 
829  * @constructor
830  * Create a new Column
831  * @param {Object} config The config object
832  */
833
834 Roo.bootstrap.Column = function(config){
835     Roo.bootstrap.Column.superclass.constructor.call(this, config);
836 };
837
838 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
839     
840     xs: false,
841     sm: false,
842     md: false,
843     lg: false,
844     xsoff: false,
845     smoff: false,
846     mdoff: false,
847     lgoff: false,
848     html: '',
849     offset: 0,
850     alert: false,
851     fa: false,
852     icon : false,
853     hidden : false,
854     fasize : 1,
855     
856     getAutoCreate : function(){
857         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
858         
859         cfg = {
860             tag: 'div',
861             cls: 'column'
862         };
863         
864         var settings=this;
865         ['xs','sm','md','lg'].map(function(size){
866             //Roo.log( size + ':' + settings[size]);
867             
868             if (settings[size+'off'] !== false) {
869                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
870             }
871             
872             if (settings[size] === false) {
873                 return;
874             }
875             Roo.log(settings[size]);
876             if (!settings[size]) { // 0 = hidden
877                 cfg.cls += ' hidden-' + size;
878                 return;
879             }
880             cfg.cls += ' col-' + size + '-' + settings[size];
881             
882         });
883         
884         if (this.hidden) {
885             cfg.cls += ' hidden';
886         }
887         
888         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
889             cfg.cls +=' alert alert-' + this.alert;
890         }
891         
892         
893         if (this.html.length) {
894             cfg.html = this.html;
895         }
896         if (this.fa) {
897             var fasize = '';
898             if (this.fasize > 1) {
899                 fasize = ' fa-' + this.fasize + 'x';
900             }
901             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
902             
903             
904         }
905         if (this.icon) {
906             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
907         }
908         
909         return cfg;
910     }
911    
912 });
913
914  
915
916  /*
917  * - LGPL
918  *
919  * page container.
920  * 
921  */
922
923
924 /**
925  * @class Roo.bootstrap.Container
926  * @extends Roo.bootstrap.Component
927  * Bootstrap Container class
928  * @cfg {Boolean} jumbotron is it a jumbotron element
929  * @cfg {String} html content of element
930  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
931  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
932  * @cfg {String} header content of header (for panel)
933  * @cfg {String} footer content of footer (for panel)
934  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
935  * @cfg {String} tag (header|aside|section) type of HTML tag.
936  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
937  * @cfg {String} fa (ban|check|...) font awesome icon
938  * @cfg {String} icon (info-sign|check|...) glyphicon name
939  * @cfg {Boolean} hidden (true|false) hide the element
940
941  *     
942  * @constructor
943  * Create a new Container
944  * @param {Object} config The config object
945  */
946
947 Roo.bootstrap.Container = function(config){
948     Roo.bootstrap.Container.superclass.constructor.call(this, config);
949 };
950
951 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
952     
953     jumbotron : false,
954     well: '',
955     panel : '',
956     header: '',
957     footer : '',
958     sticky: '',
959     tag : false,
960     alert : false,
961     fa: false,
962     icon : false,
963   
964      
965     getChildContainer : function() {
966         
967         if(!this.el){
968             return false;
969         }
970         
971         if (this.panel.length) {
972             return this.el.select('.panel-body',true).first();
973         }
974         
975         return this.el;
976     },
977     
978     
979     getAutoCreate : function(){
980         
981         var cfg = {
982             tag : this.tag || 'div',
983             html : '',
984             cls : ''
985         };
986         if (this.jumbotron) {
987             cfg.cls = 'jumbotron';
988         }
989         
990         
991         
992         // - this is applied by the parent..
993         //if (this.cls) {
994         //    cfg.cls = this.cls + '';
995         //}
996         
997         if (this.sticky.length) {
998             
999             var bd = Roo.get(document.body);
1000             if (!bd.hasClass('bootstrap-sticky')) {
1001                 bd.addClass('bootstrap-sticky');
1002                 Roo.select('html',true).setStyle('height', '100%');
1003             }
1004              
1005             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1006         }
1007         
1008         
1009         if (this.well.length) {
1010             switch (this.well) {
1011                 case 'lg':
1012                 case 'sm':
1013                     cfg.cls +=' well well-' +this.well;
1014                     break;
1015                 default:
1016                     cfg.cls +=' well';
1017                     break;
1018             }
1019         }
1020         
1021         if (this.hidden) {
1022             cfg.cls += ' hidden';
1023         }
1024         
1025         
1026         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1027             cfg.cls +=' alert alert-' + this.alert;
1028         }
1029         
1030         var body = cfg;
1031         
1032         if (this.panel.length) {
1033             cfg.cls += ' panel panel-' + this.panel;
1034             cfg.cn = [];
1035             if (this.header.length) {
1036                 cfg.cn.push({
1037                     
1038                     cls : 'panel-heading',
1039                     cn : [{
1040                         tag: 'h3',
1041                         cls : 'panel-title',
1042                         html : this.header
1043                     }]
1044                     
1045                 });
1046             }
1047             body = false;
1048             cfg.cn.push({
1049                 cls : 'panel-body',
1050                 html : this.html
1051             });
1052             
1053             
1054             if (this.footer.length) {
1055                 cfg.cn.push({
1056                     cls : 'panel-footer',
1057                     html : this.footer
1058                     
1059                 });
1060             }
1061             
1062         }
1063         
1064         if (body) {
1065             body.html = this.html || cfg.html;
1066             // prefix with the icons..
1067             if (this.fa) {
1068                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1069             }
1070             if (this.icon) {
1071                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1072             }
1073             
1074             
1075         }
1076         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1077             cfg.cls =  'container';
1078         }
1079         
1080         return cfg;
1081     },
1082     
1083     titleEl : function()
1084     {
1085         if(!this.el || !this.panel.length || !this.header.length){
1086             return;
1087         }
1088         
1089         return this.el.select('.panel-title',true).first();
1090     },
1091     
1092     setTitle : function(v)
1093     {
1094         var titleEl = this.titleEl();
1095         
1096         if(!titleEl){
1097             return;
1098         }
1099         
1100         titleEl.dom.innerHTML = v;
1101     },
1102     
1103     getTitle : function()
1104     {
1105         
1106         var titleEl = this.titleEl();
1107         
1108         if(!titleEl){
1109             return '';
1110         }
1111         
1112         return titleEl.dom.innerHTML;
1113     }
1114    
1115 });
1116
1117  /*
1118  * - LGPL
1119  *
1120  * image
1121  * 
1122  */
1123
1124
1125 /**
1126  * @class Roo.bootstrap.Img
1127  * @extends Roo.bootstrap.Component
1128  * Bootstrap Img class
1129  * @cfg {Boolean} imgResponsive false | true
1130  * @cfg {String} border rounded | circle | thumbnail
1131  * @cfg {String} src image source
1132  * @cfg {String} alt image alternative text
1133  * @cfg {String} href a tag href
1134  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1135  * 
1136  * @constructor
1137  * Create a new Input
1138  * @param {Object} config The config object
1139  */
1140
1141 Roo.bootstrap.Img = function(config){
1142     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1143     
1144     this.addEvents({
1145         // img events
1146         /**
1147          * @event click
1148          * The img click event for the img.
1149          * @param {Roo.EventObject} e
1150          */
1151         "click" : true
1152     });
1153 };
1154
1155 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1156     
1157     imgResponsive: true,
1158     border: '',
1159     src: '',
1160     href: false,
1161     target: false,
1162
1163     getAutoCreate : function(){
1164         
1165         var cfg = {
1166             tag: 'img',
1167             cls: (this.imgResponsive) ? 'img-responsive' : '',
1168             html : null
1169         }
1170         
1171         cfg.html = this.html || cfg.html;
1172         
1173         cfg.src = this.src || cfg.src;
1174         
1175         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1176             cfg.cls += ' img-' + this.border;
1177         }
1178         
1179         if(this.alt){
1180             cfg.alt = this.alt;
1181         }
1182         
1183         if(this.href){
1184             var a = {
1185                 tag: 'a',
1186                 href: this.href,
1187                 cn: [
1188                     cfg
1189                 ]
1190             }
1191             
1192             if(this.target){
1193                 a.target = this.target;
1194             }
1195             
1196         }
1197         
1198         
1199         return (this.href) ? a : cfg;
1200     },
1201     
1202     initEvents: function() {
1203         
1204         if(!this.href){
1205             this.el.on('click', this.onClick, this);
1206         }
1207     },
1208     
1209     onClick : function(e)
1210     {
1211         Roo.log('img onclick');
1212         this.fireEvent('click', this, e);
1213     }
1214    
1215 });
1216
1217  /*
1218  * - LGPL
1219  *
1220  * image
1221  * 
1222  */
1223
1224
1225 /**
1226  * @class Roo.bootstrap.Link
1227  * @extends Roo.bootstrap.Component
1228  * Bootstrap Link Class
1229  * @cfg {String} alt image alternative text
1230  * @cfg {String} href a tag href
1231  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1232  * @cfg {String} html the content of the link.
1233  * @cfg {String} anchor name for the anchor link
1234
1235  * @cfg {Boolean} preventDefault (true | false) default false
1236
1237  * 
1238  * @constructor
1239  * Create a new Input
1240  * @param {Object} config The config object
1241  */
1242
1243 Roo.bootstrap.Link = function(config){
1244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1245     
1246     this.addEvents({
1247         // img events
1248         /**
1249          * @event click
1250          * The img click event for the img.
1251          * @param {Roo.EventObject} e
1252          */
1253         "click" : true
1254     });
1255 };
1256
1257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1258     
1259     href: false,
1260     target: false,
1261     preventDefault: false,
1262     anchor : false,
1263     alt : false,
1264
1265     getAutoCreate : function()
1266     {
1267         
1268         var cfg = {
1269             tag: 'a'
1270         };
1271         // anchor's do not require html/href...
1272         if (this.anchor === false) {
1273             cfg.html = this.html || 'html-missing';
1274             cfg.href = this.href || '#';
1275         } else {
1276             cfg.name = this.anchor;
1277             if (this.html !== false) {
1278                 cfg.html = this.html;
1279             }
1280             if (this.href !== false) {
1281                 cfg.href = this.href;
1282             }
1283         }
1284         
1285         if(this.alt !== false){
1286             cfg.alt = this.alt;
1287         }
1288         
1289         
1290         if(this.target !== false) {
1291             cfg.target = this.target;
1292         }
1293         
1294         return cfg;
1295     },
1296     
1297     initEvents: function() {
1298         
1299         if(!this.href || this.preventDefault){
1300             this.el.on('click', this.onClick, this);
1301         }
1302     },
1303     
1304     onClick : function(e)
1305     {
1306         if(this.preventDefault){
1307             e.preventDefault();
1308         }
1309         //Roo.log('img onclick');
1310         this.fireEvent('click', this, e);
1311     }
1312    
1313 });
1314
1315  /*
1316  * - LGPL
1317  *
1318  * header
1319  * 
1320  */
1321
1322 /**
1323  * @class Roo.bootstrap.Header
1324  * @extends Roo.bootstrap.Component
1325  * Bootstrap Header class
1326  * @cfg {String} html content of header
1327  * @cfg {Number} level (1|2|3|4|5|6) default 1
1328  * 
1329  * @constructor
1330  * Create a new Header
1331  * @param {Object} config The config object
1332  */
1333
1334
1335 Roo.bootstrap.Header  = function(config){
1336     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1337 };
1338
1339 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1340     
1341     //href : false,
1342     html : false,
1343     level : 1,
1344     
1345     
1346     
1347     getAutoCreate : function(){
1348         
1349         var cfg = {
1350             tag: 'h' + (1 *this.level),
1351             html: this.html || 'fill in html'
1352         } ;
1353         
1354         return cfg;
1355     }
1356    
1357 });
1358
1359  
1360
1361  /*
1362  * Based on:
1363  * Ext JS Library 1.1.1
1364  * Copyright(c) 2006-2007, Ext JS, LLC.
1365  *
1366  * Originally Released Under LGPL - original licence link has changed is not relivant.
1367  *
1368  * Fork - LGPL
1369  * <script type="text/javascript">
1370  */
1371  
1372 /**
1373  * @class Roo.bootstrap.MenuMgr
1374  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1375  * @singleton
1376  */
1377 Roo.bootstrap.MenuMgr = function(){
1378    var menus, active, groups = {}, attached = false, lastShow = new Date();
1379
1380    // private - called when first menu is created
1381    function init(){
1382        menus = {};
1383        active = new Roo.util.MixedCollection();
1384        Roo.get(document).addKeyListener(27, function(){
1385            if(active.length > 0){
1386                hideAll();
1387            }
1388        });
1389    }
1390
1391    // private
1392    function hideAll(){
1393        if(active && active.length > 0){
1394            var c = active.clone();
1395            c.each(function(m){
1396                m.hide();
1397            });
1398        }
1399    }
1400
1401    // private
1402    function onHide(m){
1403        active.remove(m);
1404        if(active.length < 1){
1405            Roo.get(document).un("mouseup", onMouseDown);
1406             
1407            attached = false;
1408        }
1409    }
1410
1411    // private
1412    function onShow(m){
1413        var last = active.last();
1414        lastShow = new Date();
1415        active.add(m);
1416        if(!attached){
1417           Roo.get(document).on("mouseup", onMouseDown);
1418            
1419            attached = true;
1420        }
1421        if(m.parentMenu){
1422           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1423           m.parentMenu.activeChild = m;
1424        }else if(last && last.isVisible()){
1425           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1426        }
1427    }
1428
1429    // private
1430    function onBeforeHide(m){
1431        if(m.activeChild){
1432            m.activeChild.hide();
1433        }
1434        if(m.autoHideTimer){
1435            clearTimeout(m.autoHideTimer);
1436            delete m.autoHideTimer;
1437        }
1438    }
1439
1440    // private
1441    function onBeforeShow(m){
1442        var pm = m.parentMenu;
1443        if(!pm && !m.allowOtherMenus){
1444            hideAll();
1445        }else if(pm && pm.activeChild && active != m){
1446            pm.activeChild.hide();
1447        }
1448    }
1449
1450    // private
1451    function onMouseDown(e){
1452         Roo.log("on MouseDown");
1453         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1454            hideAll();
1455         }
1456         
1457         
1458    }
1459
1460    // private
1461    function onBeforeCheck(mi, state){
1462        if(state){
1463            var g = groups[mi.group];
1464            for(var i = 0, l = g.length; i < l; i++){
1465                if(g[i] != mi){
1466                    g[i].setChecked(false);
1467                }
1468            }
1469        }
1470    }
1471
1472    return {
1473
1474        /**
1475         * Hides all menus that are currently visible
1476         */
1477        hideAll : function(){
1478             hideAll();  
1479        },
1480
1481        // private
1482        register : function(menu){
1483            if(!menus){
1484                init();
1485            }
1486            menus[menu.id] = menu;
1487            menu.on("beforehide", onBeforeHide);
1488            menu.on("hide", onHide);
1489            menu.on("beforeshow", onBeforeShow);
1490            menu.on("show", onShow);
1491            var g = menu.group;
1492            if(g && menu.events["checkchange"]){
1493                if(!groups[g]){
1494                    groups[g] = [];
1495                }
1496                groups[g].push(menu);
1497                menu.on("checkchange", onCheck);
1498            }
1499        },
1500
1501         /**
1502          * Returns a {@link Roo.menu.Menu} object
1503          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1504          * be used to generate and return a new Menu instance.
1505          */
1506        get : function(menu){
1507            if(typeof menu == "string"){ // menu id
1508                return menus[menu];
1509            }else if(menu.events){  // menu instance
1510                return menu;
1511            }
1512            /*else if(typeof menu.length == 'number'){ // array of menu items?
1513                return new Roo.bootstrap.Menu({items:menu});
1514            }else{ // otherwise, must be a config
1515                return new Roo.bootstrap.Menu(menu);
1516            }
1517            */
1518            return false;
1519        },
1520
1521        // private
1522        unregister : function(menu){
1523            delete menus[menu.id];
1524            menu.un("beforehide", onBeforeHide);
1525            menu.un("hide", onHide);
1526            menu.un("beforeshow", onBeforeShow);
1527            menu.un("show", onShow);
1528            var g = menu.group;
1529            if(g && menu.events["checkchange"]){
1530                groups[g].remove(menu);
1531                menu.un("checkchange", onCheck);
1532            }
1533        },
1534
1535        // private
1536        registerCheckable : function(menuItem){
1537            var g = menuItem.group;
1538            if(g){
1539                if(!groups[g]){
1540                    groups[g] = [];
1541                }
1542                groups[g].push(menuItem);
1543                menuItem.on("beforecheckchange", onBeforeCheck);
1544            }
1545        },
1546
1547        // private
1548        unregisterCheckable : function(menuItem){
1549            var g = menuItem.group;
1550            if(g){
1551                groups[g].remove(menuItem);
1552                menuItem.un("beforecheckchange", onBeforeCheck);
1553            }
1554        }
1555    };
1556 }();/*
1557  * - LGPL
1558  *
1559  * menu
1560  * 
1561  */
1562
1563 /**
1564  * @class Roo.bootstrap.Menu
1565  * @extends Roo.bootstrap.Component
1566  * Bootstrap Menu class - container for MenuItems
1567  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1568  * 
1569  * @constructor
1570  * Create a new Menu
1571  * @param {Object} config The config object
1572  */
1573
1574
1575 Roo.bootstrap.Menu = function(config){
1576     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1577     if (this.registerMenu) {
1578         Roo.bootstrap.MenuMgr.register(this);
1579     }
1580     this.addEvents({
1581         /**
1582          * @event beforeshow
1583          * Fires before this menu is displayed
1584          * @param {Roo.menu.Menu} this
1585          */
1586         beforeshow : true,
1587         /**
1588          * @event beforehide
1589          * Fires before this menu is hidden
1590          * @param {Roo.menu.Menu} this
1591          */
1592         beforehide : true,
1593         /**
1594          * @event show
1595          * Fires after this menu is displayed
1596          * @param {Roo.menu.Menu} this
1597          */
1598         show : true,
1599         /**
1600          * @event hide
1601          * Fires after this menu is hidden
1602          * @param {Roo.menu.Menu} this
1603          */
1604         hide : true,
1605         /**
1606          * @event click
1607          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1608          * @param {Roo.menu.Menu} this
1609          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1610          * @param {Roo.EventObject} e
1611          */
1612         click : true,
1613         /**
1614          * @event mouseover
1615          * Fires when the mouse is hovering over this menu
1616          * @param {Roo.menu.Menu} this
1617          * @param {Roo.EventObject} e
1618          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1619          */
1620         mouseover : true,
1621         /**
1622          * @event mouseout
1623          * Fires when the mouse exits this menu
1624          * @param {Roo.menu.Menu} this
1625          * @param {Roo.EventObject} e
1626          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1627          */
1628         mouseout : true,
1629         /**
1630          * @event itemclick
1631          * Fires when a menu item contained in this menu is clicked
1632          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1633          * @param {Roo.EventObject} e
1634          */
1635         itemclick: true
1636     });
1637     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1638 };
1639
1640 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1641     
1642    /// html : false,
1643     //align : '',
1644     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1645     type: false,
1646     /**
1647      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1648      */
1649     registerMenu : true,
1650     
1651     menuItems :false, // stores the menu items..
1652     
1653     hidden:true,
1654     
1655     parentMenu : false,
1656     
1657     getChildContainer : function() {
1658         return this.el;  
1659     },
1660     
1661     getAutoCreate : function(){
1662          
1663         //if (['right'].indexOf(this.align)!==-1) {
1664         //    cfg.cn[1].cls += ' pull-right'
1665         //}
1666         
1667         
1668         var cfg = {
1669             tag : 'ul',
1670             cls : 'dropdown-menu' ,
1671             style : 'z-index:1000'
1672             
1673         }
1674         
1675         if (this.type === 'submenu') {
1676             cfg.cls = 'submenu active';
1677         }
1678         if (this.type === 'treeview') {
1679             cfg.cls = 'treeview-menu';
1680         }
1681         
1682         return cfg;
1683     },
1684     initEvents : function() {
1685         
1686        // Roo.log("ADD event");
1687        // Roo.log(this.triggerEl.dom);
1688         this.triggerEl.on('click', this.onTriggerPress, this);
1689         this.triggerEl.addClass('dropdown-toggle');
1690         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1691
1692         this.el.on("mouseover", this.onMouseOver, this);
1693         this.el.on("mouseout", this.onMouseOut, this);
1694         
1695         
1696     },
1697     findTargetItem : function(e){
1698         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1699         if(!t){
1700             return false;
1701         }
1702         //Roo.log(t);         Roo.log(t.id);
1703         if(t && t.id){
1704             //Roo.log(this.menuitems);
1705             return this.menuitems.get(t.id);
1706             
1707             //return this.items.get(t.menuItemId);
1708         }
1709         
1710         return false;
1711     },
1712     onClick : function(e){
1713         Roo.log("menu.onClick");
1714         var t = this.findTargetItem(e);
1715         if(!t || t.isContainer){
1716             return;
1717         }
1718         Roo.log(e);
1719         /*
1720         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1721             if(t == this.activeItem && t.shouldDeactivate(e)){
1722                 this.activeItem.deactivate();
1723                 delete this.activeItem;
1724                 return;
1725             }
1726             if(t.canActivate){
1727                 this.setActiveItem(t, true);
1728             }
1729             return;
1730             
1731             
1732         }
1733         */
1734        
1735         Roo.log('pass click event');
1736         
1737         t.onClick(e);
1738         
1739         this.fireEvent("click", this, t, e);
1740         
1741         this.hide();
1742     },
1743      onMouseOver : function(e){
1744         var t  = this.findTargetItem(e);
1745         //Roo.log(t);
1746         //if(t){
1747         //    if(t.canActivate && !t.disabled){
1748         //        this.setActiveItem(t, true);
1749         //    }
1750         //}
1751         
1752         this.fireEvent("mouseover", this, e, t);
1753     },
1754     isVisible : function(){
1755         return !this.hidden;
1756     },
1757      onMouseOut : function(e){
1758         var t  = this.findTargetItem(e);
1759         
1760         //if(t ){
1761         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1762         //        this.activeItem.deactivate();
1763         //        delete this.activeItem;
1764         //    }
1765         //}
1766         this.fireEvent("mouseout", this, e, t);
1767     },
1768     
1769     
1770     /**
1771      * Displays this menu relative to another element
1772      * @param {String/HTMLElement/Roo.Element} element The element to align to
1773      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1774      * the element (defaults to this.defaultAlign)
1775      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1776      */
1777     show : function(el, pos, parentMenu){
1778         this.parentMenu = parentMenu;
1779         if(!this.el){
1780             this.render();
1781         }
1782         this.fireEvent("beforeshow", this);
1783         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1784     },
1785      /**
1786      * Displays this menu at a specific xy position
1787      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1788      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1789      */
1790     showAt : function(xy, parentMenu, /* private: */_e){
1791         this.parentMenu = parentMenu;
1792         if(!this.el){
1793             this.render();
1794         }
1795         if(_e !== false){
1796             this.fireEvent("beforeshow", this);
1797             
1798             //xy = this.el.adjustForConstraints(xy);
1799         }
1800         //this.el.setXY(xy);
1801         //this.el.show();
1802         this.hideMenuItems();
1803         this.hidden = false;
1804         this.triggerEl.addClass('open');
1805         this.focus();
1806         this.fireEvent("show", this);
1807     },
1808     
1809     focus : function(){
1810         return;
1811         if(!this.hidden){
1812             this.doFocus.defer(50, this);
1813         }
1814     },
1815
1816     doFocus : function(){
1817         if(!this.hidden){
1818             this.focusEl.focus();
1819         }
1820     },
1821
1822     /**
1823      * Hides this menu and optionally all parent menus
1824      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1825      */
1826     hide : function(deep){
1827         
1828         this.hideMenuItems();
1829         if(this.el && this.isVisible()){
1830             this.fireEvent("beforehide", this);
1831             if(this.activeItem){
1832                 this.activeItem.deactivate();
1833                 this.activeItem = null;
1834             }
1835             this.triggerEl.removeClass('open');;
1836             this.hidden = true;
1837             this.fireEvent("hide", this);
1838         }
1839         if(deep === true && this.parentMenu){
1840             this.parentMenu.hide(true);
1841         }
1842     },
1843     
1844     onTriggerPress  : function(e)
1845     {
1846         
1847         Roo.log('trigger press');
1848         //Roo.log(e.getTarget());
1849        // Roo.log(this.triggerEl.dom);
1850         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1851             return;
1852         }
1853         if (this.isVisible()) {
1854             Roo.log('hide');
1855             this.hide();
1856         } else {
1857             this.show(this.triggerEl, false, false);
1858         }
1859         
1860         
1861     },
1862     
1863          
1864        
1865     
1866     hideMenuItems : function()
1867     {
1868         //$(backdrop).remove()
1869         Roo.select('.open',true).each(function(aa) {
1870             
1871             aa.removeClass('open');
1872           //var parent = getParent($(this))
1873           //var relatedTarget = { relatedTarget: this }
1874           
1875            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1876           //if (e.isDefaultPrevented()) return
1877            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1878         })
1879     },
1880     addxtypeChild : function (tree, cntr) {
1881         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1882           
1883         this.menuitems.add(comp);
1884         return comp;
1885
1886     },
1887     getEl : function()
1888     {
1889         Roo.log(this.el);
1890         return this.el;
1891     }
1892 });
1893
1894  
1895  /*
1896  * - LGPL
1897  *
1898  * menu item
1899  * 
1900  */
1901
1902
1903 /**
1904  * @class Roo.bootstrap.MenuItem
1905  * @extends Roo.bootstrap.Component
1906  * Bootstrap MenuItem class
1907  * @cfg {String} html the menu label
1908  * @cfg {String} href the link
1909  * @cfg {Boolean} preventDefault (true | false) default true
1910  * @cfg {Boolean} isContainer (true | false) default false
1911  * 
1912  * 
1913  * @constructor
1914  * Create a new MenuItem
1915  * @param {Object} config The config object
1916  */
1917
1918
1919 Roo.bootstrap.MenuItem = function(config){
1920     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1921     this.addEvents({
1922         // raw events
1923         /**
1924          * @event click
1925          * The raw click event for the entire grid.
1926          * @param {Roo.EventObject} e
1927          */
1928         "click" : true
1929     });
1930 };
1931
1932 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1933     
1934     href : false,
1935     html : false,
1936     preventDefault: true,
1937     isContainer : false,
1938     
1939     getAutoCreate : function(){
1940         
1941         if(this.isContainer){
1942             return {
1943                 tag: 'li',
1944                 cls: 'dropdown-menu-item'
1945             };
1946         }
1947         
1948         var cfg= {
1949             tag: 'li',
1950             cls: 'dropdown-menu-item',
1951             cn: [
1952                     {
1953                         tag : 'a',
1954                         href : '#',
1955                         html : 'Link'
1956                     }
1957                 ]
1958         };
1959         if (this.parent().type == 'treeview') {
1960             cfg.cls = 'treeview-menu';
1961         }
1962         
1963         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1964         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1965         return cfg;
1966     },
1967     
1968     initEvents: function() {
1969         
1970         //this.el.select('a').on('click', this.onClick, this);
1971         
1972     },
1973     onClick : function(e)
1974     {
1975         Roo.log('item on click ');
1976         //if(this.preventDefault){
1977         //    e.preventDefault();
1978         //}
1979         //this.parent().hideMenuItems();
1980         
1981         this.fireEvent('click', this, e);
1982     },
1983     getEl : function()
1984     {
1985         return this.el;
1986     }
1987 });
1988
1989  
1990
1991  /*
1992  * - LGPL
1993  *
1994  * menu separator
1995  * 
1996  */
1997
1998
1999 /**
2000  * @class Roo.bootstrap.MenuSeparator
2001  * @extends Roo.bootstrap.Component
2002  * Bootstrap MenuSeparator class
2003  * 
2004  * @constructor
2005  * Create a new MenuItem
2006  * @param {Object} config The config object
2007  */
2008
2009
2010 Roo.bootstrap.MenuSeparator = function(config){
2011     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2012 };
2013
2014 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2015     
2016     getAutoCreate : function(){
2017         var cfg = {
2018             cls: 'divider',
2019             tag : 'li'
2020         };
2021         
2022         return cfg;
2023     }
2024    
2025 });
2026
2027  
2028
2029  
2030 /*
2031 <div class="modal fade">
2032   <div class="modal-dialog">
2033     <div class="modal-content">
2034       <div class="modal-header">
2035         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2036         <h4 class="modal-title">Modal title</h4>
2037       </div>
2038       <div class="modal-body">
2039         <p>One fine body&hellip;</p>
2040       </div>
2041       <div class="modal-footer">
2042         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2043         <button type="button" class="btn btn-primary">Save changes</button>
2044       </div>
2045     </div><!-- /.modal-content -->
2046   </div><!-- /.modal-dialog -->
2047 </div><!-- /.modal -->
2048 */
2049 /*
2050  * - LGPL
2051  *
2052  * page contgainer.
2053  * 
2054  */
2055
2056 /**
2057  * @class Roo.bootstrap.Modal
2058  * @extends Roo.bootstrap.Component
2059  * Bootstrap Modal class
2060  * @cfg {String} title Title of dialog
2061  * @cfg {Boolean} specificTitle (true|false) default false
2062  * @cfg {Array} buttons Array of buttons or standard button set..
2063  * @cfg {String} buttonPosition (left|right|center) default right
2064  * @cfg {Boolean} animate (true | false) default true
2065  * 
2066  * @constructor
2067  * Create a new Modal Dialog
2068  * @param {Object} config The config object
2069  */
2070
2071 Roo.bootstrap.Modal = function(config){
2072     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2073     this.addEvents({
2074         // raw events
2075         /**
2076          * @event btnclick
2077          * The raw btnclick event for the button
2078          * @param {Roo.EventObject} e
2079          */
2080         "btnclick" : true
2081     });
2082     this.buttons = this.buttons || [];
2083 };
2084
2085 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2086     
2087     title : 'test dialog',
2088    
2089     buttons : false,
2090     
2091     // set on load...
2092     body:  false,
2093     
2094     specificTitle: false,
2095     
2096     buttonPosition: 'right',
2097     
2098     animate : true,
2099     
2100     onRender : function(ct, position)
2101     {
2102         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2103      
2104         if(!this.el){
2105             var cfg = Roo.apply({},  this.getAutoCreate());
2106             cfg.id = Roo.id();
2107             //if(!cfg.name){
2108             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2109             //}
2110             //if (!cfg.name.length) {
2111             //    delete cfg.name;
2112            // }
2113             if (this.cls) {
2114                 cfg.cls += ' ' + this.cls;
2115             }
2116             if (this.style) {
2117                 cfg.style = this.style;
2118             }
2119             this.el = Roo.get(document.body).createChild(cfg, position);
2120         }
2121         //var type = this.el.dom.type;
2122         
2123         if(this.tabIndex !== undefined){
2124             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2125         }
2126         
2127         
2128         
2129         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2130         this.maskEl.enableDisplayMode("block");
2131         this.maskEl.hide();
2132         //this.el.addClass("x-dlg-modal");
2133     
2134         if (this.buttons.length) {
2135             Roo.each(this.buttons, function(bb) {
2136                 b = Roo.apply({}, bb);
2137                 b.xns = b.xns || Roo.bootstrap;
2138                 b.xtype = b.xtype || 'Button';
2139                 if (typeof(b.listeners) == 'undefined') {
2140                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2141                 }
2142                 
2143                 var btn = Roo.factory(b);
2144                 
2145                 btn.onRender(this.el.select('.modal-footer div').first());
2146                 
2147             },this);
2148         }
2149         // render the children.
2150         var nitems = [];
2151         
2152         if(typeof(this.items) != 'undefined'){
2153             var items = this.items;
2154             delete this.items;
2155
2156             for(var i =0;i < items.length;i++) {
2157                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2158             }
2159         }
2160         
2161         this.items = nitems;
2162         
2163         this.body = this.el.select('.modal-body',true).first();
2164         this.close = this.el.select('.modal-header .close', true).first();
2165         this.footer = this.el.select('.modal-footer',true).first();
2166         this.initEvents();
2167         //this.el.addClass([this.fieldClass, this.cls]);
2168         
2169     },
2170     getAutoCreate : function(){
2171         
2172         
2173         var bdy = {
2174                 cls : 'modal-body',
2175                 html : this.html || ''
2176         };
2177         
2178         var title = {
2179             tag: 'h4',
2180             cls : 'modal-title',
2181             html : this.title
2182         };
2183         
2184         if(this.specificTitle){
2185             title = this.title;
2186         };
2187         
2188         var modal = {
2189             cls: "modal",
2190             style : 'display: none',
2191             cn : [
2192                 {
2193                     cls: "modal-dialog",
2194                     cn : [
2195                         {
2196                             cls : "modal-content",
2197                             cn : [
2198                                 {
2199                                     cls : 'modal-header',
2200                                     cn : [
2201                                         {
2202                                             tag: 'button',
2203                                             cls : 'close',
2204                                             html : '&times'
2205                                         },
2206                                         title
2207                                     ]
2208                                 },
2209                                 bdy,
2210                                 {
2211                                     cls : 'modal-footer',
2212                                     cn : [
2213                                         {
2214                                             tag: 'div',
2215                                             cls: 'btn-' + this.buttonPosition
2216                                         }
2217                                     ]
2218                                     
2219                                 }
2220                                 
2221                                 
2222                             ]
2223                             
2224                         }
2225                     ]
2226                         
2227                 }
2228             ]
2229         };
2230         
2231         if(this.animate){
2232             modal.cls += ' fade';
2233         }
2234         
2235         return modal;
2236           
2237     },
2238     getChildContainer : function() {
2239          
2240          return this.el.select('.modal-body',true).first();
2241         
2242     },
2243     getButtonContainer : function() {
2244          return this.el.select('.modal-footer div',true).first();
2245         
2246     },
2247     initEvents : function()
2248     {
2249         this.el.select('.modal-header .close').on('click', this.hide, this);
2250 //        
2251 //        this.addxtype(this);
2252     },
2253     show : function() {
2254         
2255         if (!this.rendered) {
2256             this.render();
2257         }
2258         
2259         this.el.setStyle('display', 'block');
2260         
2261         if(this.animate){
2262             var _this = this;
2263             (function(){ _this.el.addClass('in'); }).defer(50);
2264         }else{
2265             this.el.addClass('in');
2266         }
2267         
2268         Roo.get(document.body).addClass("x-body-masked");
2269         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2270         this.maskEl.show();
2271         this.el.setStyle('zIndex', '10001');
2272         this.fireEvent('show', this);
2273         
2274         
2275     },
2276     hide : function()
2277     {
2278         this.maskEl.hide();
2279         Roo.get(document.body).removeClass("x-body-masked");
2280         this.el.removeClass('in');
2281         
2282         if(this.animate){
2283             var _this = this;
2284             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2285         }else{
2286             this.el.setStyle('display', 'none');
2287         }
2288         
2289         this.fireEvent('hide', this);
2290     },
2291     
2292     addButton : function(str, cb)
2293     {
2294          
2295         
2296         var b = Roo.apply({}, { html : str } );
2297         b.xns = b.xns || Roo.bootstrap;
2298         b.xtype = b.xtype || 'Button';
2299         if (typeof(b.listeners) == 'undefined') {
2300             b.listeners = { click : cb.createDelegate(this)  };
2301         }
2302         
2303         var btn = Roo.factory(b);
2304            
2305         btn.onRender(this.el.select('.modal-footer div').first());
2306         
2307         return btn;   
2308        
2309     },
2310     
2311     setDefaultButton : function(btn)
2312     {
2313         //this.el.select('.modal-footer').()
2314     },
2315     resizeTo: function(w,h)
2316     {
2317         // skip..
2318     },
2319     setContentSize  : function(w, h)
2320     {
2321         
2322     },
2323     onButtonClick: function(btn,e)
2324     {
2325         //Roo.log([a,b,c]);
2326         this.fireEvent('btnclick', btn.name, e);
2327     },
2328     setTitle: function(str) {
2329         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2330         
2331     }
2332 });
2333
2334
2335 Roo.apply(Roo.bootstrap.Modal,  {
2336     /**
2337          * Button config that displays a single OK button
2338          * @type Object
2339          */
2340         OK :  [{
2341             name : 'ok',
2342             weight : 'primary',
2343             html : 'OK'
2344         }], 
2345         /**
2346          * Button config that displays Yes and No buttons
2347          * @type Object
2348          */
2349         YESNO : [
2350             {
2351                 name  : 'no',
2352                 html : 'No'
2353             },
2354             {
2355                 name  :'yes',
2356                 weight : 'primary',
2357                 html : 'Yes'
2358             }
2359         ],
2360         
2361         /**
2362          * Button config that displays OK and Cancel buttons
2363          * @type Object
2364          */
2365         OKCANCEL : [
2366             {
2367                name : 'cancel',
2368                 html : 'Cancel'
2369             },
2370             {
2371                 name : 'ok',
2372                 weight : 'primary',
2373                 html : 'OK'
2374             }
2375         ],
2376         /**
2377          * Button config that displays Yes, No and Cancel buttons
2378          * @type Object
2379          */
2380         YESNOCANCEL : [
2381             {
2382                 name : 'yes',
2383                 weight : 'primary',
2384                 html : 'Yes'
2385             },
2386             {
2387                 name : 'no',
2388                 html : 'No'
2389             },
2390             {
2391                 name : 'cancel',
2392                 html : 'Cancel'
2393             }
2394         ]
2395 });
2396  /*
2397  * - LGPL
2398  *
2399  * messagebox - can be used as a replace
2400  * 
2401  */
2402 /**
2403  * @class Roo.MessageBox
2404  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2405  * Example usage:
2406  *<pre><code>
2407 // Basic alert:
2408 Roo.Msg.alert('Status', 'Changes saved successfully.');
2409
2410 // Prompt for user data:
2411 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2412     if (btn == 'ok'){
2413         // process text value...
2414     }
2415 });
2416
2417 // Show a dialog using config options:
2418 Roo.Msg.show({
2419    title:'Save Changes?',
2420    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2421    buttons: Roo.Msg.YESNOCANCEL,
2422    fn: processResult,
2423    animEl: 'elId'
2424 });
2425 </code></pre>
2426  * @singleton
2427  */
2428 Roo.bootstrap.MessageBox = function(){
2429     var dlg, opt, mask, waitTimer;
2430     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2431     var buttons, activeTextEl, bwidth;
2432
2433     
2434     // private
2435     var handleButton = function(button){
2436         dlg.hide();
2437         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2438     };
2439
2440     // private
2441     var handleHide = function(){
2442         if(opt && opt.cls){
2443             dlg.el.removeClass(opt.cls);
2444         }
2445         //if(waitTimer){
2446         //    Roo.TaskMgr.stop(waitTimer);
2447         //    waitTimer = null;
2448         //}
2449     };
2450
2451     // private
2452     var updateButtons = function(b){
2453         var width = 0;
2454         if(!b){
2455             buttons["ok"].hide();
2456             buttons["cancel"].hide();
2457             buttons["yes"].hide();
2458             buttons["no"].hide();
2459             //dlg.footer.dom.style.display = 'none';
2460             return width;
2461         }
2462         dlg.footer.dom.style.display = '';
2463         for(var k in buttons){
2464             if(typeof buttons[k] != "function"){
2465                 if(b[k]){
2466                     buttons[k].show();
2467                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2468                     width += buttons[k].el.getWidth()+15;
2469                 }else{
2470                     buttons[k].hide();
2471                 }
2472             }
2473         }
2474         return width;
2475     };
2476
2477     // private
2478     var handleEsc = function(d, k, e){
2479         if(opt && opt.closable !== false){
2480             dlg.hide();
2481         }
2482         if(e){
2483             e.stopEvent();
2484         }
2485     };
2486
2487     return {
2488         /**
2489          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2490          * @return {Roo.BasicDialog} The BasicDialog element
2491          */
2492         getDialog : function(){
2493            if(!dlg){
2494                 dlg = new Roo.bootstrap.Modal( {
2495                     //draggable: true,
2496                     //resizable:false,
2497                     //constraintoviewport:false,
2498                     //fixedcenter:true,
2499                     //collapsible : false,
2500                     //shim:true,
2501                     //modal: true,
2502                   //  width:400,
2503                   //  height:100,
2504                     //buttonAlign:"center",
2505                     closeClick : function(){
2506                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2507                             handleButton("no");
2508                         }else{
2509                             handleButton("cancel");
2510                         }
2511                     }
2512                 });
2513                 dlg.render();
2514                 dlg.on("hide", handleHide);
2515                 mask = dlg.mask;
2516                 //dlg.addKeyListener(27, handleEsc);
2517                 buttons = {};
2518                 this.buttons = buttons;
2519                 var bt = this.buttonText;
2520                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2521                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2522                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2523                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2524                 Roo.log(buttons)
2525                 bodyEl = dlg.body.createChild({
2526
2527                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2528                         '<textarea class="roo-mb-textarea"></textarea>' +
2529                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2530                 });
2531                 msgEl = bodyEl.dom.firstChild;
2532                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2533                 textboxEl.enableDisplayMode();
2534                 textboxEl.addKeyListener([10,13], function(){
2535                     if(dlg.isVisible() && opt && opt.buttons){
2536                         if(opt.buttons.ok){
2537                             handleButton("ok");
2538                         }else if(opt.buttons.yes){
2539                             handleButton("yes");
2540                         }
2541                     }
2542                 });
2543                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2544                 textareaEl.enableDisplayMode();
2545                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2546                 progressEl.enableDisplayMode();
2547                 var pf = progressEl.dom.firstChild;
2548                 if (pf) {
2549                     pp = Roo.get(pf.firstChild);
2550                     pp.setHeight(pf.offsetHeight);
2551                 }
2552                 
2553             }
2554             return dlg;
2555         },
2556
2557         /**
2558          * Updates the message box body text
2559          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2560          * the XHTML-compliant non-breaking space character '&amp;#160;')
2561          * @return {Roo.MessageBox} This message box
2562          */
2563         updateText : function(text){
2564             if(!dlg.isVisible() && !opt.width){
2565                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2566             }
2567             msgEl.innerHTML = text || '&#160;';
2568       
2569             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2570             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2571             var w = Math.max(
2572                     Math.min(opt.width || cw , this.maxWidth), 
2573                     Math.max(opt.minWidth || this.minWidth, bwidth)
2574             );
2575             if(opt.prompt){
2576                 activeTextEl.setWidth(w);
2577             }
2578             if(dlg.isVisible()){
2579                 dlg.fixedcenter = false;
2580             }
2581             // to big, make it scroll. = But as usual stupid IE does not support
2582             // !important..
2583             
2584             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2585                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2586                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2587             } else {
2588                 bodyEl.dom.style.height = '';
2589                 bodyEl.dom.style.overflowY = '';
2590             }
2591             if (cw > w) {
2592                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2593             } else {
2594                 bodyEl.dom.style.overflowX = '';
2595             }
2596             
2597             dlg.setContentSize(w, bodyEl.getHeight());
2598             if(dlg.isVisible()){
2599                 dlg.fixedcenter = true;
2600             }
2601             return this;
2602         },
2603
2604         /**
2605          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2606          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2607          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2608          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2609          * @return {Roo.MessageBox} This message box
2610          */
2611         updateProgress : function(value, text){
2612             if(text){
2613                 this.updateText(text);
2614             }
2615             if (pp) { // weird bug on my firefox - for some reason this is not defined
2616                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2617             }
2618             return this;
2619         },        
2620
2621         /**
2622          * Returns true if the message box is currently displayed
2623          * @return {Boolean} True if the message box is visible, else false
2624          */
2625         isVisible : function(){
2626             return dlg && dlg.isVisible();  
2627         },
2628
2629         /**
2630          * Hides the message box if it is displayed
2631          */
2632         hide : function(){
2633             if(this.isVisible()){
2634                 dlg.hide();
2635             }  
2636         },
2637
2638         /**
2639          * Displays a new message box, or reinitializes an existing message box, based on the config options
2640          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2641          * The following config object properties are supported:
2642          * <pre>
2643 Property    Type             Description
2644 ----------  ---------------  ------------------------------------------------------------------------------------
2645 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2646                                    closes (defaults to undefined)
2647 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2648                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2649 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2650                                    progress and wait dialogs will ignore this property and always hide the
2651                                    close button as they can only be closed programmatically.
2652 cls               String           A custom CSS class to apply to the message box element
2653 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2654                                    displayed (defaults to 75)
2655 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2656                                    function will be btn (the name of the button that was clicked, if applicable,
2657                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2658                                    Progress and wait dialogs will ignore this option since they do not respond to
2659                                    user actions and can only be closed programmatically, so any required function
2660                                    should be called by the same code after it closes the dialog.
2661 icon              String           A CSS class that provides a background image to be used as an icon for
2662                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2663 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2664 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2665 modal             Boolean          False to allow user interaction with the page while the message box is
2666                                    displayed (defaults to true)
2667 msg               String           A string that will replace the existing message box body text (defaults
2668                                    to the XHTML-compliant non-breaking space character '&#160;')
2669 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2670 progress          Boolean          True to display a progress bar (defaults to false)
2671 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2672 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2673 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2674 title             String           The title text
2675 value             String           The string value to set into the active textbox element if displayed
2676 wait              Boolean          True to display a progress bar (defaults to false)
2677 width             Number           The width of the dialog in pixels
2678 </pre>
2679          *
2680          * Example usage:
2681          * <pre><code>
2682 Roo.Msg.show({
2683    title: 'Address',
2684    msg: 'Please enter your address:',
2685    width: 300,
2686    buttons: Roo.MessageBox.OKCANCEL,
2687    multiline: true,
2688    fn: saveAddress,
2689    animEl: 'addAddressBtn'
2690 });
2691 </code></pre>
2692          * @param {Object} config Configuration options
2693          * @return {Roo.MessageBox} This message box
2694          */
2695         show : function(options)
2696         {
2697             
2698             // this causes nightmares if you show one dialog after another
2699             // especially on callbacks..
2700              
2701             if(this.isVisible()){
2702                 
2703                 this.hide();
2704                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2705                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2706                 Roo.log("New Dialog Message:" +  options.msg )
2707                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2708                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2709                 
2710             }
2711             var d = this.getDialog();
2712             opt = options;
2713             d.setTitle(opt.title || "&#160;");
2714             d.close.setDisplayed(opt.closable !== false);
2715             activeTextEl = textboxEl;
2716             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2717             if(opt.prompt){
2718                 if(opt.multiline){
2719                     textboxEl.hide();
2720                     textareaEl.show();
2721                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2722                         opt.multiline : this.defaultTextHeight);
2723                     activeTextEl = textareaEl;
2724                 }else{
2725                     textboxEl.show();
2726                     textareaEl.hide();
2727                 }
2728             }else{
2729                 textboxEl.hide();
2730                 textareaEl.hide();
2731             }
2732             progressEl.setDisplayed(opt.progress === true);
2733             this.updateProgress(0);
2734             activeTextEl.dom.value = opt.value || "";
2735             if(opt.prompt){
2736                 dlg.setDefaultButton(activeTextEl);
2737             }else{
2738                 var bs = opt.buttons;
2739                 var db = null;
2740                 if(bs && bs.ok){
2741                     db = buttons["ok"];
2742                 }else if(bs && bs.yes){
2743                     db = buttons["yes"];
2744                 }
2745                 dlg.setDefaultButton(db);
2746             }
2747             bwidth = updateButtons(opt.buttons);
2748             this.updateText(opt.msg);
2749             if(opt.cls){
2750                 d.el.addClass(opt.cls);
2751             }
2752             d.proxyDrag = opt.proxyDrag === true;
2753             d.modal = opt.modal !== false;
2754             d.mask = opt.modal !== false ? mask : false;
2755             if(!d.isVisible()){
2756                 // force it to the end of the z-index stack so it gets a cursor in FF
2757                 document.body.appendChild(dlg.el.dom);
2758                 d.animateTarget = null;
2759                 d.show(options.animEl);
2760             }
2761             return this;
2762         },
2763
2764         /**
2765          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2766          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2767          * and closing the message box when the process is complete.
2768          * @param {String} title The title bar text
2769          * @param {String} msg The message box body text
2770          * @return {Roo.MessageBox} This message box
2771          */
2772         progress : function(title, msg){
2773             this.show({
2774                 title : title,
2775                 msg : msg,
2776                 buttons: false,
2777                 progress:true,
2778                 closable:false,
2779                 minWidth: this.minProgressWidth,
2780                 modal : true
2781             });
2782             return this;
2783         },
2784
2785         /**
2786          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2787          * If a callback function is passed it will be called after the user clicks the button, and the
2788          * id of the button that was clicked will be passed as the only parameter to the callback
2789          * (could also be the top-right close button).
2790          * @param {String} title The title bar text
2791          * @param {String} msg The message box body text
2792          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2793          * @param {Object} scope (optional) The scope of the callback function
2794          * @return {Roo.MessageBox} This message box
2795          */
2796         alert : function(title, msg, fn, scope){
2797             this.show({
2798                 title : title,
2799                 msg : msg,
2800                 buttons: this.OK,
2801                 fn: fn,
2802                 scope : scope,
2803                 modal : true
2804             });
2805             return this;
2806         },
2807
2808         /**
2809          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2810          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2811          * You are responsible for closing the message box when the process is complete.
2812          * @param {String} msg The message box body text
2813          * @param {String} title (optional) The title bar text
2814          * @return {Roo.MessageBox} This message box
2815          */
2816         wait : function(msg, title){
2817             this.show({
2818                 title : title,
2819                 msg : msg,
2820                 buttons: false,
2821                 closable:false,
2822                 progress:true,
2823                 modal:true,
2824                 width:300,
2825                 wait:true
2826             });
2827             waitTimer = Roo.TaskMgr.start({
2828                 run: function(i){
2829                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2830                 },
2831                 interval: 1000
2832             });
2833             return this;
2834         },
2835
2836         /**
2837          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2838          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2839          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2840          * @param {String} title The title bar text
2841          * @param {String} msg The message box body text
2842          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2843          * @param {Object} scope (optional) The scope of the callback function
2844          * @return {Roo.MessageBox} This message box
2845          */
2846         confirm : function(title, msg, fn, scope){
2847             this.show({
2848                 title : title,
2849                 msg : msg,
2850                 buttons: this.YESNO,
2851                 fn: fn,
2852                 scope : scope,
2853                 modal : true
2854             });
2855             return this;
2856         },
2857
2858         /**
2859          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2860          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2861          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2862          * (could also be the top-right close button) and the text that was entered will be passed as the two
2863          * parameters to the callback.
2864          * @param {String} title The title bar text
2865          * @param {String} msg The message box body text
2866          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2867          * @param {Object} scope (optional) The scope of the callback function
2868          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2869          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2870          * @return {Roo.MessageBox} This message box
2871          */
2872         prompt : function(title, msg, fn, scope, multiline){
2873             this.show({
2874                 title : title,
2875                 msg : msg,
2876                 buttons: this.OKCANCEL,
2877                 fn: fn,
2878                 minWidth:250,
2879                 scope : scope,
2880                 prompt:true,
2881                 multiline: multiline,
2882                 modal : true
2883             });
2884             return this;
2885         },
2886
2887         /**
2888          * Button config that displays a single OK button
2889          * @type Object
2890          */
2891         OK : {ok:true},
2892         /**
2893          * Button config that displays Yes and No buttons
2894          * @type Object
2895          */
2896         YESNO : {yes:true, no:true},
2897         /**
2898          * Button config that displays OK and Cancel buttons
2899          * @type Object
2900          */
2901         OKCANCEL : {ok:true, cancel:true},
2902         /**
2903          * Button config that displays Yes, No and Cancel buttons
2904          * @type Object
2905          */
2906         YESNOCANCEL : {yes:true, no:true, cancel:true},
2907
2908         /**
2909          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2910          * @type Number
2911          */
2912         defaultTextHeight : 75,
2913         /**
2914          * The maximum width in pixels of the message box (defaults to 600)
2915          * @type Number
2916          */
2917         maxWidth : 600,
2918         /**
2919          * The minimum width in pixels of the message box (defaults to 100)
2920          * @type Number
2921          */
2922         minWidth : 100,
2923         /**
2924          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2925          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2926          * @type Number
2927          */
2928         minProgressWidth : 250,
2929         /**
2930          * An object containing the default button text strings that can be overriden for localized language support.
2931          * Supported properties are: ok, cancel, yes and no.
2932          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2933          * @type Object
2934          */
2935         buttonText : {
2936             ok : "OK",
2937             cancel : "Cancel",
2938             yes : "Yes",
2939             no : "No"
2940         }
2941     };
2942 }();
2943
2944 /**
2945  * Shorthand for {@link Roo.MessageBox}
2946  */
2947 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2948 Roo.Msg = Roo.Msg || Roo.MessageBox;
2949 /*
2950  * - LGPL
2951  *
2952  * navbar
2953  * 
2954  */
2955
2956 /**
2957  * @class Roo.bootstrap.Navbar
2958  * @extends Roo.bootstrap.Component
2959  * Bootstrap Navbar class
2960
2961  * @constructor
2962  * Create a new Navbar
2963  * @param {Object} config The config object
2964  */
2965
2966
2967 Roo.bootstrap.Navbar = function(config){
2968     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2969     
2970 };
2971
2972 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2973     
2974     
2975    
2976     // private
2977     navItems : false,
2978     loadMask : false,
2979     
2980     
2981     getAutoCreate : function(){
2982         
2983         
2984         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2985         
2986     },
2987     
2988     initEvents :function ()
2989     {
2990         //Roo.log(this.el.select('.navbar-toggle',true));
2991         this.el.select('.navbar-toggle',true).on('click', function() {
2992            // Roo.log('click');
2993             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2994         }, this);
2995         
2996         var mark = {
2997             tag: "div",
2998             cls:"x-dlg-mask"
2999         }
3000         
3001         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3002         
3003         var size = this.el.getSize();
3004         this.maskEl.setSize(size.width, size.height);
3005         this.maskEl.enableDisplayMode("block");
3006         this.maskEl.hide();
3007         
3008         if(this.loadMask){
3009             this.maskEl.show();
3010         }
3011     },
3012     
3013     
3014     getChildContainer : function()
3015     {
3016         if (this.el.select('.collapse').getCount()) {
3017             return this.el.select('.collapse',true).first();
3018         }
3019         
3020         return this.el;
3021     },
3022     
3023     mask : function()
3024     {
3025         this.maskEl.show();
3026     },
3027     
3028     unmask : function()
3029     {
3030         this.maskEl.hide();
3031     } 
3032     
3033     
3034     
3035     
3036 });
3037
3038
3039
3040  
3041
3042  /*
3043  * - LGPL
3044  *
3045  * navbar
3046  * 
3047  */
3048
3049 /**
3050  * @class Roo.bootstrap.NavSimplebar
3051  * @extends Roo.bootstrap.Navbar
3052  * Bootstrap Sidebar class
3053  *
3054  * @cfg {Boolean} inverse is inverted color
3055  * 
3056  * @cfg {String} type (nav | pills | tabs)
3057  * @cfg {Boolean} arrangement stacked | justified
3058  * @cfg {String} align (left | right) alignment
3059  * 
3060  * @cfg {Boolean} main (true|false) main nav bar? default false
3061  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3062  * 
3063  * @cfg {String} tag (header|footer|nav|div) default is nav 
3064
3065  * 
3066  * 
3067  * 
3068  * @constructor
3069  * Create a new Sidebar
3070  * @param {Object} config The config object
3071  */
3072
3073
3074 Roo.bootstrap.NavSimplebar = function(config){
3075     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3076 };
3077
3078 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3079     
3080     inverse: false,
3081     
3082     type: false,
3083     arrangement: '',
3084     align : false,
3085     
3086     
3087     
3088     main : false,
3089     
3090     
3091     tag : false,
3092     
3093     
3094     getAutoCreate : function(){
3095         
3096         
3097         var cfg = {
3098             tag : this.tag || 'div',
3099             cls : 'navbar'
3100         };
3101           
3102         
3103         cfg.cn = [
3104             {
3105                 cls: 'nav',
3106                 tag : 'ul'
3107             }
3108         ];
3109         
3110          
3111         this.type = this.type || 'nav';
3112         if (['tabs','pills'].indexOf(this.type)!==-1) {
3113             cfg.cn[0].cls += ' nav-' + this.type
3114         
3115         
3116         } else {
3117             if (this.type!=='nav') {
3118                 Roo.log('nav type must be nav/tabs/pills')
3119             }
3120             cfg.cn[0].cls += ' navbar-nav'
3121         }
3122         
3123         
3124         
3125         
3126         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3127             cfg.cn[0].cls += ' nav-' + this.arrangement;
3128         }
3129         
3130         
3131         if (this.align === 'right') {
3132             cfg.cn[0].cls += ' navbar-right';
3133         }
3134         
3135         if (this.inverse) {
3136             cfg.cls += ' navbar-inverse';
3137             
3138         }
3139         
3140         
3141         return cfg;
3142     
3143         
3144     }
3145     
3146     
3147     
3148 });
3149
3150
3151
3152  
3153
3154  
3155        /*
3156  * - LGPL
3157  *
3158  * navbar
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavHeaderbar
3164  * @extends Roo.bootstrap.NavSimplebar
3165  * Bootstrap Sidebar class
3166  *
3167  * @cfg {String} brand what is brand
3168  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3169  * @cfg {String} brand_href href of the brand
3170  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3171  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3172  * 
3173  * @constructor
3174  * Create a new Sidebar
3175  * @param {Object} config The config object
3176  */
3177
3178
3179 Roo.bootstrap.NavHeaderbar = function(config){
3180     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3181 };
3182
3183 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3184     
3185     position: '',
3186     brand: '',
3187     brand_href: false,
3188     srButton : true,
3189     autohide : false,
3190     
3191     getAutoCreate : function(){
3192         
3193         var   cfg = {
3194             tag: this.nav || 'nav',
3195             cls: 'navbar',
3196             role: 'navigation',
3197             cn: []
3198         };
3199         
3200         if(this.srButton){
3201             cfg.cn.push({
3202                 tag: 'div',
3203                 cls: 'navbar-header',
3204                 cn: [
3205                     {
3206                         tag: 'button',
3207                         type: 'button',
3208                         cls: 'navbar-toggle',
3209                         'data-toggle': 'collapse',
3210                         cn: [
3211                             {
3212                                 tag: 'span',
3213                                 cls: 'sr-only',
3214                                 html: 'Toggle navigation'
3215                             },
3216                             {
3217                                 tag: 'span',
3218                                 cls: 'icon-bar'
3219                             },
3220                             {
3221                                 tag: 'span',
3222                                 cls: 'icon-bar'
3223                             },
3224                             {
3225                                 tag: 'span',
3226                                 cls: 'icon-bar'
3227                             }
3228                         ]
3229                     }
3230                 ]
3231             });
3232         }
3233         
3234         cfg.cn.push({
3235             tag: 'div',
3236             cls: 'collapse navbar-collapse',
3237             cn : []
3238         });
3239         
3240         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3241         
3242         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3243             cfg.cls += ' navbar-' + this.position;
3244             
3245             // tag can override this..
3246             
3247             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3248         }
3249         
3250         if (this.brand !== '') {
3251             cfg.cn[0].cn.push({
3252                 tag: 'a',
3253                 href: this.brand_href ? this.brand_href : '#',
3254                 cls: 'navbar-brand',
3255                 cn: [
3256                 this.brand
3257                 ]
3258             });
3259         }
3260         
3261         if(this.main){
3262             cfg.cls += ' main-nav';
3263         }
3264         
3265         
3266         return cfg;
3267
3268         
3269     },
3270     initEvents : function()
3271     {
3272         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3273         
3274         if (this.autohide) {
3275             
3276             var prevScroll = 0;
3277             var ft = this.el;
3278             
3279             Roo.get(document).on('scroll',function(e) {
3280                 var ns = Roo.get(document).getScroll().top;
3281                 var os = prevScroll;
3282                 prevScroll = ns;
3283                 
3284                 if(ns > os){
3285                     ft.removeClass('slideDown');
3286                     ft.addClass('slideUp');
3287                     return;
3288                 }
3289                 ft.removeClass('slideUp');
3290                 ft.addClass('slideDown');
3291                  
3292               
3293           },this);
3294         }
3295     }    
3296           
3297       
3298     
3299     
3300 });
3301
3302
3303
3304  
3305
3306  /*
3307  * - LGPL
3308  *
3309  * navbar
3310  * 
3311  */
3312
3313 /**
3314  * @class Roo.bootstrap.NavSidebar
3315  * @extends Roo.bootstrap.Navbar
3316  * Bootstrap Sidebar class
3317  * 
3318  * @constructor
3319  * Create a new Sidebar
3320  * @param {Object} config The config object
3321  */
3322
3323
3324 Roo.bootstrap.NavSidebar = function(config){
3325     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3326 };
3327
3328 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3329     
3330     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3331     
3332     getAutoCreate : function(){
3333         
3334         
3335         return  {
3336             tag: 'div',
3337             cls: 'sidebar sidebar-nav'
3338         };
3339     
3340         
3341     }
3342     
3343     
3344     
3345 });
3346
3347
3348
3349  
3350
3351  /*
3352  * - LGPL
3353  *
3354  * nav group
3355  * 
3356  */
3357
3358 /**
3359  * @class Roo.bootstrap.NavGroup
3360  * @extends Roo.bootstrap.Component
3361  * Bootstrap NavGroup class
3362  * @cfg {String} align left | right
3363  * @cfg {Boolean} inverse false | true
3364  * @cfg {String} type (nav|pills|tab) default nav
3365  * @cfg {String} navId - reference Id for navbar.
3366
3367  * 
3368  * @constructor
3369  * Create a new nav group
3370  * @param {Object} config The config object
3371  */
3372
3373 Roo.bootstrap.NavGroup = function(config){
3374     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3375     this.navItems = [];
3376    
3377     Roo.bootstrap.NavGroup.register(this);
3378      this.addEvents({
3379         /**
3380              * @event changed
3381              * Fires when the active item changes
3382              * @param {Roo.bootstrap.NavGroup} this
3383              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3384              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3385          */
3386         'changed': true
3387      });
3388     
3389 };
3390
3391 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3392     
3393     align: '',
3394     inverse: false,
3395     form: false,
3396     type: 'nav',
3397     navId : '',
3398     // private
3399     
3400     navItems : false, 
3401     
3402     getAutoCreate : function()
3403     {
3404         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3405         
3406         cfg = {
3407             tag : 'ul',
3408             cls: 'nav' 
3409         }
3410         
3411         if (['tabs','pills'].indexOf(this.type)!==-1) {
3412             cfg.cls += ' nav-' + this.type
3413         } else {
3414             if (this.type!=='nav') {
3415                 Roo.log('nav type must be nav/tabs/pills')
3416             }
3417             cfg.cls += ' navbar-nav'
3418         }
3419         
3420         if (this.parent().sidebar) {
3421             cfg = {
3422                 tag: 'ul',
3423                 cls: 'dashboard-menu sidebar-menu'
3424             }
3425             
3426             return cfg;
3427         }
3428         
3429         if (this.form === true) {
3430             cfg = {
3431                 tag: 'form',
3432                 cls: 'navbar-form'
3433             }
3434             
3435             if (this.align === 'right') {
3436                 cfg.cls += ' navbar-right';
3437             } else {
3438                 cfg.cls += ' navbar-left';
3439             }
3440         }
3441         
3442         if (this.align === 'right') {
3443             cfg.cls += ' navbar-right';
3444         }
3445         
3446         if (this.inverse) {
3447             cfg.cls += ' navbar-inverse';
3448             
3449         }
3450         
3451         
3452         return cfg;
3453     },
3454     /**
3455     * sets the active Navigation item
3456     * @param {Roo.bootstrap.NavItem} the new current navitem
3457     */
3458     setActiveItem : function(item)
3459     {
3460         var prev = false;
3461         Roo.each(this.navItems, function(v){
3462             if (v == item) {
3463                 return ;
3464             }
3465             if (v.isActive()) {
3466                 v.setActive(false, true);
3467                 prev = v;
3468                 
3469             }
3470             
3471         });
3472
3473         item.setActive(true, true);
3474         this.fireEvent('changed', this, item, prev);
3475         
3476         
3477     },
3478     /**
3479     * gets the active Navigation item
3480     * @return {Roo.bootstrap.NavItem} the current navitem
3481     */
3482     getActive : function()
3483     {
3484         
3485         var prev = false;
3486         Roo.each(this.navItems, function(v){
3487             
3488             if (v.isActive()) {
3489                 prev = v;
3490                 
3491             }
3492             
3493         });
3494         return prev;
3495     },
3496     
3497     indexOfNav : function()
3498     {
3499         
3500         var prev = false;
3501         Roo.each(this.navItems, function(v,i){
3502             
3503             if (v.isActive()) {
3504                 prev = i;
3505                 
3506             }
3507             
3508         });
3509         return prev;
3510     },
3511     /**
3512     * adds a Navigation item
3513     * @param {Roo.bootstrap.NavItem} the navitem to add
3514     */
3515     addItem : function(cfg)
3516     {
3517         var cn = new Roo.bootstrap.NavItem(cfg);
3518         this.register(cn);
3519         cn.parentId = this.id;
3520         cn.onRender(this.el, null);
3521         return cn;
3522     },
3523     /**
3524     * register a Navigation item
3525     * @param {Roo.bootstrap.NavItem} the navitem to add
3526     */
3527     register : function(item)
3528     {
3529         this.navItems.push( item);
3530         item.navId = this.navId;
3531     
3532     },
3533     
3534     /**
3535     * clear all the Navigation item
3536     */
3537    
3538     clearAll : function()
3539     {
3540         this.navItems = [];
3541         this.el.dom.innerHTML = '';
3542     },
3543     
3544     getNavItem: function(tabId)
3545     {
3546         var ret = false;
3547         Roo.each(this.navItems, function(e) {
3548             if (e.tabId == tabId) {
3549                ret =  e;
3550                return false;
3551             }
3552             return true;
3553             
3554         });
3555         return ret;
3556     },
3557     
3558     setActiveNext : function()
3559     {
3560         var i = this.indexOfNav(this.getActive());
3561         if (i > this.navItems.length) {
3562             return;
3563         }
3564         this.setActiveItem(this.navItems[i+1]);
3565     },
3566     setActivePrev : function()
3567     {
3568         var i = this.indexOfNav(this.getActive());
3569         if (i  < 1) {
3570             return;
3571         }
3572         this.setActiveItem(this.navItems[i-1]);
3573     },
3574     clearWasActive : function(except) {
3575         Roo.each(this.navItems, function(e) {
3576             if (e.tabId != except.tabId && e.was_active) {
3577                e.was_active = false;
3578                return false;
3579             }
3580             return true;
3581             
3582         });
3583     },
3584     getWasActive : function ()
3585     {
3586         var r = false;
3587         Roo.each(this.navItems, function(e) {
3588             if (e.was_active) {
3589                r = e;
3590                return false;
3591             }
3592             return true;
3593             
3594         });
3595         return r;
3596     }
3597     
3598     
3599 });
3600
3601  
3602 Roo.apply(Roo.bootstrap.NavGroup, {
3603     
3604     groups: {},
3605      /**
3606     * register a Navigation Group
3607     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3608     */
3609     register : function(navgrp)
3610     {
3611         this.groups[navgrp.navId] = navgrp;
3612         
3613     },
3614     /**
3615     * fetch a Navigation Group based on the navigation ID
3616     * @param {string} the navgroup to add
3617     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3618     */
3619     get: function(navId) {
3620         if (typeof(this.groups[navId]) == 'undefined') {
3621             return false;
3622             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3623         }
3624         return this.groups[navId] ;
3625     }
3626     
3627     
3628     
3629 });
3630
3631  /*
3632  * - LGPL
3633  *
3634  * row
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavItem
3640  * @extends Roo.bootstrap.Component
3641  * Bootstrap Navbar.NavItem class
3642  * @cfg {String} href  link to
3643  * @cfg {String} html content of button
3644  * @cfg {String} badge text inside badge
3645  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3646  * @cfg {String} glyphicon name of glyphicon
3647  * @cfg {String} icon name of font awesome icon
3648  * @cfg {Boolean} active Is item active
3649  * @cfg {Boolean} disabled Is item disabled
3650  
3651  * @cfg {Boolean} preventDefault (true | false) default false
3652  * @cfg {String} tabId the tab that this item activates.
3653  * @cfg {String} tagtype (a|span) render as a href or span?
3654   
3655  * @constructor
3656  * Create a new Navbar Item
3657  * @param {Object} config The config object
3658  */
3659 Roo.bootstrap.NavItem = function(config){
3660     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3661     this.addEvents({
3662         // raw events
3663         /**
3664          * @event click
3665          * The raw click event for the entire grid.
3666          * @param {Roo.EventObject} e
3667          */
3668         "click" : true,
3669          /**
3670             * @event changed
3671             * Fires when the active item active state changes
3672             * @param {Roo.bootstrap.NavItem} this
3673             * @param {boolean} state the new state
3674              
3675          */
3676         'changed': true
3677     });
3678    
3679 };
3680
3681 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3682     
3683     href: false,
3684     html: '',
3685     badge: '',
3686     icon: false,
3687     glyphicon: false,
3688     active: false,
3689     preventDefault : false,
3690     tabId : false,
3691     tagtype : 'a',
3692     disabled : false,
3693     
3694     was_active : false,
3695     
3696     getAutoCreate : function(){
3697          
3698         var cfg = {
3699             tag: 'li',
3700             cls: 'nav-item'
3701             
3702         }
3703         if (this.active) {
3704             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3705         }
3706         if (this.disabled) {
3707             cfg.cls += ' disabled';
3708         }
3709         
3710         if (this.href || this.html || this.glyphicon || this.icon) {
3711             cfg.cn = [
3712                 {
3713                     tag: this.tagtype,
3714                     href : this.href || "#",
3715                     html: this.html || ''
3716                 }
3717             ];
3718             
3719             if (this.icon) {
3720                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3721             }
3722
3723             if(this.glyphicon) {
3724                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3725             }
3726             
3727             if (this.menu) {
3728                 
3729                 cfg.cn[0].html += " <span class='caret'></span>";
3730              
3731             }
3732             
3733             if (this.badge !== '') {
3734                  
3735                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3736             }
3737         }
3738         
3739         
3740         
3741         return cfg;
3742     },
3743     initEvents: function() 
3744     {
3745         if (typeof (this.menu) != 'undefined') {
3746             this.menu.parentType = this.xtype;
3747             this.menu.triggerEl = this.el;
3748             this.addxtype(Roo.apply({}, this.menu));
3749         }
3750         
3751         this.el.select('a',true).on('click', this.onClick, this);
3752         
3753         if(this.tagtype == 'span'){
3754             this.el.select('span',true).on('click', this.onClick, this);
3755         }
3756        
3757         // at this point parent should be available..
3758         this.parent().register(this);
3759     },
3760     
3761     onClick : function(e)
3762     {
3763          
3764         if(this.preventDefault){
3765             e.preventDefault();
3766         }
3767         if (this.disabled) {
3768             return;
3769         }
3770         
3771         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3772         if (tg && tg.transition) {
3773             Roo.log("waiting for the transitionend");
3774             return;
3775         }
3776         
3777         Roo.log("fire event clicked");
3778         if(this.fireEvent('click', this, e) === false){
3779             return;
3780         };
3781         
3782         if(this.tagtype == 'span'){
3783             return;
3784         }
3785         
3786         var p = this.parent();
3787         if (['tabs','pills'].indexOf(p.type)!==-1) {
3788             if (typeof(p.setActiveItem) !== 'undefined') {
3789                 p.setActiveItem(this);
3790             }
3791         }
3792         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3793         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3794             // remove the collapsed menu expand...
3795             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3796         }
3797         
3798     },
3799     
3800     isActive: function () {
3801         return this.active
3802     },
3803     setActive : function(state, fire, is_was_active)
3804     {
3805         if (this.active && !state & this.navId) {
3806             this.was_active = true;
3807             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3808             if (nv) {
3809                 nv.clearWasActive(this);
3810             }
3811             
3812         }
3813         this.active = state;
3814         
3815         if (!state ) {
3816             this.el.removeClass('active');
3817         } else if (!this.el.hasClass('active')) {
3818             this.el.addClass('active');
3819         }
3820         if (fire) {
3821             this.fireEvent('changed', this, state);
3822         }
3823         
3824         // show a panel if it's registered and related..
3825         
3826         if (!this.navId || !this.tabId || !state || is_was_active) {
3827             return;
3828         }
3829         
3830         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3831         if (!tg) {
3832             return;
3833         }
3834         var pan = tg.getPanelByName(this.tabId);
3835         if (!pan) {
3836             return;
3837         }
3838         // if we can not flip to new panel - go back to old nav highlight..
3839         if (false == tg.showPanel(pan)) {
3840             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3841             if (nv) {
3842                 var onav = nv.getWasActive();
3843                 if (onav) {
3844                     onav.setActive(true, false, true);
3845                 }
3846             }
3847             
3848         }
3849         
3850         
3851         
3852     },
3853      // this should not be here...
3854     setDisabled : function(state)
3855     {
3856         this.disabled = state;
3857         if (!state ) {
3858             this.el.removeClass('disabled');
3859         } else if (!this.el.hasClass('disabled')) {
3860             this.el.addClass('disabled');
3861         }
3862         
3863     }
3864 });
3865  
3866
3867  /*
3868  * - LGPL
3869  *
3870  * sidebar item
3871  *
3872  *  li
3873  *    <span> icon </span>
3874  *    <span> text </span>
3875  *    <span>badge </span>
3876  */
3877
3878 /**
3879  * @class Roo.bootstrap.NavSidebarItem
3880  * @extends Roo.bootstrap.NavItem
3881  * Bootstrap Navbar.NavSidebarItem class
3882  * @constructor
3883  * Create a new Navbar Button
3884  * @param {Object} config The config object
3885  */
3886 Roo.bootstrap.NavSidebarItem = function(config){
3887     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3888     this.addEvents({
3889         // raw events
3890         /**
3891          * @event click
3892          * The raw click event for the entire grid.
3893          * @param {Roo.EventObject} e
3894          */
3895         "click" : true,
3896          /**
3897             * @event changed
3898             * Fires when the active item active state changes
3899             * @param {Roo.bootstrap.NavSidebarItem} this
3900             * @param {boolean} state the new state
3901              
3902          */
3903         'changed': true
3904     });
3905    
3906 };
3907
3908 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3909     
3910     
3911     getAutoCreate : function(){
3912         
3913         
3914         var a = {
3915                 tag: 'a',
3916                 href : this.href || '#',
3917                 cls: '',
3918                 html : '',
3919                 cn : []
3920         };
3921         var cfg = {
3922             tag: 'li',
3923             cls: '',
3924             cn: [ a ]
3925         }
3926         var span = {
3927             tag: 'span',
3928             html : this.html || ''
3929         }
3930         
3931         
3932         if (this.active) {
3933             cfg.cls += ' active';
3934         }
3935         
3936         // left icon..
3937         if (this.glyphicon || this.icon) {
3938             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3939             a.cn.push({ tag : 'i', cls : c }) ;
3940         }
3941         // html..
3942         a.cn.push(span);
3943         // then badge..
3944         if (this.badge !== '') {
3945             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3946         }
3947         // fi
3948         if (this.menu) {
3949             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3950             a.cls += 'dropdown-toggle treeview' ;
3951             
3952         }
3953         
3954         
3955         
3956         return cfg;
3957          
3958            
3959     }
3960    
3961      
3962  
3963 });
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * row
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.Row
3975  * @extends Roo.bootstrap.Component
3976  * Bootstrap Row class (contains columns...)
3977  * 
3978  * @constructor
3979  * Create a new Row
3980  * @param {Object} config The config object
3981  */
3982
3983 Roo.bootstrap.Row = function(config){
3984     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3985 };
3986
3987 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3988     
3989     getAutoCreate : function(){
3990        return {
3991             cls: 'row clearfix'
3992        };
3993     }
3994     
3995     
3996 });
3997
3998  
3999
4000  /*
4001  * - LGPL
4002  *
4003  * element
4004  * 
4005  */
4006
4007 /**
4008  * @class Roo.bootstrap.Element
4009  * @extends Roo.bootstrap.Component
4010  * Bootstrap Element class
4011  * @cfg {String} html contents of the element
4012  * @cfg {String} tag tag of the element
4013  * @cfg {String} cls class of the element
4014  * 
4015  * @constructor
4016  * Create a new Element
4017  * @param {Object} config The config object
4018  */
4019
4020 Roo.bootstrap.Element = function(config){
4021     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4022 };
4023
4024 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4025     
4026     tag: 'div',
4027     cls: '',
4028     html: '',
4029      
4030     
4031     getAutoCreate : function(){
4032         
4033         var cfg = {
4034             tag: this.tag,
4035             cls: this.cls,
4036             html: this.html
4037         }
4038         
4039         
4040         
4041         return cfg;
4042     }
4043    
4044 });
4045
4046  
4047
4048  /*
4049  * - LGPL
4050  *
4051  * pagination
4052  * 
4053  */
4054
4055 /**
4056  * @class Roo.bootstrap.Pagination
4057  * @extends Roo.bootstrap.Component
4058  * Bootstrap Pagination class
4059  * @cfg {String} size xs | sm | md | lg
4060  * @cfg {Boolean} inverse false | true
4061  * 
4062  * @constructor
4063  * Create a new Pagination
4064  * @param {Object} config The config object
4065  */
4066
4067 Roo.bootstrap.Pagination = function(config){
4068     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4069 };
4070
4071 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4072     
4073     cls: false,
4074     size: false,
4075     inverse: false,
4076     
4077     getAutoCreate : function(){
4078         var cfg = {
4079             tag: 'ul',
4080                 cls: 'pagination'
4081         };
4082         if (this.inverse) {
4083             cfg.cls += ' inverse';
4084         }
4085         if (this.html) {
4086             cfg.html=this.html;
4087         }
4088         if (this.cls) {
4089             cfg.cls += " " + this.cls;
4090         }
4091         return cfg;
4092     }
4093    
4094 });
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * Pagination item
4102  * 
4103  */
4104
4105
4106 /**
4107  * @class Roo.bootstrap.PaginationItem
4108  * @extends Roo.bootstrap.Component
4109  * Bootstrap PaginationItem class
4110  * @cfg {String} html text
4111  * @cfg {String} href the link
4112  * @cfg {Boolean} preventDefault (true | false) default true
4113  * @cfg {Boolean} active (true | false) default false
4114  * 
4115  * 
4116  * @constructor
4117  * Create a new PaginationItem
4118  * @param {Object} config The config object
4119  */
4120
4121
4122 Roo.bootstrap.PaginationItem = function(config){
4123     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4124     this.addEvents({
4125         // raw events
4126         /**
4127          * @event click
4128          * The raw click event for the entire grid.
4129          * @param {Roo.EventObject} e
4130          */
4131         "click" : true
4132     });
4133 };
4134
4135 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4136     
4137     href : false,
4138     html : false,
4139     preventDefault: true,
4140     active : false,
4141     cls : false,
4142     
4143     getAutoCreate : function(){
4144         var cfg= {
4145             tag: 'li',
4146             cn: [
4147                 {
4148                     tag : 'a',
4149                     href : this.href ? this.href : '#',
4150                     html : this.html ? this.html : ''
4151                 }
4152             ]
4153         };
4154         
4155         if(this.cls){
4156             cfg.cls = this.cls;
4157         }
4158         
4159         if(this.active){
4160             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4161         }
4162         
4163         return cfg;
4164     },
4165     
4166     initEvents: function() {
4167         
4168         this.el.on('click', this.onClick, this);
4169         
4170     },
4171     onClick : function(e)
4172     {
4173         Roo.log('PaginationItem on click ');
4174         if(this.preventDefault){
4175             e.preventDefault();
4176         }
4177         
4178         this.fireEvent('click', this, e);
4179     }
4180    
4181 });
4182
4183  
4184
4185  /*
4186  * - LGPL
4187  *
4188  * slider
4189  * 
4190  */
4191
4192
4193 /**
4194  * @class Roo.bootstrap.Slider
4195  * @extends Roo.bootstrap.Component
4196  * Bootstrap Slider class
4197  *    
4198  * @constructor
4199  * Create a new Slider
4200  * @param {Object} config The config object
4201  */
4202
4203 Roo.bootstrap.Slider = function(config){
4204     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4205 };
4206
4207 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4208     
4209     getAutoCreate : function(){
4210         
4211         var cfg = {
4212             tag: 'div',
4213             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4214             cn: [
4215                 {
4216                     tag: 'a',
4217                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4218                 }
4219             ]
4220         }
4221         
4222         return cfg;
4223     }
4224    
4225 });
4226
4227  /*
4228  * Based on:
4229  * Ext JS Library 1.1.1
4230  * Copyright(c) 2006-2007, Ext JS, LLC.
4231  *
4232  * Originally Released Under LGPL - original licence link has changed is not relivant.
4233  *
4234  * Fork - LGPL
4235  * <script type="text/javascript">
4236  */
4237  
4238
4239 /**
4240  * @class Roo.grid.ColumnModel
4241  * @extends Roo.util.Observable
4242  * This is the default implementation of a ColumnModel used by the Grid. It defines
4243  * the columns in the grid.
4244  * <br>Usage:<br>
4245  <pre><code>
4246  var colModel = new Roo.grid.ColumnModel([
4247         {header: "Ticker", width: 60, sortable: true, locked: true},
4248         {header: "Company Name", width: 150, sortable: true},
4249         {header: "Market Cap.", width: 100, sortable: true},
4250         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4251         {header: "Employees", width: 100, sortable: true, resizable: false}
4252  ]);
4253  </code></pre>
4254  * <p>
4255  
4256  * The config options listed for this class are options which may appear in each
4257  * individual column definition.
4258  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4259  * @constructor
4260  * @param {Object} config An Array of column config objects. See this class's
4261  * config objects for details.
4262 */
4263 Roo.grid.ColumnModel = function(config){
4264         /**
4265      * The config passed into the constructor
4266      */
4267     this.config = config;
4268     this.lookup = {};
4269
4270     // if no id, create one
4271     // if the column does not have a dataIndex mapping,
4272     // map it to the order it is in the config
4273     for(var i = 0, len = config.length; i < len; i++){
4274         var c = config[i];
4275         if(typeof c.dataIndex == "undefined"){
4276             c.dataIndex = i;
4277         }
4278         if(typeof c.renderer == "string"){
4279             c.renderer = Roo.util.Format[c.renderer];
4280         }
4281         if(typeof c.id == "undefined"){
4282             c.id = Roo.id();
4283         }
4284         if(c.editor && c.editor.xtype){
4285             c.editor  = Roo.factory(c.editor, Roo.grid);
4286         }
4287         if(c.editor && c.editor.isFormField){
4288             c.editor = new Roo.grid.GridEditor(c.editor);
4289         }
4290         this.lookup[c.id] = c;
4291     }
4292
4293     /**
4294      * The width of columns which have no width specified (defaults to 100)
4295      * @type Number
4296      */
4297     this.defaultWidth = 100;
4298
4299     /**
4300      * Default sortable of columns which have no sortable specified (defaults to false)
4301      * @type Boolean
4302      */
4303     this.defaultSortable = false;
4304
4305     this.addEvents({
4306         /**
4307              * @event widthchange
4308              * Fires when the width of a column changes.
4309              * @param {ColumnModel} this
4310              * @param {Number} columnIndex The column index
4311              * @param {Number} newWidth The new width
4312              */
4313             "widthchange": true,
4314         /**
4315              * @event headerchange
4316              * Fires when the text of a header changes.
4317              * @param {ColumnModel} this
4318              * @param {Number} columnIndex The column index
4319              * @param {Number} newText The new header text
4320              */
4321             "headerchange": true,
4322         /**
4323              * @event hiddenchange
4324              * Fires when a column is hidden or "unhidden".
4325              * @param {ColumnModel} this
4326              * @param {Number} columnIndex The column index
4327              * @param {Boolean} hidden true if hidden, false otherwise
4328              */
4329             "hiddenchange": true,
4330             /**
4331          * @event columnmoved
4332          * Fires when a column is moved.
4333          * @param {ColumnModel} this
4334          * @param {Number} oldIndex
4335          * @param {Number} newIndex
4336          */
4337         "columnmoved" : true,
4338         /**
4339          * @event columlockchange
4340          * Fires when a column's locked state is changed
4341          * @param {ColumnModel} this
4342          * @param {Number} colIndex
4343          * @param {Boolean} locked true if locked
4344          */
4345         "columnlockchange" : true
4346     });
4347     Roo.grid.ColumnModel.superclass.constructor.call(this);
4348 };
4349 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4350     /**
4351      * @cfg {String} header The header text to display in the Grid view.
4352      */
4353     /**
4354      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4355      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4356      * specified, the column's index is used as an index into the Record's data Array.
4357      */
4358     /**
4359      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4360      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4361      */
4362     /**
4363      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4364      * Defaults to the value of the {@link #defaultSortable} property.
4365      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4366      */
4367     /**
4368      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4369      */
4370     /**
4371      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4372      */
4373     /**
4374      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4375      */
4376     /**
4377      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4378      */
4379     /**
4380      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4381      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4382      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4383      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4384      */
4385        /**
4386      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4387      */
4388     /**
4389      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4390      */
4391
4392     /**
4393      * Returns the id of the column at the specified index.
4394      * @param {Number} index The column index
4395      * @return {String} the id
4396      */
4397     getColumnId : function(index){
4398         return this.config[index].id;
4399     },
4400
4401     /**
4402      * Returns the column for a specified id.
4403      * @param {String} id The column id
4404      * @return {Object} the column
4405      */
4406     getColumnById : function(id){
4407         return this.lookup[id];
4408     },
4409
4410     
4411     /**
4412      * Returns the column for a specified dataIndex.
4413      * @param {String} dataIndex The column dataIndex
4414      * @return {Object|Boolean} the column or false if not found
4415      */
4416     getColumnByDataIndex: function(dataIndex){
4417         var index = this.findColumnIndex(dataIndex);
4418         return index > -1 ? this.config[index] : false;
4419     },
4420     
4421     /**
4422      * Returns the index for a specified column id.
4423      * @param {String} id The column id
4424      * @return {Number} the index, or -1 if not found
4425      */
4426     getIndexById : function(id){
4427         for(var i = 0, len = this.config.length; i < len; i++){
4428             if(this.config[i].id == id){
4429                 return i;
4430             }
4431         }
4432         return -1;
4433     },
4434     
4435     /**
4436      * Returns the index for a specified column dataIndex.
4437      * @param {String} dataIndex The column dataIndex
4438      * @return {Number} the index, or -1 if not found
4439      */
4440     
4441     findColumnIndex : function(dataIndex){
4442         for(var i = 0, len = this.config.length; i < len; i++){
4443             if(this.config[i].dataIndex == dataIndex){
4444                 return i;
4445             }
4446         }
4447         return -1;
4448     },
4449     
4450     
4451     moveColumn : function(oldIndex, newIndex){
4452         var c = this.config[oldIndex];
4453         this.config.splice(oldIndex, 1);
4454         this.config.splice(newIndex, 0, c);
4455         this.dataMap = null;
4456         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4457     },
4458
4459     isLocked : function(colIndex){
4460         return this.config[colIndex].locked === true;
4461     },
4462
4463     setLocked : function(colIndex, value, suppressEvent){
4464         if(this.isLocked(colIndex) == value){
4465             return;
4466         }
4467         this.config[colIndex].locked = value;
4468         if(!suppressEvent){
4469             this.fireEvent("columnlockchange", this, colIndex, value);
4470         }
4471     },
4472
4473     getTotalLockedWidth : function(){
4474         var totalWidth = 0;
4475         for(var i = 0; i < this.config.length; i++){
4476             if(this.isLocked(i) && !this.isHidden(i)){
4477                 this.totalWidth += this.getColumnWidth(i);
4478             }
4479         }
4480         return totalWidth;
4481     },
4482
4483     getLockedCount : function(){
4484         for(var i = 0, len = this.config.length; i < len; i++){
4485             if(!this.isLocked(i)){
4486                 return i;
4487             }
4488         }
4489     },
4490
4491     /**
4492      * Returns the number of columns.
4493      * @return {Number}
4494      */
4495     getColumnCount : function(visibleOnly){
4496         if(visibleOnly === true){
4497             var c = 0;
4498             for(var i = 0, len = this.config.length; i < len; i++){
4499                 if(!this.isHidden(i)){
4500                     c++;
4501                 }
4502             }
4503             return c;
4504         }
4505         return this.config.length;
4506     },
4507
4508     /**
4509      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4510      * @param {Function} fn
4511      * @param {Object} scope (optional)
4512      * @return {Array} result
4513      */
4514     getColumnsBy : function(fn, scope){
4515         var r = [];
4516         for(var i = 0, len = this.config.length; i < len; i++){
4517             var c = this.config[i];
4518             if(fn.call(scope||this, c, i) === true){
4519                 r[r.length] = c;
4520             }
4521         }
4522         return r;
4523     },
4524
4525     /**
4526      * Returns true if the specified column is sortable.
4527      * @param {Number} col The column index
4528      * @return {Boolean}
4529      */
4530     isSortable : function(col){
4531         if(typeof this.config[col].sortable == "undefined"){
4532             return this.defaultSortable;
4533         }
4534         return this.config[col].sortable;
4535     },
4536
4537     /**
4538      * Returns the rendering (formatting) function defined for the column.
4539      * @param {Number} col The column index.
4540      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4541      */
4542     getRenderer : function(col){
4543         if(!this.config[col].renderer){
4544             return Roo.grid.ColumnModel.defaultRenderer;
4545         }
4546         return this.config[col].renderer;
4547     },
4548
4549     /**
4550      * Sets the rendering (formatting) function for a column.
4551      * @param {Number} col The column index
4552      * @param {Function} fn The function to use to process the cell's raw data
4553      * to return HTML markup for the grid view. The render function is called with
4554      * the following parameters:<ul>
4555      * <li>Data value.</li>
4556      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4557      * <li>css A CSS style string to apply to the table cell.</li>
4558      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4559      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4560      * <li>Row index</li>
4561      * <li>Column index</li>
4562      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4563      */
4564     setRenderer : function(col, fn){
4565         this.config[col].renderer = fn;
4566     },
4567
4568     /**
4569      * Returns the width for the specified column.
4570      * @param {Number} col The column index
4571      * @return {Number}
4572      */
4573     getColumnWidth : function(col){
4574         return this.config[col].width * 1 || this.defaultWidth;
4575     },
4576
4577     /**
4578      * Sets the width for a column.
4579      * @param {Number} col The column index
4580      * @param {Number} width The new width
4581      */
4582     setColumnWidth : function(col, width, suppressEvent){
4583         this.config[col].width = width;
4584         this.totalWidth = null;
4585         if(!suppressEvent){
4586              this.fireEvent("widthchange", this, col, width);
4587         }
4588     },
4589
4590     /**
4591      * Returns the total width of all columns.
4592      * @param {Boolean} includeHidden True to include hidden column widths
4593      * @return {Number}
4594      */
4595     getTotalWidth : function(includeHidden){
4596         if(!this.totalWidth){
4597             this.totalWidth = 0;
4598             for(var i = 0, len = this.config.length; i < len; i++){
4599                 if(includeHidden || !this.isHidden(i)){
4600                     this.totalWidth += this.getColumnWidth(i);
4601                 }
4602             }
4603         }
4604         return this.totalWidth;
4605     },
4606
4607     /**
4608      * Returns the header for the specified column.
4609      * @param {Number} col The column index
4610      * @return {String}
4611      */
4612     getColumnHeader : function(col){
4613         return this.config[col].header;
4614     },
4615
4616     /**
4617      * Sets the header for a column.
4618      * @param {Number} col The column index
4619      * @param {String} header The new header
4620      */
4621     setColumnHeader : function(col, header){
4622         this.config[col].header = header;
4623         this.fireEvent("headerchange", this, col, header);
4624     },
4625
4626     /**
4627      * Returns the tooltip for the specified column.
4628      * @param {Number} col The column index
4629      * @return {String}
4630      */
4631     getColumnTooltip : function(col){
4632             return this.config[col].tooltip;
4633     },
4634     /**
4635      * Sets the tooltip for a column.
4636      * @param {Number} col The column index
4637      * @param {String} tooltip The new tooltip
4638      */
4639     setColumnTooltip : function(col, tooltip){
4640             this.config[col].tooltip = tooltip;
4641     },
4642
4643     /**
4644      * Returns the dataIndex for the specified column.
4645      * @param {Number} col The column index
4646      * @return {Number}
4647      */
4648     getDataIndex : function(col){
4649         return this.config[col].dataIndex;
4650     },
4651
4652     /**
4653      * Sets the dataIndex for a column.
4654      * @param {Number} col The column index
4655      * @param {Number} dataIndex The new dataIndex
4656      */
4657     setDataIndex : function(col, dataIndex){
4658         this.config[col].dataIndex = dataIndex;
4659     },
4660
4661     
4662     
4663     /**
4664      * Returns true if the cell is editable.
4665      * @param {Number} colIndex The column index
4666      * @param {Number} rowIndex The row index
4667      * @return {Boolean}
4668      */
4669     isCellEditable : function(colIndex, rowIndex){
4670         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4671     },
4672
4673     /**
4674      * Returns the editor defined for the cell/column.
4675      * return false or null to disable editing.
4676      * @param {Number} colIndex The column index
4677      * @param {Number} rowIndex The row index
4678      * @return {Object}
4679      */
4680     getCellEditor : function(colIndex, rowIndex){
4681         return this.config[colIndex].editor;
4682     },
4683
4684     /**
4685      * Sets if a column is editable.
4686      * @param {Number} col The column index
4687      * @param {Boolean} editable True if the column is editable
4688      */
4689     setEditable : function(col, editable){
4690         this.config[col].editable = editable;
4691     },
4692
4693
4694     /**
4695      * Returns true if the column is hidden.
4696      * @param {Number} colIndex The column index
4697      * @return {Boolean}
4698      */
4699     isHidden : function(colIndex){
4700         return this.config[colIndex].hidden;
4701     },
4702
4703
4704     /**
4705      * Returns true if the column width cannot be changed
4706      */
4707     isFixed : function(colIndex){
4708         return this.config[colIndex].fixed;
4709     },
4710
4711     /**
4712      * Returns true if the column can be resized
4713      * @return {Boolean}
4714      */
4715     isResizable : function(colIndex){
4716         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4717     },
4718     /**
4719      * Sets if a column is hidden.
4720      * @param {Number} colIndex The column index
4721      * @param {Boolean} hidden True if the column is hidden
4722      */
4723     setHidden : function(colIndex, hidden){
4724         this.config[colIndex].hidden = hidden;
4725         this.totalWidth = null;
4726         this.fireEvent("hiddenchange", this, colIndex, hidden);
4727     },
4728
4729     /**
4730      * Sets the editor for a column.
4731      * @param {Number} col The column index
4732      * @param {Object} editor The editor object
4733      */
4734     setEditor : function(col, editor){
4735         this.config[col].editor = editor;
4736     }
4737 });
4738
4739 Roo.grid.ColumnModel.defaultRenderer = function(value){
4740         if(typeof value == "string" && value.length < 1){
4741             return "&#160;";
4742         }
4743         return value;
4744 };
4745
4746 // Alias for backwards compatibility
4747 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4748 /*
4749  * Based on:
4750  * Ext JS Library 1.1.1
4751  * Copyright(c) 2006-2007, Ext JS, LLC.
4752  *
4753  * Originally Released Under LGPL - original licence link has changed is not relivant.
4754  *
4755  * Fork - LGPL
4756  * <script type="text/javascript">
4757  */
4758  
4759 /**
4760  * @class Roo.LoadMask
4761  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4762  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4763  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4764  * element's UpdateManager load indicator and will be destroyed after the initial load.
4765  * @constructor
4766  * Create a new LoadMask
4767  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4768  * @param {Object} config The config object
4769  */
4770 Roo.LoadMask = function(el, config){
4771     this.el = Roo.get(el);
4772     Roo.apply(this, config);
4773     if(this.store){
4774         this.store.on('beforeload', this.onBeforeLoad, this);
4775         this.store.on('load', this.onLoad, this);
4776         this.store.on('loadexception', this.onLoadException, this);
4777         this.removeMask = false;
4778     }else{
4779         var um = this.el.getUpdateManager();
4780         um.showLoadIndicator = false; // disable the default indicator
4781         um.on('beforeupdate', this.onBeforeLoad, this);
4782         um.on('update', this.onLoad, this);
4783         um.on('failure', this.onLoad, this);
4784         this.removeMask = true;
4785     }
4786 };
4787
4788 Roo.LoadMask.prototype = {
4789     /**
4790      * @cfg {Boolean} removeMask
4791      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4792      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4793      */
4794     /**
4795      * @cfg {String} msg
4796      * The text to display in a centered loading message box (defaults to 'Loading...')
4797      */
4798     msg : 'Loading...',
4799     /**
4800      * @cfg {String} msgCls
4801      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4802      */
4803     msgCls : 'x-mask-loading',
4804
4805     /**
4806      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4807      * @type Boolean
4808      */
4809     disabled: false,
4810
4811     /**
4812      * Disables the mask to prevent it from being displayed
4813      */
4814     disable : function(){
4815        this.disabled = true;
4816     },
4817
4818     /**
4819      * Enables the mask so that it can be displayed
4820      */
4821     enable : function(){
4822         this.disabled = false;
4823     },
4824     
4825     onLoadException : function()
4826     {
4827         Roo.log(arguments);
4828         
4829         if (typeof(arguments[3]) != 'undefined') {
4830             Roo.MessageBox.alert("Error loading",arguments[3]);
4831         } 
4832         /*
4833         try {
4834             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4835                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4836             }   
4837         } catch(e) {
4838             
4839         }
4840         */
4841     
4842         
4843         
4844         this.el.unmask(this.removeMask);
4845     },
4846     // private
4847     onLoad : function()
4848     {
4849         this.el.unmask(this.removeMask);
4850     },
4851
4852     // private
4853     onBeforeLoad : function(){
4854         if(!this.disabled){
4855             this.el.mask(this.msg, this.msgCls);
4856         }
4857     },
4858
4859     // private
4860     destroy : function(){
4861         if(this.store){
4862             this.store.un('beforeload', this.onBeforeLoad, this);
4863             this.store.un('load', this.onLoad, this);
4864             this.store.un('loadexception', this.onLoadException, this);
4865         }else{
4866             var um = this.el.getUpdateManager();
4867             um.un('beforeupdate', this.onBeforeLoad, this);
4868             um.un('update', this.onLoad, this);
4869             um.un('failure', this.onLoad, this);
4870         }
4871     }
4872 };/*
4873  * - LGPL
4874  *
4875  * table
4876  * 
4877  */
4878
4879 /**
4880  * @class Roo.bootstrap.Table
4881  * @extends Roo.bootstrap.Component
4882  * Bootstrap Table class
4883  * @cfg {String} cls table class
4884  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4885  * @cfg {String} bgcolor Specifies the background color for a table
4886  * @cfg {Number} border Specifies whether the table cells should have borders or not
4887  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4888  * @cfg {Number} cellspacing Specifies the space between cells
4889  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4890  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4891  * @cfg {String} sortable Specifies that the table should be sortable
4892  * @cfg {String} summary Specifies a summary of the content of a table
4893  * @cfg {Number} width Specifies the width of a table
4894  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4895  * 
4896  * @cfg {boolean} striped Should the rows be alternative striped
4897  * @cfg {boolean} bordered Add borders to the table
4898  * @cfg {boolean} hover Add hover highlighting
4899  * @cfg {boolean} condensed Format condensed
4900  * @cfg {boolean} responsive Format condensed
4901  * @cfg {Boolean} loadMask (true|false) default false
4902  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4903  * @cfg {Boolean} thead (true|false) generate thead, default true
4904  * @cfg {Boolean} RowSelection (true|false) default false
4905  * @cfg {Boolean} CellSelection (true|false) default false
4906  *
4907  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4908  
4909  * 
4910  * @constructor
4911  * Create a new Table
4912  * @param {Object} config The config object
4913  */
4914
4915 Roo.bootstrap.Table = function(config){
4916     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4917     
4918     if (this.sm) {
4919         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4920         this.sm = this.selModel;
4921         this.sm.xmodule = this.xmodule || false;
4922     }
4923     if (this.cm && typeof(this.cm.config) == 'undefined') {
4924         this.colModel = new Roo.grid.ColumnModel(this.cm);
4925         this.cm = this.colModel;
4926         this.cm.xmodule = this.xmodule || false;
4927     }
4928     if (this.store) {
4929         this.store= Roo.factory(this.store, Roo.data);
4930         this.ds = this.store;
4931         this.ds.xmodule = this.xmodule || false;
4932          
4933     }
4934     if (this.footer && this.store) {
4935         this.footer.dataSource = this.ds;
4936         this.footer = Roo.factory(this.footer);
4937     }
4938     
4939     /** @private */
4940     this.addEvents({
4941         /**
4942          * @event cellclick
4943          * Fires when a cell is clicked
4944          * @param {Roo.bootstrap.Table} this
4945          * @param {Roo.Element} el
4946          * @param {Number} rowIndex
4947          * @param {Number} columnIndex
4948          * @param {Roo.EventObject} e
4949          */
4950         "cellclick" : true,
4951         /**
4952          * @event celldblclick
4953          * Fires when a cell is double clicked
4954          * @param {Roo.bootstrap.Table} this
4955          * @param {Roo.Element} el
4956          * @param {Number} rowIndex
4957          * @param {Number} columnIndex
4958          * @param {Roo.EventObject} e
4959          */
4960         "celldblclick" : true,
4961         /**
4962          * @event rowclick
4963          * Fires when a row is clicked
4964          * @param {Roo.bootstrap.Table} this
4965          * @param {Roo.Element} el
4966          * @param {Number} rowIndex
4967          * @param {Roo.EventObject} e
4968          */
4969         "rowclick" : true,
4970         /**
4971          * @event rowdblclick
4972          * Fires when a row is double clicked
4973          * @param {Roo.bootstrap.Table} this
4974          * @param {Roo.Element} el
4975          * @param {Number} rowIndex
4976          * @param {Roo.EventObject} e
4977          */
4978         "rowdblclick" : true,
4979         /**
4980          * @event mouseover
4981          * Fires when a mouseover occur
4982          * @param {Roo.bootstrap.Table} this
4983          * @param {Roo.Element} el
4984          * @param {Number} rowIndex
4985          * @param {Number} columnIndex
4986          * @param {Roo.EventObject} e
4987          */
4988         "mouseover" : true,
4989         /**
4990          * @event mouseout
4991          * Fires when a mouseout occur
4992          * @param {Roo.bootstrap.Table} this
4993          * @param {Roo.Element} el
4994          * @param {Number} rowIndex
4995          * @param {Number} columnIndex
4996          * @param {Roo.EventObject} e
4997          */
4998         "mouseout" : true,
4999         /**
5000          * @event rowclass
5001          * Fires when a row is rendered, so you can change add a style to it.
5002          * @param {Roo.bootstrap.Table} this
5003          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5004          */
5005         'rowclass' : true
5006         
5007     });
5008 };
5009
5010 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5011     
5012     cls: false,
5013     align: false,
5014     bgcolor: false,
5015     border: false,
5016     cellpadding: false,
5017     cellspacing: false,
5018     frame: false,
5019     rules: false,
5020     sortable: false,
5021     summary: false,
5022     width: false,
5023     striped : false,
5024     bordered: false,
5025     hover:  false,
5026     condensed : false,
5027     responsive : false,
5028     sm : false,
5029     cm : false,
5030     store : false,
5031     loadMask : false,
5032     tfoot : true,
5033     thead : true,
5034     RowSelection : false,
5035     CellSelection : false,
5036     layout : false,
5037     
5038     // Roo.Element - the tbody
5039     mainBody: false, 
5040     
5041     getAutoCreate : function(){
5042         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5043         
5044         cfg = {
5045             tag: 'table',
5046             cls : 'table',
5047             cn : []
5048         }
5049             
5050         if (this.striped) {
5051             cfg.cls += ' table-striped';
5052         }
5053         
5054         if (this.hover) {
5055             cfg.cls += ' table-hover';
5056         }
5057         if (this.bordered) {
5058             cfg.cls += ' table-bordered';
5059         }
5060         if (this.condensed) {
5061             cfg.cls += ' table-condensed';
5062         }
5063         if (this.responsive) {
5064             cfg.cls += ' table-responsive';
5065         }
5066         
5067         if (this.cls) {
5068             cfg.cls+=  ' ' +this.cls;
5069         }
5070         
5071         // this lot should be simplifed...
5072         
5073         if (this.align) {
5074             cfg.align=this.align;
5075         }
5076         if (this.bgcolor) {
5077             cfg.bgcolor=this.bgcolor;
5078         }
5079         if (this.border) {
5080             cfg.border=this.border;
5081         }
5082         if (this.cellpadding) {
5083             cfg.cellpadding=this.cellpadding;
5084         }
5085         if (this.cellspacing) {
5086             cfg.cellspacing=this.cellspacing;
5087         }
5088         if (this.frame) {
5089             cfg.frame=this.frame;
5090         }
5091         if (this.rules) {
5092             cfg.rules=this.rules;
5093         }
5094         if (this.sortable) {
5095             cfg.sortable=this.sortable;
5096         }
5097         if (this.summary) {
5098             cfg.summary=this.summary;
5099         }
5100         if (this.width) {
5101             cfg.width=this.width;
5102         }
5103         if (this.layout) {
5104             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5105         }
5106         
5107         if(this.store || this.cm){
5108             if(this.thead){
5109                 cfg.cn.push(this.renderHeader());
5110             }
5111             
5112             cfg.cn.push(this.renderBody());
5113             
5114             if(this.tfoot){
5115                 cfg.cn.push(this.renderFooter());
5116             }
5117             
5118             cfg.cls+=  ' TableGrid';
5119         }
5120         
5121         return { cn : [ cfg ] };
5122     },
5123     
5124     initEvents : function()
5125     {   
5126         if(!this.store || !this.cm){
5127             return;
5128         }
5129         
5130         //Roo.log('initEvents with ds!!!!');
5131         
5132         this.mainBody = this.el.select('tbody', true).first();
5133         
5134         
5135         var _this = this;
5136         
5137         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5138             e.on('click', _this.sort, _this);
5139         });
5140         
5141         this.el.on("click", this.onClick, this);
5142         this.el.on("dblclick", this.onDblClick, this);
5143         
5144         this.parent().el.setStyle('position', 'relative');
5145         if (this.footer) {
5146             this.footer.parentId = this.id;
5147             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5148         }
5149         
5150         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5151         
5152         this.store.on('load', this.onLoad, this);
5153         this.store.on('beforeload', this.onBeforeLoad, this);
5154         this.store.on('update', this.onUpdate, this);
5155         
5156     },
5157     
5158     onMouseover : function(e, el)
5159     {
5160         var cell = Roo.get(el);
5161         
5162         if(!cell){
5163             return;
5164         }
5165         
5166         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5167             cell = cell.findParent('td', false, true);
5168         }
5169         
5170         var row = cell.findParent('tr', false, true);
5171         var cellIndex = cell.dom.cellIndex;
5172         var rowIndex = row.dom.rowIndex - 1; // start from 0
5173         
5174         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5175         
5176     },
5177     
5178     onMouseout : function(e, el)
5179     {
5180         var cell = Roo.get(el);
5181         
5182         if(!cell){
5183             return;
5184         }
5185         
5186         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5187             cell = cell.findParent('td', false, true);
5188         }
5189         
5190         var row = cell.findParent('tr', false, true);
5191         var cellIndex = cell.dom.cellIndex;
5192         var rowIndex = row.dom.rowIndex - 1; // start from 0
5193         
5194         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5195         
5196     },
5197     
5198     onClick : function(e, el)
5199     {
5200         var cell = Roo.get(el);
5201         
5202         if(!cell || (!this.CellSelection && !this.RowSelection)){
5203             return;
5204         }
5205         
5206         
5207         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5208             cell = cell.findParent('td', false, true);
5209         }
5210         
5211         var row = cell.findParent('tr', false, true);
5212         var cellIndex = cell.dom.cellIndex;
5213         var rowIndex = row.dom.rowIndex - 1;
5214         
5215         if(this.CellSelection){
5216             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5217         }
5218         
5219         if(this.RowSelection){
5220             this.fireEvent('rowclick', this, row, rowIndex, e);
5221         }
5222         
5223         
5224     },
5225     
5226     onDblClick : function(e,el)
5227     {
5228         var cell = Roo.get(el);
5229         
5230         if(!cell || (!this.CellSelection && !this.RowSelection)){
5231             return;
5232         }
5233         
5234         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5235             cell = cell.findParent('td', false, true);
5236         }
5237         
5238         var row = cell.findParent('tr', false, true);
5239         var cellIndex = cell.dom.cellIndex;
5240         var rowIndex = row.dom.rowIndex - 1;
5241         
5242         if(this.CellSelection){
5243             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5244         }
5245         
5246         if(this.RowSelection){
5247             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5248         }
5249     },
5250     
5251     sort : function(e,el)
5252     {
5253         var col = Roo.get(el)
5254         
5255         if(!col.hasClass('sortable')){
5256             return;
5257         }
5258         
5259         var sort = col.attr('sort');
5260         var dir = 'ASC';
5261         
5262         if(col.hasClass('glyphicon-arrow-up')){
5263             dir = 'DESC';
5264         }
5265         
5266         this.store.sortInfo = {field : sort, direction : dir};
5267         
5268         if (this.footer) {
5269             Roo.log("calling footer first");
5270             this.footer.onClick('first');
5271         } else {
5272         
5273             this.store.load({ params : { start : 0 } });
5274         }
5275     },
5276     
5277     renderHeader : function()
5278     {
5279         var header = {
5280             tag: 'thead',
5281             cn : []
5282         };
5283         
5284         var cm = this.cm;
5285         
5286         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5287             
5288             var config = cm.config[i];
5289                     
5290             var c = {
5291                 tag: 'th',
5292                 style : '',
5293                 html: cm.getColumnHeader(i)
5294             };
5295             
5296             if(typeof(config.hidden) != 'undefined' && config.hidden){
5297                 c.style += ' display:none;';
5298             }
5299             
5300             if(typeof(config.dataIndex) != 'undefined'){
5301                 c.sort = config.dataIndex;
5302             }
5303             
5304             if(typeof(config.sortable) != 'undefined' && config.sortable){
5305                 c.cls = 'sortable';
5306             }
5307             
5308             if(typeof(config.align) != 'undefined' && config.align.length){
5309                 c.style += ' text-align:' + config.align + ';';
5310             }
5311             
5312             if(typeof(config.width) != 'undefined'){
5313                 c.style += ' width:' + config.width + 'px;';
5314             }
5315             
5316             header.cn.push(c)
5317         }
5318         
5319         return header;
5320     },
5321     
5322     renderBody : function()
5323     {
5324         var body = {
5325             tag: 'tbody',
5326             cn : [
5327                 {
5328                     tag: 'tr',
5329                     cn : [
5330                         {
5331                             tag : 'td',
5332                             colspan :  this.cm.getColumnCount()
5333                         }
5334                     ]
5335                 }
5336             ]
5337         };
5338         
5339         return body;
5340     },
5341     
5342     renderFooter : function()
5343     {
5344         var footer = {
5345             tag: 'tfoot',
5346             cn : [
5347                 {
5348                     tag: 'tr',
5349                     cn : [
5350                         {
5351                             tag : 'td',
5352                             colspan :  this.cm.getColumnCount()
5353                         }
5354                     ]
5355                 }
5356             ]
5357         };
5358         
5359         return footer;
5360     },
5361     
5362     
5363     
5364     onLoad : function()
5365     {
5366         Roo.log('ds onload');
5367         this.clear();
5368         
5369         var _this = this;
5370         var cm = this.cm;
5371         var ds = this.store;
5372         
5373         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5374             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5375             
5376             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5377                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5378             }
5379             
5380             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5381                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5382             }
5383         });
5384         
5385         var tbody =  this.mainBody;
5386               
5387         if(ds.getCount() > 0){
5388             ds.data.each(function(d,rowIndex){
5389                 var row =  this.renderRow(cm, ds, rowIndex);
5390                 
5391                 tbody.createChild(row);
5392                 
5393                 var _this = this;
5394                 
5395                 if(row.cellObjects.length){
5396                     Roo.each(row.cellObjects, function(r){
5397                         _this.renderCellObject(r);
5398                     })
5399                 }
5400                 
5401             }, this);
5402         }
5403         
5404         Roo.each(this.el.select('tbody td', true).elements, function(e){
5405             e.on('mouseover', _this.onMouseover, _this);
5406         });
5407         
5408         Roo.each(this.el.select('tbody td', true).elements, function(e){
5409             e.on('mouseout', _this.onMouseout, _this);
5410         });
5411
5412         //if(this.loadMask){
5413         //    this.maskEl.hide();
5414         //}
5415     },
5416     
5417     
5418     onUpdate : function(ds,record)
5419     {
5420         this.refreshRow(record);
5421     },
5422     onRemove : function(ds, record, index, isUpdate){
5423         if(isUpdate !== true){
5424             this.fireEvent("beforerowremoved", this, index, record);
5425         }
5426         var bt = this.mainBody.dom;
5427         if(bt.rows[index]){
5428             bt.removeChild(bt.rows[index]);
5429         }
5430         
5431         if(isUpdate !== true){
5432             //this.stripeRows(index);
5433             //this.syncRowHeights(index, index);
5434             //this.layout();
5435             this.fireEvent("rowremoved", this, index, record);
5436         }
5437     },
5438     
5439     
5440     refreshRow : function(record){
5441         var ds = this.store, index;
5442         if(typeof record == 'number'){
5443             index = record;
5444             record = ds.getAt(index);
5445         }else{
5446             index = ds.indexOf(record);
5447         }
5448         this.insertRow(ds, index, true);
5449         this.onRemove(ds, record, index+1, true);
5450         //this.syncRowHeights(index, index);
5451         //this.layout();
5452         this.fireEvent("rowupdated", this, index, record);
5453     },
5454     
5455     insertRow : function(dm, rowIndex, isUpdate){
5456         
5457         if(!isUpdate){
5458             this.fireEvent("beforerowsinserted", this, rowIndex);
5459         }
5460             //var s = this.getScrollState();
5461         var row = this.renderRow(this.cm, this.store, rowIndex);
5462         // insert before rowIndex..
5463         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5464         
5465         var _this = this;
5466                 
5467         if(row.cellObjects.length){
5468             Roo.each(row.cellObjects, function(r){
5469                 _this.renderCellObject(r);
5470             })
5471         }
5472             
5473         if(!isUpdate){
5474             this.fireEvent("rowsinserted", this, rowIndex);
5475             //this.syncRowHeights(firstRow, lastRow);
5476             //this.stripeRows(firstRow);
5477             //this.layout();
5478         }
5479         
5480     },
5481     
5482     
5483     getRowDom : function(rowIndex)
5484     {
5485         // not sure if I need to check this.. but let's do it anyway..
5486         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5487                 this.mainBody.dom.rows[rowIndex] : false
5488     },
5489     // returns the object tree for a tr..
5490   
5491     
5492     renderRow : function(cm, ds, rowIndex) {
5493         
5494         var d = ds.getAt(rowIndex);
5495         
5496         var row = {
5497             tag : 'tr',
5498             cn : []
5499         };
5500             
5501         var cellObjects = [];
5502         
5503         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5504             var config = cm.config[i];
5505             
5506             var renderer = cm.getRenderer(i);
5507             var value = '';
5508             var id = false;
5509             
5510             if(typeof(renderer) !== 'undefined'){
5511                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5512             }
5513             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5514             // and are rendered into the cells after the row is rendered - using the id for the element.
5515             
5516             if(typeof(value) === 'object'){
5517                 id = Roo.id();
5518                 cellObjects.push({
5519                     container : id,
5520                     cfg : value 
5521                 })
5522             }
5523             
5524             var rowcfg = {
5525                 record: d,
5526                 rowIndex : rowIndex,
5527                 colIndex : i,
5528                 rowClass : ''
5529             }
5530
5531             this.fireEvent('rowclass', this, rowcfg);
5532             
5533             var td = {
5534                 tag: 'td',
5535                 cls : rowcfg.rowClass,
5536                 style: '',
5537                 html: (typeof(value) === 'object') ? '' : value
5538             };
5539             
5540             if (id) {
5541                 td.id = id;
5542             }
5543             
5544             if(typeof(config.hidden) != 'undefined' && config.hidden){
5545                 td.style += ' display:none;';
5546             }
5547             
5548             if(typeof(config.align) != 'undefined' && config.align.length){
5549                 td.style += ' text-align:' + config.align + ';';
5550             }
5551             
5552             if(typeof(config.width) != 'undefined'){
5553                 td.style += ' width:' +  config.width + 'px;';
5554             }
5555              
5556             row.cn.push(td);
5557            
5558         }
5559         
5560         row.cellObjects = cellObjects;
5561         
5562         return row;
5563           
5564     },
5565     
5566     
5567     
5568     onBeforeLoad : function()
5569     {
5570         //Roo.log('ds onBeforeLoad');
5571         
5572         //this.clear();
5573         
5574         //if(this.loadMask){
5575         //    this.maskEl.show();
5576         //}
5577     },
5578     
5579     clear : function()
5580     {
5581         this.el.select('tbody', true).first().dom.innerHTML = '';
5582     },
5583     
5584     getSelectionModel : function(){
5585         if(!this.selModel){
5586             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5587         }
5588         return this.selModel;
5589     },
5590     /*
5591      * Render the Roo.bootstrap object from renderder
5592      */
5593     renderCellObject : function(r)
5594     {
5595         var _this = this;
5596         
5597         var t = r.cfg.render(r.container);
5598         
5599         if(r.cfg.cn){
5600             Roo.each(r.cfg.cn, function(c){
5601                 var child = {
5602                     container: t.getChildContainer(),
5603                     cfg: c
5604                 }
5605                 _this.renderCellObject(child);
5606             })
5607         }
5608     }
5609    
5610 });
5611
5612  
5613
5614  /*
5615  * - LGPL
5616  *
5617  * table cell
5618  * 
5619  */
5620
5621 /**
5622  * @class Roo.bootstrap.TableCell
5623  * @extends Roo.bootstrap.Component
5624  * Bootstrap TableCell class
5625  * @cfg {String} html cell contain text
5626  * @cfg {String} cls cell class
5627  * @cfg {String} tag cell tag (td|th) default td
5628  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5629  * @cfg {String} align Aligns the content in a cell
5630  * @cfg {String} axis Categorizes cells
5631  * @cfg {String} bgcolor Specifies the background color of a cell
5632  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5633  * @cfg {Number} colspan Specifies the number of columns a cell should span
5634  * @cfg {String} headers Specifies one or more header cells a cell is related to
5635  * @cfg {Number} height Sets the height of a cell
5636  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5637  * @cfg {Number} rowspan Sets the number of rows a cell should span
5638  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5639  * @cfg {String} valign Vertical aligns the content in a cell
5640  * @cfg {Number} width Specifies the width of a cell
5641  * 
5642  * @constructor
5643  * Create a new TableCell
5644  * @param {Object} config The config object
5645  */
5646
5647 Roo.bootstrap.TableCell = function(config){
5648     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5649 };
5650
5651 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5652     
5653     html: false,
5654     cls: false,
5655     tag: false,
5656     abbr: false,
5657     align: false,
5658     axis: false,
5659     bgcolor: false,
5660     charoff: false,
5661     colspan: false,
5662     headers: false,
5663     height: false,
5664     nowrap: false,
5665     rowspan: false,
5666     scope: false,
5667     valign: false,
5668     width: false,
5669     
5670     
5671     getAutoCreate : function(){
5672         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5673         
5674         cfg = {
5675             tag: 'td'
5676         }
5677         
5678         if(this.tag){
5679             cfg.tag = this.tag;
5680         }
5681         
5682         if (this.html) {
5683             cfg.html=this.html
5684         }
5685         if (this.cls) {
5686             cfg.cls=this.cls
5687         }
5688         if (this.abbr) {
5689             cfg.abbr=this.abbr
5690         }
5691         if (this.align) {
5692             cfg.align=this.align
5693         }
5694         if (this.axis) {
5695             cfg.axis=this.axis
5696         }
5697         if (this.bgcolor) {
5698             cfg.bgcolor=this.bgcolor
5699         }
5700         if (this.charoff) {
5701             cfg.charoff=this.charoff
5702         }
5703         if (this.colspan) {
5704             cfg.colspan=this.colspan
5705         }
5706         if (this.headers) {
5707             cfg.headers=this.headers
5708         }
5709         if (this.height) {
5710             cfg.height=this.height
5711         }
5712         if (this.nowrap) {
5713             cfg.nowrap=this.nowrap
5714         }
5715         if (this.rowspan) {
5716             cfg.rowspan=this.rowspan
5717         }
5718         if (this.scope) {
5719             cfg.scope=this.scope
5720         }
5721         if (this.valign) {
5722             cfg.valign=this.valign
5723         }
5724         if (this.width) {
5725             cfg.width=this.width
5726         }
5727         
5728         
5729         return cfg;
5730     }
5731    
5732 });
5733
5734  
5735
5736  /*
5737  * - LGPL
5738  *
5739  * table row
5740  * 
5741  */
5742
5743 /**
5744  * @class Roo.bootstrap.TableRow
5745  * @extends Roo.bootstrap.Component
5746  * Bootstrap TableRow class
5747  * @cfg {String} cls row class
5748  * @cfg {String} align Aligns the content in a table row
5749  * @cfg {String} bgcolor Specifies a background color for a table row
5750  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5751  * @cfg {String} valign Vertical aligns the content in a table row
5752  * 
5753  * @constructor
5754  * Create a new TableRow
5755  * @param {Object} config The config object
5756  */
5757
5758 Roo.bootstrap.TableRow = function(config){
5759     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5760 };
5761
5762 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5763     
5764     cls: false,
5765     align: false,
5766     bgcolor: false,
5767     charoff: false,
5768     valign: false,
5769     
5770     getAutoCreate : function(){
5771         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5772         
5773         cfg = {
5774             tag: 'tr'
5775         }
5776             
5777         if(this.cls){
5778             cfg.cls = this.cls;
5779         }
5780         if(this.align){
5781             cfg.align = this.align;
5782         }
5783         if(this.bgcolor){
5784             cfg.bgcolor = this.bgcolor;
5785         }
5786         if(this.charoff){
5787             cfg.charoff = this.charoff;
5788         }
5789         if(this.valign){
5790             cfg.valign = this.valign;
5791         }
5792         
5793         return cfg;
5794     }
5795    
5796 });
5797
5798  
5799
5800  /*
5801  * - LGPL
5802  *
5803  * table body
5804  * 
5805  */
5806
5807 /**
5808  * @class Roo.bootstrap.TableBody
5809  * @extends Roo.bootstrap.Component
5810  * Bootstrap TableBody class
5811  * @cfg {String} cls element class
5812  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5813  * @cfg {String} align Aligns the content inside the element
5814  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5815  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5816  * 
5817  * @constructor
5818  * Create a new TableBody
5819  * @param {Object} config The config object
5820  */
5821
5822 Roo.bootstrap.TableBody = function(config){
5823     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5824 };
5825
5826 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5827     
5828     cls: false,
5829     tag: false,
5830     align: false,
5831     charoff: false,
5832     valign: false,
5833     
5834     getAutoCreate : function(){
5835         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5836         
5837         cfg = {
5838             tag: 'tbody'
5839         }
5840             
5841         if (this.cls) {
5842             cfg.cls=this.cls
5843         }
5844         if(this.tag){
5845             cfg.tag = this.tag;
5846         }
5847         
5848         if(this.align){
5849             cfg.align = this.align;
5850         }
5851         if(this.charoff){
5852             cfg.charoff = this.charoff;
5853         }
5854         if(this.valign){
5855             cfg.valign = this.valign;
5856         }
5857         
5858         return cfg;
5859     }
5860     
5861     
5862 //    initEvents : function()
5863 //    {
5864 //        
5865 //        if(!this.store){
5866 //            return;
5867 //        }
5868 //        
5869 //        this.store = Roo.factory(this.store, Roo.data);
5870 //        this.store.on('load', this.onLoad, this);
5871 //        
5872 //        this.store.load();
5873 //        
5874 //    },
5875 //    
5876 //    onLoad: function () 
5877 //    {   
5878 //        this.fireEvent('load', this);
5879 //    }
5880 //    
5881 //   
5882 });
5883
5884  
5885
5886  /*
5887  * Based on:
5888  * Ext JS Library 1.1.1
5889  * Copyright(c) 2006-2007, Ext JS, LLC.
5890  *
5891  * Originally Released Under LGPL - original licence link has changed is not relivant.
5892  *
5893  * Fork - LGPL
5894  * <script type="text/javascript">
5895  */
5896
5897 // as we use this in bootstrap.
5898 Roo.namespace('Roo.form');
5899  /**
5900  * @class Roo.form.Action
5901  * Internal Class used to handle form actions
5902  * @constructor
5903  * @param {Roo.form.BasicForm} el The form element or its id
5904  * @param {Object} config Configuration options
5905  */
5906
5907  
5908  
5909 // define the action interface
5910 Roo.form.Action = function(form, options){
5911     this.form = form;
5912     this.options = options || {};
5913 };
5914 /**
5915  * Client Validation Failed
5916  * @const 
5917  */
5918 Roo.form.Action.CLIENT_INVALID = 'client';
5919 /**
5920  * Server Validation Failed
5921  * @const 
5922  */
5923 Roo.form.Action.SERVER_INVALID = 'server';
5924  /**
5925  * Connect to Server Failed
5926  * @const 
5927  */
5928 Roo.form.Action.CONNECT_FAILURE = 'connect';
5929 /**
5930  * Reading Data from Server Failed
5931  * @const 
5932  */
5933 Roo.form.Action.LOAD_FAILURE = 'load';
5934
5935 Roo.form.Action.prototype = {
5936     type : 'default',
5937     failureType : undefined,
5938     response : undefined,
5939     result : undefined,
5940
5941     // interface method
5942     run : function(options){
5943
5944     },
5945
5946     // interface method
5947     success : function(response){
5948
5949     },
5950
5951     // interface method
5952     handleResponse : function(response){
5953
5954     },
5955
5956     // default connection failure
5957     failure : function(response){
5958         
5959         this.response = response;
5960         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5961         this.form.afterAction(this, false);
5962     },
5963
5964     processResponse : function(response){
5965         this.response = response;
5966         if(!response.responseText){
5967             return true;
5968         }
5969         this.result = this.handleResponse(response);
5970         return this.result;
5971     },
5972
5973     // utility functions used internally
5974     getUrl : function(appendParams){
5975         var url = this.options.url || this.form.url || this.form.el.dom.action;
5976         if(appendParams){
5977             var p = this.getParams();
5978             if(p){
5979                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5980             }
5981         }
5982         return url;
5983     },
5984
5985     getMethod : function(){
5986         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5987     },
5988
5989     getParams : function(){
5990         var bp = this.form.baseParams;
5991         var p = this.options.params;
5992         if(p){
5993             if(typeof p == "object"){
5994                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5995             }else if(typeof p == 'string' && bp){
5996                 p += '&' + Roo.urlEncode(bp);
5997             }
5998         }else if(bp){
5999             p = Roo.urlEncode(bp);
6000         }
6001         return p;
6002     },
6003
6004     createCallback : function(){
6005         return {
6006             success: this.success,
6007             failure: this.failure,
6008             scope: this,
6009             timeout: (this.form.timeout*1000),
6010             upload: this.form.fileUpload ? this.success : undefined
6011         };
6012     }
6013 };
6014
6015 Roo.form.Action.Submit = function(form, options){
6016     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6017 };
6018
6019 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6020     type : 'submit',
6021
6022     haveProgress : false,
6023     uploadComplete : false,
6024     
6025     // uploadProgress indicator.
6026     uploadProgress : function()
6027     {
6028         if (!this.form.progressUrl) {
6029             return;
6030         }
6031         
6032         if (!this.haveProgress) {
6033             Roo.MessageBox.progress("Uploading", "Uploading");
6034         }
6035         if (this.uploadComplete) {
6036            Roo.MessageBox.hide();
6037            return;
6038         }
6039         
6040         this.haveProgress = true;
6041    
6042         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6043         
6044         var c = new Roo.data.Connection();
6045         c.request({
6046             url : this.form.progressUrl,
6047             params: {
6048                 id : uid
6049             },
6050             method: 'GET',
6051             success : function(req){
6052                //console.log(data);
6053                 var rdata = false;
6054                 var edata;
6055                 try  {
6056                    rdata = Roo.decode(req.responseText)
6057                 } catch (e) {
6058                     Roo.log("Invalid data from server..");
6059                     Roo.log(edata);
6060                     return;
6061                 }
6062                 if (!rdata || !rdata.success) {
6063                     Roo.log(rdata);
6064                     Roo.MessageBox.alert(Roo.encode(rdata));
6065                     return;
6066                 }
6067                 var data = rdata.data;
6068                 
6069                 if (this.uploadComplete) {
6070                    Roo.MessageBox.hide();
6071                    return;
6072                 }
6073                    
6074                 if (data){
6075                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6076                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6077                     );
6078                 }
6079                 this.uploadProgress.defer(2000,this);
6080             },
6081        
6082             failure: function(data) {
6083                 Roo.log('progress url failed ');
6084                 Roo.log(data);
6085             },
6086             scope : this
6087         });
6088            
6089     },
6090     
6091     
6092     run : function()
6093     {
6094         // run get Values on the form, so it syncs any secondary forms.
6095         this.form.getValues();
6096         
6097         var o = this.options;
6098         var method = this.getMethod();
6099         var isPost = method == 'POST';
6100         if(o.clientValidation === false || this.form.isValid()){
6101             
6102             if (this.form.progressUrl) {
6103                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6104                     (new Date() * 1) + '' + Math.random());
6105                     
6106             } 
6107             
6108             
6109             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6110                 form:this.form.el.dom,
6111                 url:this.getUrl(!isPost),
6112                 method: method,
6113                 params:isPost ? this.getParams() : null,
6114                 isUpload: this.form.fileUpload
6115             }));
6116             
6117             this.uploadProgress();
6118
6119         }else if (o.clientValidation !== false){ // client validation failed
6120             this.failureType = Roo.form.Action.CLIENT_INVALID;
6121             this.form.afterAction(this, false);
6122         }
6123     },
6124
6125     success : function(response)
6126     {
6127         this.uploadComplete= true;
6128         if (this.haveProgress) {
6129             Roo.MessageBox.hide();
6130         }
6131         
6132         
6133         var result = this.processResponse(response);
6134         if(result === true || result.success){
6135             this.form.afterAction(this, true);
6136             return;
6137         }
6138         if(result.errors){
6139             this.form.markInvalid(result.errors);
6140             this.failureType = Roo.form.Action.SERVER_INVALID;
6141         }
6142         this.form.afterAction(this, false);
6143     },
6144     failure : function(response)
6145     {
6146         this.uploadComplete= true;
6147         if (this.haveProgress) {
6148             Roo.MessageBox.hide();
6149         }
6150         
6151         this.response = response;
6152         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6153         this.form.afterAction(this, false);
6154     },
6155     
6156     handleResponse : function(response){
6157         if(this.form.errorReader){
6158             var rs = this.form.errorReader.read(response);
6159             var errors = [];
6160             if(rs.records){
6161                 for(var i = 0, len = rs.records.length; i < len; i++) {
6162                     var r = rs.records[i];
6163                     errors[i] = r.data;
6164                 }
6165             }
6166             if(errors.length < 1){
6167                 errors = null;
6168             }
6169             return {
6170                 success : rs.success,
6171                 errors : errors
6172             };
6173         }
6174         var ret = false;
6175         try {
6176             ret = Roo.decode(response.responseText);
6177         } catch (e) {
6178             ret = {
6179                 success: false,
6180                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6181                 errors : []
6182             };
6183         }
6184         return ret;
6185         
6186     }
6187 });
6188
6189
6190 Roo.form.Action.Load = function(form, options){
6191     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6192     this.reader = this.form.reader;
6193 };
6194
6195 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6196     type : 'load',
6197
6198     run : function(){
6199         
6200         Roo.Ajax.request(Roo.apply(
6201                 this.createCallback(), {
6202                     method:this.getMethod(),
6203                     url:this.getUrl(false),
6204                     params:this.getParams()
6205         }));
6206     },
6207
6208     success : function(response){
6209         
6210         var result = this.processResponse(response);
6211         if(result === true || !result.success || !result.data){
6212             this.failureType = Roo.form.Action.LOAD_FAILURE;
6213             this.form.afterAction(this, false);
6214             return;
6215         }
6216         this.form.clearInvalid();
6217         this.form.setValues(result.data);
6218         this.form.afterAction(this, true);
6219     },
6220
6221     handleResponse : function(response){
6222         if(this.form.reader){
6223             var rs = this.form.reader.read(response);
6224             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6225             return {
6226                 success : rs.success,
6227                 data : data
6228             };
6229         }
6230         return Roo.decode(response.responseText);
6231     }
6232 });
6233
6234 Roo.form.Action.ACTION_TYPES = {
6235     'load' : Roo.form.Action.Load,
6236     'submit' : Roo.form.Action.Submit
6237 };/*
6238  * - LGPL
6239  *
6240  * form
6241  * 
6242  */
6243
6244 /**
6245  * @class Roo.bootstrap.Form
6246  * @extends Roo.bootstrap.Component
6247  * Bootstrap Form class
6248  * @cfg {String} method  GET | POST (default POST)
6249  * @cfg {String} labelAlign top | left (default top)
6250  * @cfg {String} align left  | right - for navbars
6251  * @cfg {Boolean} loadMask load mask when submit (default true)
6252
6253  * 
6254  * @constructor
6255  * Create a new Form
6256  * @param {Object} config The config object
6257  */
6258
6259
6260 Roo.bootstrap.Form = function(config){
6261     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6262     this.addEvents({
6263         /**
6264          * @event clientvalidation
6265          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6266          * @param {Form} this
6267          * @param {Boolean} valid true if the form has passed client-side validation
6268          */
6269         clientvalidation: true,
6270         /**
6271          * @event beforeaction
6272          * Fires before any action is performed. Return false to cancel the action.
6273          * @param {Form} this
6274          * @param {Action} action The action to be performed
6275          */
6276         beforeaction: true,
6277         /**
6278          * @event actionfailed
6279          * Fires when an action fails.
6280          * @param {Form} this
6281          * @param {Action} action The action that failed
6282          */
6283         actionfailed : true,
6284         /**
6285          * @event actioncomplete
6286          * Fires when an action is completed.
6287          * @param {Form} this
6288          * @param {Action} action The action that completed
6289          */
6290         actioncomplete : true
6291     });
6292     
6293 };
6294
6295 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6296       
6297      /**
6298      * @cfg {String} method
6299      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6300      */
6301     method : 'POST',
6302     /**
6303      * @cfg {String} url
6304      * The URL to use for form actions if one isn't supplied in the action options.
6305      */
6306     /**
6307      * @cfg {Boolean} fileUpload
6308      * Set to true if this form is a file upload.
6309      */
6310      
6311     /**
6312      * @cfg {Object} baseParams
6313      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6314      */
6315       
6316     /**
6317      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6318      */
6319     timeout: 30,
6320     /**
6321      * @cfg {Sting} align (left|right) for navbar forms
6322      */
6323     align : 'left',
6324
6325     // private
6326     activeAction : null,
6327  
6328     /**
6329      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6330      * element by passing it or its id or mask the form itself by passing in true.
6331      * @type Mixed
6332      */
6333     waitMsgTarget : false,
6334     
6335     loadMask : true,
6336     
6337     getAutoCreate : function(){
6338         
6339         var cfg = {
6340             tag: 'form',
6341             method : this.method || 'POST',
6342             id : this.id || Roo.id(),
6343             cls : ''
6344         }
6345         if (this.parent().xtype.match(/^Nav/)) {
6346             cfg.cls = 'navbar-form navbar-' + this.align;
6347             
6348         }
6349         
6350         if (this.labelAlign == 'left' ) {
6351             cfg.cls += ' form-horizontal';
6352         }
6353         
6354         
6355         return cfg;
6356     },
6357     initEvents : function()
6358     {
6359         this.el.on('submit', this.onSubmit, this);
6360         // this was added as random key presses on the form where triggering form submit.
6361         this.el.on('keypress', function(e) {
6362             if (e.getCharCode() != 13) {
6363                 return true;
6364             }
6365             // we might need to allow it for textareas.. and some other items.
6366             // check e.getTarget().
6367             
6368             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6369                 return true;
6370             }
6371         
6372             Roo.log("keypress blocked");
6373             
6374             e.preventDefault();
6375             return false;
6376         });
6377         
6378     },
6379     // private
6380     onSubmit : function(e){
6381         e.stopEvent();
6382     },
6383     
6384      /**
6385      * Returns true if client-side validation on the form is successful.
6386      * @return Boolean
6387      */
6388     isValid : function(){
6389         var items = this.getItems();
6390         var valid = true;
6391         items.each(function(f){
6392            if(!f.validate()){
6393                valid = false;
6394                
6395            }
6396         });
6397         return valid;
6398     },
6399     /**
6400      * Returns true if any fields in this form have changed since their original load.
6401      * @return Boolean
6402      */
6403     isDirty : function(){
6404         var dirty = false;
6405         var items = this.getItems();
6406         items.each(function(f){
6407            if(f.isDirty()){
6408                dirty = true;
6409                return false;
6410            }
6411            return true;
6412         });
6413         return dirty;
6414     },
6415      /**
6416      * Performs a predefined action (submit or load) or custom actions you define on this form.
6417      * @param {String} actionName The name of the action type
6418      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6419      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6420      * accept other config options):
6421      * <pre>
6422 Property          Type             Description
6423 ----------------  ---------------  ----------------------------------------------------------------------------------
6424 url               String           The url for the action (defaults to the form's url)
6425 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6426 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6427 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6428                                    validate the form on the client (defaults to false)
6429      * </pre>
6430      * @return {BasicForm} this
6431      */
6432     doAction : function(action, options){
6433         if(typeof action == 'string'){
6434             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6435         }
6436         if(this.fireEvent('beforeaction', this, action) !== false){
6437             this.beforeAction(action);
6438             action.run.defer(100, action);
6439         }
6440         return this;
6441     },
6442     
6443     // private
6444     beforeAction : function(action){
6445         var o = action.options;
6446         
6447         if(this.loadMask){
6448             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6449         }
6450         // not really supported yet.. ??
6451         
6452         //if(this.waitMsgTarget === true){
6453         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6454         //}else if(this.waitMsgTarget){
6455         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6456         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6457         //}else {
6458         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6459        // }
6460          
6461     },
6462
6463     // private
6464     afterAction : function(action, success){
6465         this.activeAction = null;
6466         var o = action.options;
6467         
6468         //if(this.waitMsgTarget === true){
6469             this.el.unmask();
6470         //}else if(this.waitMsgTarget){
6471         //    this.waitMsgTarget.unmask();
6472         //}else{
6473         //    Roo.MessageBox.updateProgress(1);
6474         //    Roo.MessageBox.hide();
6475        // }
6476         // 
6477         if(success){
6478             if(o.reset){
6479                 this.reset();
6480             }
6481             Roo.callback(o.success, o.scope, [this, action]);
6482             this.fireEvent('actioncomplete', this, action);
6483             
6484         }else{
6485             
6486             // failure condition..
6487             // we have a scenario where updates need confirming.
6488             // eg. if a locking scenario exists..
6489             // we look for { errors : { needs_confirm : true }} in the response.
6490             if (
6491                 (typeof(action.result) != 'undefined')  &&
6492                 (typeof(action.result.errors) != 'undefined')  &&
6493                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6494            ){
6495                 var _t = this;
6496                 Roo.log("not supported yet");
6497                  /*
6498                 
6499                 Roo.MessageBox.confirm(
6500                     "Change requires confirmation",
6501                     action.result.errorMsg,
6502                     function(r) {
6503                         if (r != 'yes') {
6504                             return;
6505                         }
6506                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6507                     }
6508                     
6509                 );
6510                 */
6511                 
6512                 
6513                 return;
6514             }
6515             
6516             Roo.callback(o.failure, o.scope, [this, action]);
6517             // show an error message if no failed handler is set..
6518             if (!this.hasListener('actionfailed')) {
6519                 Roo.log("need to add dialog support");
6520                 /*
6521                 Roo.MessageBox.alert("Error",
6522                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6523                         action.result.errorMsg :
6524                         "Saving Failed, please check your entries or try again"
6525                 );
6526                 */
6527             }
6528             
6529             this.fireEvent('actionfailed', this, action);
6530         }
6531         
6532     },
6533     /**
6534      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6535      * @param {String} id The value to search for
6536      * @return Field
6537      */
6538     findField : function(id){
6539         var items = this.getItems();
6540         var field = items.get(id);
6541         if(!field){
6542              items.each(function(f){
6543                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6544                     field = f;
6545                     return false;
6546                 }
6547                 return true;
6548             });
6549         }
6550         return field || null;
6551     },
6552      /**
6553      * Mark fields in this form invalid in bulk.
6554      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6555      * @return {BasicForm} this
6556      */
6557     markInvalid : function(errors){
6558         if(errors instanceof Array){
6559             for(var i = 0, len = errors.length; i < len; i++){
6560                 var fieldError = errors[i];
6561                 var f = this.findField(fieldError.id);
6562                 if(f){
6563                     f.markInvalid(fieldError.msg);
6564                 }
6565             }
6566         }else{
6567             var field, id;
6568             for(id in errors){
6569                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6570                     field.markInvalid(errors[id]);
6571                 }
6572             }
6573         }
6574         //Roo.each(this.childForms || [], function (f) {
6575         //    f.markInvalid(errors);
6576         //});
6577         
6578         return this;
6579     },
6580
6581     /**
6582      * Set values for fields in this form in bulk.
6583      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6584      * @return {BasicForm} this
6585      */
6586     setValues : function(values){
6587         if(values instanceof Array){ // array of objects
6588             for(var i = 0, len = values.length; i < len; i++){
6589                 var v = values[i];
6590                 var f = this.findField(v.id);
6591                 if(f){
6592                     f.setValue(v.value);
6593                     if(this.trackResetOnLoad){
6594                         f.originalValue = f.getValue();
6595                     }
6596                 }
6597             }
6598         }else{ // object hash
6599             var field, id;
6600             for(id in values){
6601                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6602                     
6603                     if (field.setFromData && 
6604                         field.valueField && 
6605                         field.displayField &&
6606                         // combos' with local stores can 
6607                         // be queried via setValue()
6608                         // to set their value..
6609                         (field.store && !field.store.isLocal)
6610                         ) {
6611                         // it's a combo
6612                         var sd = { };
6613                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6614                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6615                         field.setFromData(sd);
6616                         
6617                     } else {
6618                         field.setValue(values[id]);
6619                     }
6620                     
6621                     
6622                     if(this.trackResetOnLoad){
6623                         field.originalValue = field.getValue();
6624                     }
6625                 }
6626             }
6627         }
6628          
6629         //Roo.each(this.childForms || [], function (f) {
6630         //    f.setValues(values);
6631         //});
6632                 
6633         return this;
6634     },
6635
6636     /**
6637      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6638      * they are returned as an array.
6639      * @param {Boolean} asString
6640      * @return {Object}
6641      */
6642     getValues : function(asString){
6643         //if (this.childForms) {
6644             // copy values from the child forms
6645         //    Roo.each(this.childForms, function (f) {
6646         //        this.setValues(f.getValues());
6647         //    }, this);
6648         //}
6649         
6650         
6651         
6652         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6653         if(asString === true){
6654             return fs;
6655         }
6656         return Roo.urlDecode(fs);
6657     },
6658     
6659     /**
6660      * Returns the fields in this form as an object with key/value pairs. 
6661      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6662      * @return {Object}
6663      */
6664     getFieldValues : function(with_hidden)
6665     {
6666         var items = this.getItems();
6667         var ret = {};
6668         items.each(function(f){
6669             if (!f.getName()) {
6670                 return;
6671             }
6672             var v = f.getValue();
6673             if (f.inputType =='radio') {
6674                 if (typeof(ret[f.getName()]) == 'undefined') {
6675                     ret[f.getName()] = ''; // empty..
6676                 }
6677                 
6678                 if (!f.el.dom.checked) {
6679                     return;
6680                     
6681                 }
6682                 v = f.el.dom.value;
6683                 
6684             }
6685             
6686             // not sure if this supported any more..
6687             if ((typeof(v) == 'object') && f.getRawValue) {
6688                 v = f.getRawValue() ; // dates..
6689             }
6690             // combo boxes where name != hiddenName...
6691             if (f.name != f.getName()) {
6692                 ret[f.name] = f.getRawValue();
6693             }
6694             ret[f.getName()] = v;
6695         });
6696         
6697         return ret;
6698     },
6699
6700     /**
6701      * Clears all invalid messages in this form.
6702      * @return {BasicForm} this
6703      */
6704     clearInvalid : function(){
6705         var items = this.getItems();
6706         
6707         items.each(function(f){
6708            f.clearInvalid();
6709         });
6710         
6711         
6712         
6713         return this;
6714     },
6715
6716     /**
6717      * Resets this form.
6718      * @return {BasicForm} this
6719      */
6720     reset : function(){
6721         var items = this.getItems();
6722         items.each(function(f){
6723             f.reset();
6724         });
6725         
6726         Roo.each(this.childForms || [], function (f) {
6727             f.reset();
6728         });
6729        
6730         
6731         return this;
6732     },
6733     getItems : function()
6734     {
6735         var r=new Roo.util.MixedCollection(false, function(o){
6736             return o.id || (o.id = Roo.id());
6737         });
6738         var iter = function(el) {
6739             if (el.inputEl) {
6740                 r.add(el);
6741             }
6742             if (!el.items) {
6743                 return;
6744             }
6745             Roo.each(el.items,function(e) {
6746                 iter(e);
6747             });
6748             
6749             
6750         };
6751         iter(this);
6752         return r;
6753         
6754         
6755         
6756         
6757     }
6758     
6759 });
6760
6761  
6762 /*
6763  * Based on:
6764  * Ext JS Library 1.1.1
6765  * Copyright(c) 2006-2007, Ext JS, LLC.
6766  *
6767  * Originally Released Under LGPL - original licence link has changed is not relivant.
6768  *
6769  * Fork - LGPL
6770  * <script type="text/javascript">
6771  */
6772 /**
6773  * @class Roo.form.VTypes
6774  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6775  * @singleton
6776  */
6777 Roo.form.VTypes = function(){
6778     // closure these in so they are only created once.
6779     var alpha = /^[a-zA-Z_]+$/;
6780     var alphanum = /^[a-zA-Z0-9_]+$/;
6781     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6782     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6783
6784     // All these messages and functions are configurable
6785     return {
6786         /**
6787          * The function used to validate email addresses
6788          * @param {String} value The email address
6789          */
6790         'email' : function(v){
6791             return email.test(v);
6792         },
6793         /**
6794          * The error text to display when the email validation function returns false
6795          * @type String
6796          */
6797         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6798         /**
6799          * The keystroke filter mask to be applied on email input
6800          * @type RegExp
6801          */
6802         'emailMask' : /[a-z0-9_\.\-@]/i,
6803
6804         /**
6805          * The function used to validate URLs
6806          * @param {String} value The URL
6807          */
6808         'url' : function(v){
6809             return url.test(v);
6810         },
6811         /**
6812          * The error text to display when the url validation function returns false
6813          * @type String
6814          */
6815         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6816         
6817         /**
6818          * The function used to validate alpha values
6819          * @param {String} value The value
6820          */
6821         'alpha' : function(v){
6822             return alpha.test(v);
6823         },
6824         /**
6825          * The error text to display when the alpha validation function returns false
6826          * @type String
6827          */
6828         'alphaText' : 'This field should only contain letters and _',
6829         /**
6830          * The keystroke filter mask to be applied on alpha input
6831          * @type RegExp
6832          */
6833         'alphaMask' : /[a-z_]/i,
6834
6835         /**
6836          * The function used to validate alphanumeric values
6837          * @param {String} value The value
6838          */
6839         'alphanum' : function(v){
6840             return alphanum.test(v);
6841         },
6842         /**
6843          * The error text to display when the alphanumeric validation function returns false
6844          * @type String
6845          */
6846         'alphanumText' : 'This field should only contain letters, numbers and _',
6847         /**
6848          * The keystroke filter mask to be applied on alphanumeric input
6849          * @type RegExp
6850          */
6851         'alphanumMask' : /[a-z0-9_]/i
6852     };
6853 }();/*
6854  * - LGPL
6855  *
6856  * Input
6857  * 
6858  */
6859
6860 /**
6861  * @class Roo.bootstrap.Input
6862  * @extends Roo.bootstrap.Component
6863  * Bootstrap Input class
6864  * @cfg {Boolean} disabled is it disabled
6865  * @cfg {String} fieldLabel - the label associated
6866  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6867  * @cfg {String} name name of the input
6868  * @cfg {string} fieldLabel - the label associated
6869  * @cfg {string}  inputType - input / file submit ...
6870  * @cfg {string} placeholder - placeholder to put in text.
6871  * @cfg {string}  before - input group add on before
6872  * @cfg {string} after - input group add on after
6873  * @cfg {string} size - (lg|sm) or leave empty..
6874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6876  * @cfg {Number} md colspan out of 12 for computer-sized screens
6877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6878  * @cfg {string} value default value of the input
6879  * @cfg {Number} labelWidth set the width of label (0-12)
6880  * @cfg {String} labelAlign (top|left)
6881  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6882  * @cfg {String} align (left|center|right) Default left
6883  * 
6884  * 
6885  * @constructor
6886  * Create a new Input
6887  * @param {Object} config The config object
6888  */
6889
6890 Roo.bootstrap.Input = function(config){
6891     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6892    
6893         this.addEvents({
6894             /**
6895              * @event focus
6896              * Fires when this field receives input focus.
6897              * @param {Roo.form.Field} this
6898              */
6899             focus : true,
6900             /**
6901              * @event blur
6902              * Fires when this field loses input focus.
6903              * @param {Roo.form.Field} this
6904              */
6905             blur : true,
6906             /**
6907              * @event specialkey
6908              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6909              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6910              * @param {Roo.form.Field} this
6911              * @param {Roo.EventObject} e The event object
6912              */
6913             specialkey : true,
6914             /**
6915              * @event change
6916              * Fires just before the field blurs if the field value has changed.
6917              * @param {Roo.form.Field} this
6918              * @param {Mixed} newValue The new value
6919              * @param {Mixed} oldValue The original value
6920              */
6921             change : true,
6922             /**
6923              * @event invalid
6924              * Fires after the field has been marked as invalid.
6925              * @param {Roo.form.Field} this
6926              * @param {String} msg The validation message
6927              */
6928             invalid : true,
6929             /**
6930              * @event valid
6931              * Fires after the field has been validated with no errors.
6932              * @param {Roo.form.Field} this
6933              */
6934             valid : true,
6935              /**
6936              * @event keyup
6937              * Fires after the key up
6938              * @param {Roo.form.Field} this
6939              * @param {Roo.EventObject}  e The event Object
6940              */
6941             keyup : true
6942         });
6943 };
6944
6945 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6946      /**
6947      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6948       automatic validation (defaults to "keyup").
6949      */
6950     validationEvent : "keyup",
6951      /**
6952      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6953      */
6954     validateOnBlur : true,
6955     /**
6956      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6957      */
6958     validationDelay : 250,
6959      /**
6960      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6961      */
6962     focusClass : "x-form-focus",  // not needed???
6963     
6964        
6965     /**
6966      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6967      */
6968     invalidClass : "has-error",
6969     
6970     /**
6971      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6972      */
6973     selectOnFocus : false,
6974     
6975      /**
6976      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6977      */
6978     maskRe : null,
6979        /**
6980      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6981      */
6982     vtype : null,
6983     
6984       /**
6985      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6986      */
6987     disableKeyFilter : false,
6988     
6989        /**
6990      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6991      */
6992     disabled : false,
6993      /**
6994      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6995      */
6996     allowBlank : true,
6997     /**
6998      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6999      */
7000     blankText : "This field is required",
7001     
7002      /**
7003      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7004      */
7005     minLength : 0,
7006     /**
7007      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7008      */
7009     maxLength : Number.MAX_VALUE,
7010     /**
7011      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7012      */
7013     minLengthText : "The minimum length for this field is {0}",
7014     /**
7015      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7016      */
7017     maxLengthText : "The maximum length for this field is {0}",
7018   
7019     
7020     /**
7021      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7022      * If available, this function will be called only after the basic validators all return true, and will be passed the
7023      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7024      */
7025     validator : null,
7026     /**
7027      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7028      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7029      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7030      */
7031     regex : null,
7032     /**
7033      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7034      */
7035     regexText : "",
7036     
7037     
7038     
7039     fieldLabel : '',
7040     inputType : 'text',
7041     
7042     name : false,
7043     placeholder: false,
7044     before : false,
7045     after : false,
7046     size : false,
7047     // private
7048     hasFocus : false,
7049     preventMark: false,
7050     isFormField : true,
7051     value : '',
7052     labelWidth : 2,
7053     labelAlign : false,
7054     readOnly : false,
7055     align : false,
7056     formatedValue : false,
7057     
7058     parentLabelAlign : function()
7059     {
7060         var parent = this;
7061         while (parent.parent()) {
7062             parent = parent.parent();
7063             if (typeof(parent.labelAlign) !='undefined') {
7064                 return parent.labelAlign;
7065             }
7066         }
7067         return 'left';
7068         
7069     },
7070     
7071     getAutoCreate : function(){
7072         
7073         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7074         
7075         var id = Roo.id();
7076         
7077         var cfg = {};
7078         
7079         if(this.inputType != 'hidden'){
7080             cfg.cls = 'form-group' //input-group
7081         }
7082         
7083         var input =  {
7084             tag: 'input',
7085             id : id,
7086             type : this.inputType,
7087             value : this.value,
7088             cls : 'form-control',
7089             placeholder : this.placeholder || ''
7090             
7091         };
7092         
7093         if(this.align){
7094             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7095         }
7096         
7097         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7098             input.maxLength = this.maxLength;
7099         }
7100         
7101         if (this.disabled) {
7102             input.disabled=true;
7103         }
7104         
7105         if (this.readOnly) {
7106             input.readonly=true;
7107         }
7108         
7109         if (this.name) {
7110             input.name = this.name;
7111         }
7112         if (this.size) {
7113             input.cls += ' input-' + this.size;
7114         }
7115         var settings=this;
7116         ['xs','sm','md','lg'].map(function(size){
7117             if (settings[size]) {
7118                 cfg.cls += ' col-' + size + '-' + settings[size];
7119             }
7120         });
7121         
7122         var inputblock = input;
7123         
7124         if (this.before || this.after) {
7125             
7126             inputblock = {
7127                 cls : 'input-group',
7128                 cn :  [] 
7129             };
7130             if (this.before && typeof(this.before) == 'string') {
7131                 
7132                 inputblock.cn.push({
7133                     tag :'span',
7134                     cls : 'roo-input-before input-group-addon',
7135                     html : this.before
7136                 });
7137             }
7138             if (this.before && typeof(this.before) == 'object') {
7139                 this.before = Roo.factory(this.before);
7140                 Roo.log(this.before);
7141                 inputblock.cn.push({
7142                     tag :'span',
7143                     cls : 'roo-input-before input-group-' +
7144                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7145                 });
7146             }
7147             
7148             inputblock.cn.push(input);
7149             
7150             if (this.after && typeof(this.after) == 'string') {
7151                 inputblock.cn.push({
7152                     tag :'span',
7153                     cls : 'roo-input-after input-group-addon',
7154                     html : this.after
7155                 });
7156             }
7157             if (this.after && typeof(this.after) == 'object') {
7158                 this.after = Roo.factory(this.after);
7159                 Roo.log(this.after);
7160                 inputblock.cn.push({
7161                     tag :'span',
7162                     cls : 'roo-input-after input-group-' +
7163                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7164                 });
7165             }
7166         };
7167         
7168         if (align ==='left' && this.fieldLabel.length) {
7169                 Roo.log("left and has label");
7170                 cfg.cn = [
7171                     
7172                     {
7173                         tag: 'label',
7174                         'for' :  id,
7175                         cls : 'control-label col-sm-' + this.labelWidth,
7176                         html : this.fieldLabel
7177                         
7178                     },
7179                     {
7180                         cls : "col-sm-" + (12 - this.labelWidth), 
7181                         cn: [
7182                             inputblock
7183                         ]
7184                     }
7185                     
7186                 ];
7187         } else if ( this.fieldLabel.length) {
7188                 Roo.log(" label");
7189                  cfg.cn = [
7190                    
7191                     {
7192                         tag: 'label',
7193                         //cls : 'input-group-addon',
7194                         html : this.fieldLabel
7195                         
7196                     },
7197                     
7198                     inputblock
7199                     
7200                 ];
7201
7202         } else {
7203             
7204                 Roo.log(" no label && no align");
7205                 cfg.cn = [
7206                     
7207                         inputblock
7208                     
7209                 ];
7210                 
7211                 
7212         };
7213         Roo.log('input-parentType: ' + this.parentType);
7214         
7215         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7216            cfg.cls += ' navbar-form';
7217            Roo.log(cfg);
7218         }
7219         
7220         return cfg;
7221         
7222     },
7223     /**
7224      * return the real input element.
7225      */
7226     inputEl: function ()
7227     {
7228         return this.el.select('input.form-control',true).first();
7229     },
7230     
7231     tooltipEl : function()
7232     {
7233         return this.inputEl();
7234     },
7235     
7236     setDisabled : function(v)
7237     {
7238         var i  = this.inputEl().dom;
7239         if (!v) {
7240             i.removeAttribute('disabled');
7241             return;
7242             
7243         }
7244         i.setAttribute('disabled','true');
7245     },
7246     initEvents : function()
7247     {
7248           
7249         this.inputEl().on("keydown" , this.fireKey,  this);
7250         this.inputEl().on("focus", this.onFocus,  this);
7251         this.inputEl().on("blur", this.onBlur,  this);
7252         
7253         this.inputEl().relayEvent('keyup', this);
7254
7255         // reference to original value for reset
7256         this.originalValue = this.getValue();
7257         //Roo.form.TextField.superclass.initEvents.call(this);
7258         if(this.validationEvent == 'keyup'){
7259             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7260             this.inputEl().on('keyup', this.filterValidation, this);
7261         }
7262         else if(this.validationEvent !== false){
7263             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7264         }
7265         
7266         if(this.selectOnFocus){
7267             this.on("focus", this.preFocus, this);
7268             
7269         }
7270         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7271             this.inputEl().on("keypress", this.filterKeys, this);
7272         }
7273        /* if(this.grow){
7274             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7275             this.el.on("click", this.autoSize,  this);
7276         }
7277         */
7278         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7279             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7280         }
7281         
7282         if (typeof(this.before) == 'object') {
7283             this.before.render(this.el.select('.roo-input-before',true).first());
7284         }
7285         if (typeof(this.after) == 'object') {
7286             this.after.render(this.el.select('.roo-input-after',true).first());
7287         }
7288         
7289         
7290     },
7291     filterValidation : function(e){
7292         if(!e.isNavKeyPress()){
7293             this.validationTask.delay(this.validationDelay);
7294         }
7295     },
7296      /**
7297      * Validates the field value
7298      * @return {Boolean} True if the value is valid, else false
7299      */
7300     validate : function(){
7301         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7302         if(this.disabled || this.validateValue(this.getRawValue())){
7303             this.clearInvalid();
7304             return true;
7305         }
7306         return false;
7307     },
7308     
7309     
7310     /**
7311      * Validates a value according to the field's validation rules and marks the field as invalid
7312      * if the validation fails
7313      * @param {Mixed} value The value to validate
7314      * @return {Boolean} True if the value is valid, else false
7315      */
7316     validateValue : function(value){
7317         if(value.length < 1)  { // if it's blank
7318              if(this.allowBlank){
7319                 this.clearInvalid();
7320                 return true;
7321              }else{
7322                 this.markInvalid(this.blankText);
7323                 return false;
7324              }
7325         }
7326         if(value.length < this.minLength){
7327             this.markInvalid(String.format(this.minLengthText, this.minLength));
7328             return false;
7329         }
7330         if(value.length > this.maxLength){
7331             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7332             return false;
7333         }
7334         if(this.vtype){
7335             var vt = Roo.form.VTypes;
7336             if(!vt[this.vtype](value, this)){
7337                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7338                 return false;
7339             }
7340         }
7341         if(typeof this.validator == "function"){
7342             var msg = this.validator(value);
7343             if(msg !== true){
7344                 this.markInvalid(msg);
7345                 return false;
7346             }
7347         }
7348         if(this.regex && !this.regex.test(value)){
7349             this.markInvalid(this.regexText);
7350             return false;
7351         }
7352         return true;
7353     },
7354
7355     
7356     
7357      // private
7358     fireKey : function(e){
7359         //Roo.log('field ' + e.getKey());
7360         if(e.isNavKeyPress()){
7361             this.fireEvent("specialkey", this, e);
7362         }
7363     },
7364     focus : function (selectText){
7365         if(this.rendered){
7366             this.inputEl().focus();
7367             if(selectText === true){
7368                 this.inputEl().dom.select();
7369             }
7370         }
7371         return this;
7372     } ,
7373     
7374     onFocus : function(){
7375         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7376            // this.el.addClass(this.focusClass);
7377         }
7378         if(!this.hasFocus){
7379             this.hasFocus = true;
7380             this.startValue = this.getValue();
7381             this.fireEvent("focus", this);
7382         }
7383     },
7384     
7385     beforeBlur : Roo.emptyFn,
7386
7387     
7388     // private
7389     onBlur : function(){
7390         this.beforeBlur();
7391         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7392             //this.el.removeClass(this.focusClass);
7393         }
7394         this.hasFocus = false;
7395         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7396             this.validate();
7397         }
7398         var v = this.getValue();
7399         if(String(v) !== String(this.startValue)){
7400             this.fireEvent('change', this, v, this.startValue);
7401         }
7402         this.fireEvent("blur", this);
7403     },
7404     
7405     /**
7406      * Resets the current field value to the originally loaded value and clears any validation messages
7407      */
7408     reset : function(){
7409         this.setValue(this.originalValue);
7410         this.clearInvalid();
7411     },
7412      /**
7413      * Returns the name of the field
7414      * @return {Mixed} name The name field
7415      */
7416     getName: function(){
7417         return this.name;
7418     },
7419      /**
7420      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7421      * @return {Mixed} value The field value
7422      */
7423     getValue : function(){
7424         
7425         var v = this.inputEl().getValue();
7426         
7427         return v;
7428     },
7429     /**
7430      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7431      * @return {Mixed} value The field value
7432      */
7433     getRawValue : function(){
7434         var v = this.inputEl().getValue();
7435         
7436         return v;
7437     },
7438     
7439     /**
7440      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7441      * @param {Mixed} value The value to set
7442      */
7443     setRawValue : function(v){
7444         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7445     },
7446     
7447     selectText : function(start, end){
7448         var v = this.getRawValue();
7449         if(v.length > 0){
7450             start = start === undefined ? 0 : start;
7451             end = end === undefined ? v.length : end;
7452             var d = this.inputEl().dom;
7453             if(d.setSelectionRange){
7454                 d.setSelectionRange(start, end);
7455             }else if(d.createTextRange){
7456                 var range = d.createTextRange();
7457                 range.moveStart("character", start);
7458                 range.moveEnd("character", v.length-end);
7459                 range.select();
7460             }
7461         }
7462     },
7463     
7464     /**
7465      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7466      * @param {Mixed} value The value to set
7467      */
7468     setValue : function(v){
7469         this.value = v;
7470         if(this.rendered){
7471             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7472             this.validate();
7473         }
7474     },
7475     
7476     /*
7477     processValue : function(value){
7478         if(this.stripCharsRe){
7479             var newValue = value.replace(this.stripCharsRe, '');
7480             if(newValue !== value){
7481                 this.setRawValue(newValue);
7482                 return newValue;
7483             }
7484         }
7485         return value;
7486     },
7487   */
7488     preFocus : function(){
7489         
7490         if(this.selectOnFocus){
7491             this.inputEl().dom.select();
7492         }
7493     },
7494     filterKeys : function(e){
7495         var k = e.getKey();
7496         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7497             return;
7498         }
7499         var c = e.getCharCode(), cc = String.fromCharCode(c);
7500         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7501             return;
7502         }
7503         if(!this.maskRe.test(cc)){
7504             e.stopEvent();
7505         }
7506     },
7507      /**
7508      * Clear any invalid styles/messages for this field
7509      */
7510     clearInvalid : function(){
7511         
7512         if(!this.el || this.preventMark){ // not rendered
7513             return;
7514         }
7515         this.el.removeClass(this.invalidClass);
7516         /*
7517         switch(this.msgTarget){
7518             case 'qtip':
7519                 this.el.dom.qtip = '';
7520                 break;
7521             case 'title':
7522                 this.el.dom.title = '';
7523                 break;
7524             case 'under':
7525                 if(this.errorEl){
7526                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7527                 }
7528                 break;
7529             case 'side':
7530                 if(this.errorIcon){
7531                     this.errorIcon.dom.qtip = '';
7532                     this.errorIcon.hide();
7533                     this.un('resize', this.alignErrorIcon, this);
7534                 }
7535                 break;
7536             default:
7537                 var t = Roo.getDom(this.msgTarget);
7538                 t.innerHTML = '';
7539                 t.style.display = 'none';
7540                 break;
7541         }
7542         */
7543         this.fireEvent('valid', this);
7544     },
7545      /**
7546      * Mark this field as invalid
7547      * @param {String} msg The validation message
7548      */
7549     markInvalid : function(msg){
7550         if(!this.el  || this.preventMark){ // not rendered
7551             return;
7552         }
7553         this.el.addClass(this.invalidClass);
7554         /*
7555         msg = msg || this.invalidText;
7556         switch(this.msgTarget){
7557             case 'qtip':
7558                 this.el.dom.qtip = msg;
7559                 this.el.dom.qclass = 'x-form-invalid-tip';
7560                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7561                     Roo.QuickTips.enable();
7562                 }
7563                 break;
7564             case 'title':
7565                 this.el.dom.title = msg;
7566                 break;
7567             case 'under':
7568                 if(!this.errorEl){
7569                     var elp = this.el.findParent('.x-form-element', 5, true);
7570                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7571                     this.errorEl.setWidth(elp.getWidth(true)-20);
7572                 }
7573                 this.errorEl.update(msg);
7574                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7575                 break;
7576             case 'side':
7577                 if(!this.errorIcon){
7578                     var elp = this.el.findParent('.x-form-element', 5, true);
7579                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7580                 }
7581                 this.alignErrorIcon();
7582                 this.errorIcon.dom.qtip = msg;
7583                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7584                 this.errorIcon.show();
7585                 this.on('resize', this.alignErrorIcon, this);
7586                 break;
7587             default:
7588                 var t = Roo.getDom(this.msgTarget);
7589                 t.innerHTML = msg;
7590                 t.style.display = this.msgDisplay;
7591                 break;
7592         }
7593         */
7594         this.fireEvent('invalid', this, msg);
7595     },
7596     // private
7597     SafariOnKeyDown : function(event)
7598     {
7599         // this is a workaround for a password hang bug on chrome/ webkit.
7600         
7601         var isSelectAll = false;
7602         
7603         if(this.inputEl().dom.selectionEnd > 0){
7604             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7605         }
7606         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7607             event.preventDefault();
7608             this.setValue('');
7609             return;
7610         }
7611         
7612         if(isSelectAll){ // backspace and delete key
7613             
7614             event.preventDefault();
7615             // this is very hacky as keydown always get's upper case.
7616             //
7617             var cc = String.fromCharCode(event.getCharCode());
7618             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7619             
7620         }
7621     },
7622     adjustWidth : function(tag, w){
7623         tag = tag.toLowerCase();
7624         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7625             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7626                 if(tag == 'input'){
7627                     return w + 2;
7628                 }
7629                 if(tag == 'textarea'){
7630                     return w-2;
7631                 }
7632             }else if(Roo.isOpera){
7633                 if(tag == 'input'){
7634                     return w + 2;
7635                 }
7636                 if(tag == 'textarea'){
7637                     return w-2;
7638                 }
7639             }
7640         }
7641         return w;
7642     }
7643     
7644 });
7645
7646  
7647 /*
7648  * - LGPL
7649  *
7650  * Input
7651  * 
7652  */
7653
7654 /**
7655  * @class Roo.bootstrap.TextArea
7656  * @extends Roo.bootstrap.Input
7657  * Bootstrap TextArea class
7658  * @cfg {Number} cols Specifies the visible width of a text area
7659  * @cfg {Number} rows Specifies the visible number of lines in a text area
7660  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7661  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7662  * @cfg {string} html text
7663  * 
7664  * @constructor
7665  * Create a new TextArea
7666  * @param {Object} config The config object
7667  */
7668
7669 Roo.bootstrap.TextArea = function(config){
7670     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7671    
7672 };
7673
7674 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7675      
7676     cols : false,
7677     rows : 5,
7678     readOnly : false,
7679     warp : 'soft',
7680     resize : false,
7681     value: false,
7682     html: false,
7683     
7684     getAutoCreate : function(){
7685         
7686         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7687         
7688         var id = Roo.id();
7689         
7690         var cfg = {};
7691         
7692         var input =  {
7693             tag: 'textarea',
7694             id : id,
7695             warp : this.warp,
7696             rows : this.rows,
7697             value : this.value || '',
7698             html: this.html || '',
7699             cls : 'form-control',
7700             placeholder : this.placeholder || '' 
7701             
7702         };
7703         
7704         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7705             input.maxLength = this.maxLength;
7706         }
7707         
7708         if(this.resize){
7709             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7710         }
7711         
7712         if(this.cols){
7713             input.cols = this.cols;
7714         }
7715         
7716         if (this.readOnly) {
7717             input.readonly = true;
7718         }
7719         
7720         if (this.name) {
7721             input.name = this.name;
7722         }
7723         
7724         if (this.size) {
7725             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7726         }
7727         
7728         var settings=this;
7729         ['xs','sm','md','lg'].map(function(size){
7730             if (settings[size]) {
7731                 cfg.cls += ' col-' + size + '-' + settings[size];
7732             }
7733         });
7734         
7735         var inputblock = input;
7736         
7737         if (this.before || this.after) {
7738             
7739             inputblock = {
7740                 cls : 'input-group',
7741                 cn :  [] 
7742             };
7743             if (this.before) {
7744                 inputblock.cn.push({
7745                     tag :'span',
7746                     cls : 'input-group-addon',
7747                     html : this.before
7748                 });
7749             }
7750             inputblock.cn.push(input);
7751             if (this.after) {
7752                 inputblock.cn.push({
7753                     tag :'span',
7754                     cls : 'input-group-addon',
7755                     html : this.after
7756                 });
7757             }
7758             
7759         }
7760         
7761         if (align ==='left' && this.fieldLabel.length) {
7762                 Roo.log("left and has label");
7763                 cfg.cn = [
7764                     
7765                     {
7766                         tag: 'label',
7767                         'for' :  id,
7768                         cls : 'control-label col-sm-' + this.labelWidth,
7769                         html : this.fieldLabel
7770                         
7771                     },
7772                     {
7773                         cls : "col-sm-" + (12 - this.labelWidth), 
7774                         cn: [
7775                             inputblock
7776                         ]
7777                     }
7778                     
7779                 ];
7780         } else if ( this.fieldLabel.length) {
7781                 Roo.log(" label");
7782                  cfg.cn = [
7783                    
7784                     {
7785                         tag: 'label',
7786                         //cls : 'input-group-addon',
7787                         html : this.fieldLabel
7788                         
7789                     },
7790                     
7791                     inputblock
7792                     
7793                 ];
7794
7795         } else {
7796             
7797                    Roo.log(" no label && no align");
7798                 cfg.cn = [
7799                     
7800                         inputblock
7801                     
7802                 ];
7803                 
7804                 
7805         }
7806         
7807         if (this.disabled) {
7808             input.disabled=true;
7809         }
7810         
7811         return cfg;
7812         
7813     },
7814     /**
7815      * return the real textarea element.
7816      */
7817     inputEl: function ()
7818     {
7819         return this.el.select('textarea.form-control',true).first();
7820     }
7821 });
7822
7823  
7824 /*
7825  * - LGPL
7826  *
7827  * trigger field - base class for combo..
7828  * 
7829  */
7830  
7831 /**
7832  * @class Roo.bootstrap.TriggerField
7833  * @extends Roo.bootstrap.Input
7834  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7835  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7836  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7837  * for which you can provide a custom implementation.  For example:
7838  * <pre><code>
7839 var trigger = new Roo.bootstrap.TriggerField();
7840 trigger.onTriggerClick = myTriggerFn;
7841 trigger.applyTo('my-field');
7842 </code></pre>
7843  *
7844  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7845  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7846  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7847  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7848  * @constructor
7849  * Create a new TriggerField.
7850  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7851  * to the base TextField)
7852  */
7853 Roo.bootstrap.TriggerField = function(config){
7854     this.mimicing = false;
7855     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7856 };
7857
7858 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7859     /**
7860      * @cfg {String} triggerClass A CSS class to apply to the trigger
7861      */
7862      /**
7863      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7864      */
7865     hideTrigger:false,
7866
7867     /** @cfg {Boolean} grow @hide */
7868     /** @cfg {Number} growMin @hide */
7869     /** @cfg {Number} growMax @hide */
7870
7871     /**
7872      * @hide 
7873      * @method
7874      */
7875     autoSize: Roo.emptyFn,
7876     // private
7877     monitorTab : true,
7878     // private
7879     deferHeight : true,
7880
7881     
7882     actionMode : 'wrap',
7883     
7884     
7885     
7886     getAutoCreate : function(){
7887        
7888         var align = this.labelAlign || this.parentLabelAlign();
7889         
7890         var id = Roo.id();
7891         
7892         var cfg = {
7893             cls: 'form-group' //input-group
7894         };
7895         
7896         
7897         var input =  {
7898             tag: 'input',
7899             id : id,
7900             type : this.inputType,
7901             cls : 'form-control',
7902             autocomplete: 'off',
7903             placeholder : this.placeholder || '' 
7904             
7905         };
7906         if (this.name) {
7907             input.name = this.name;
7908         }
7909         if (this.size) {
7910             input.cls += ' input-' + this.size;
7911         }
7912         
7913         if (this.disabled) {
7914             input.disabled=true;
7915         }
7916         
7917         var inputblock = input;
7918         
7919         if (this.before || this.after) {
7920             
7921             inputblock = {
7922                 cls : 'input-group',
7923                 cn :  [] 
7924             };
7925             if (this.before) {
7926                 inputblock.cn.push({
7927                     tag :'span',
7928                     cls : 'input-group-addon',
7929                     html : this.before
7930                 });
7931             }
7932             inputblock.cn.push(input);
7933             if (this.after) {
7934                 inputblock.cn.push({
7935                     tag :'span',
7936                     cls : 'input-group-addon',
7937                     html : this.after
7938                 });
7939             }
7940             
7941         };
7942         
7943         var box = {
7944             tag: 'div',
7945             cn: [
7946                 {
7947                     tag: 'input',
7948                     type : 'hidden',
7949                     cls: 'form-hidden-field'
7950                 },
7951                 inputblock
7952             ]
7953             
7954         };
7955         
7956         if(this.multiple){
7957             Roo.log('multiple');
7958             
7959             box = {
7960                 tag: 'div',
7961                 cn: [
7962                     {
7963                         tag: 'input',
7964                         type : 'hidden',
7965                         cls: 'form-hidden-field'
7966                     },
7967                     {
7968                         tag: 'ul',
7969                         cls: 'select2-choices',
7970                         cn:[
7971                             {
7972                                 tag: 'li',
7973                                 cls: 'select2-search-field',
7974                                 cn: [
7975
7976                                     inputblock
7977                                 ]
7978                             }
7979                         ]
7980                     }
7981                 ]
7982             }
7983         };
7984         
7985         var combobox = {
7986             cls: 'select2-container input-group',
7987             cn: [
7988                 box
7989 //                {
7990 //                    tag: 'ul',
7991 //                    cls: 'typeahead typeahead-long dropdown-menu',
7992 //                    style: 'display:none'
7993 //                }
7994             ]
7995         };
7996         
7997         if(!this.multiple && this.showToggleBtn){
7998             combobox.cn.push({
7999                 tag :'span',
8000                 cls : 'input-group-addon btn dropdown-toggle',
8001                 cn : [
8002                     {
8003                         tag: 'span',
8004                         cls: 'caret'
8005                     },
8006                     {
8007                         tag: 'span',
8008                         cls: 'combobox-clear',
8009                         cn  : [
8010                             {
8011                                 tag : 'i',
8012                                 cls: 'icon-remove'
8013                             }
8014                         ]
8015                     }
8016                 ]
8017
8018             })
8019         }
8020         
8021         if(this.multiple){
8022             combobox.cls += ' select2-container-multi';
8023         }
8024         
8025         if (align ==='left' && this.fieldLabel.length) {
8026             
8027                 Roo.log("left and has label");
8028                 cfg.cn = [
8029                     
8030                     {
8031                         tag: 'label',
8032                         'for' :  id,
8033                         cls : 'control-label col-sm-' + this.labelWidth,
8034                         html : this.fieldLabel
8035                         
8036                     },
8037                     {
8038                         cls : "col-sm-" + (12 - this.labelWidth), 
8039                         cn: [
8040                             combobox
8041                         ]
8042                     }
8043                     
8044                 ];
8045         } else if ( this.fieldLabel.length) {
8046                 Roo.log(" label");
8047                  cfg.cn = [
8048                    
8049                     {
8050                         tag: 'label',
8051                         //cls : 'input-group-addon',
8052                         html : this.fieldLabel
8053                         
8054                     },
8055                     
8056                     combobox
8057                     
8058                 ];
8059
8060         } else {
8061             
8062                 Roo.log(" no label && no align");
8063                 cfg = combobox
8064                      
8065                 
8066         }
8067          
8068         var settings=this;
8069         ['xs','sm','md','lg'].map(function(size){
8070             if (settings[size]) {
8071                 cfg.cls += ' col-' + size + '-' + settings[size];
8072             }
8073         });
8074         
8075         return cfg;
8076         
8077     },
8078     
8079     
8080     
8081     // private
8082     onResize : function(w, h){
8083 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8084 //        if(typeof w == 'number'){
8085 //            var x = w - this.trigger.getWidth();
8086 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8087 //            this.trigger.setStyle('left', x+'px');
8088 //        }
8089     },
8090
8091     // private
8092     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8093
8094     // private
8095     getResizeEl : function(){
8096         return this.inputEl();
8097     },
8098
8099     // private
8100     getPositionEl : function(){
8101         return this.inputEl();
8102     },
8103
8104     // private
8105     alignErrorIcon : function(){
8106         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8107     },
8108
8109     // private
8110     initEvents : function(){
8111         
8112         this.createList();
8113         
8114         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8115         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8116         if(!this.multiple && this.showToggleBtn){
8117             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8118             if(this.hideTrigger){
8119                 this.trigger.setDisplayed(false);
8120             }
8121             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8122         }
8123         
8124         if(this.multiple){
8125             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8126         }
8127         
8128         //this.trigger.addClassOnOver('x-form-trigger-over');
8129         //this.trigger.addClassOnClick('x-form-trigger-click');
8130         
8131         //if(!this.width){
8132         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8133         //}
8134     },
8135     
8136     createList : function()
8137     {
8138         this.list = Roo.get(document.body).createChild({
8139             tag: 'ul',
8140             cls: 'typeahead typeahead-long dropdown-menu',
8141             style: 'display:none'
8142         });
8143         
8144         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8145         
8146     },
8147
8148     // private
8149     initTrigger : function(){
8150        
8151     },
8152
8153     // private
8154     onDestroy : function(){
8155         if(this.trigger){
8156             this.trigger.removeAllListeners();
8157           //  this.trigger.remove();
8158         }
8159         //if(this.wrap){
8160         //    this.wrap.remove();
8161         //}
8162         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8163     },
8164
8165     // private
8166     onFocus : function(){
8167         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8168         /*
8169         if(!this.mimicing){
8170             this.wrap.addClass('x-trigger-wrap-focus');
8171             this.mimicing = true;
8172             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8173             if(this.monitorTab){
8174                 this.el.on("keydown", this.checkTab, this);
8175             }
8176         }
8177         */
8178     },
8179
8180     // private
8181     checkTab : function(e){
8182         if(e.getKey() == e.TAB){
8183             this.triggerBlur();
8184         }
8185     },
8186
8187     // private
8188     onBlur : function(){
8189         // do nothing
8190     },
8191
8192     // private
8193     mimicBlur : function(e, t){
8194         /*
8195         if(!this.wrap.contains(t) && this.validateBlur()){
8196             this.triggerBlur();
8197         }
8198         */
8199     },
8200
8201     // private
8202     triggerBlur : function(){
8203         this.mimicing = false;
8204         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8205         if(this.monitorTab){
8206             this.el.un("keydown", this.checkTab, this);
8207         }
8208         //this.wrap.removeClass('x-trigger-wrap-focus');
8209         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8210     },
8211
8212     // private
8213     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8214     validateBlur : function(e, t){
8215         return true;
8216     },
8217
8218     // private
8219     onDisable : function(){
8220         this.inputEl().dom.disabled = true;
8221         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8222         //if(this.wrap){
8223         //    this.wrap.addClass('x-item-disabled');
8224         //}
8225     },
8226
8227     // private
8228     onEnable : function(){
8229         this.inputEl().dom.disabled = false;
8230         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8231         //if(this.wrap){
8232         //    this.el.removeClass('x-item-disabled');
8233         //}
8234     },
8235
8236     // private
8237     onShow : function(){
8238         var ae = this.getActionEl();
8239         
8240         if(ae){
8241             ae.dom.style.display = '';
8242             ae.dom.style.visibility = 'visible';
8243         }
8244     },
8245
8246     // private
8247     
8248     onHide : function(){
8249         var ae = this.getActionEl();
8250         ae.dom.style.display = 'none';
8251     },
8252
8253     /**
8254      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8255      * by an implementing function.
8256      * @method
8257      * @param {EventObject} e
8258      */
8259     onTriggerClick : Roo.emptyFn
8260 });
8261  /*
8262  * Based on:
8263  * Ext JS Library 1.1.1
8264  * Copyright(c) 2006-2007, Ext JS, LLC.
8265  *
8266  * Originally Released Under LGPL - original licence link has changed is not relivant.
8267  *
8268  * Fork - LGPL
8269  * <script type="text/javascript">
8270  */
8271
8272
8273 /**
8274  * @class Roo.data.SortTypes
8275  * @singleton
8276  * Defines the default sorting (casting?) comparison functions used when sorting data.
8277  */
8278 Roo.data.SortTypes = {
8279     /**
8280      * Default sort that does nothing
8281      * @param {Mixed} s The value being converted
8282      * @return {Mixed} The comparison value
8283      */
8284     none : function(s){
8285         return s;
8286     },
8287     
8288     /**
8289      * The regular expression used to strip tags
8290      * @type {RegExp}
8291      * @property
8292      */
8293     stripTagsRE : /<\/?[^>]+>/gi,
8294     
8295     /**
8296      * Strips all HTML tags to sort on text only
8297      * @param {Mixed} s The value being converted
8298      * @return {String} The comparison value
8299      */
8300     asText : function(s){
8301         return String(s).replace(this.stripTagsRE, "");
8302     },
8303     
8304     /**
8305      * Strips all HTML tags to sort on text only - Case insensitive
8306      * @param {Mixed} s The value being converted
8307      * @return {String} The comparison value
8308      */
8309     asUCText : function(s){
8310         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8311     },
8312     
8313     /**
8314      * Case insensitive string
8315      * @param {Mixed} s The value being converted
8316      * @return {String} The comparison value
8317      */
8318     asUCString : function(s) {
8319         return String(s).toUpperCase();
8320     },
8321     
8322     /**
8323      * Date sorting
8324      * @param {Mixed} s The value being converted
8325      * @return {Number} The comparison value
8326      */
8327     asDate : function(s) {
8328         if(!s){
8329             return 0;
8330         }
8331         if(s instanceof Date){
8332             return s.getTime();
8333         }
8334         return Date.parse(String(s));
8335     },
8336     
8337     /**
8338      * Float sorting
8339      * @param {Mixed} s The value being converted
8340      * @return {Float} The comparison value
8341      */
8342     asFloat : function(s) {
8343         var val = parseFloat(String(s).replace(/,/g, ""));
8344         if(isNaN(val)) val = 0;
8345         return val;
8346     },
8347     
8348     /**
8349      * Integer sorting
8350      * @param {Mixed} s The value being converted
8351      * @return {Number} The comparison value
8352      */
8353     asInt : function(s) {
8354         var val = parseInt(String(s).replace(/,/g, ""));
8355         if(isNaN(val)) val = 0;
8356         return val;
8357     }
8358 };/*
8359  * Based on:
8360  * Ext JS Library 1.1.1
8361  * Copyright(c) 2006-2007, Ext JS, LLC.
8362  *
8363  * Originally Released Under LGPL - original licence link has changed is not relivant.
8364  *
8365  * Fork - LGPL
8366  * <script type="text/javascript">
8367  */
8368
8369 /**
8370 * @class Roo.data.Record
8371  * Instances of this class encapsulate both record <em>definition</em> information, and record
8372  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8373  * to access Records cached in an {@link Roo.data.Store} object.<br>
8374  * <p>
8375  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8376  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8377  * objects.<br>
8378  * <p>
8379  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8380  * @constructor
8381  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8382  * {@link #create}. The parameters are the same.
8383  * @param {Array} data An associative Array of data values keyed by the field name.
8384  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8385  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8386  * not specified an integer id is generated.
8387  */
8388 Roo.data.Record = function(data, id){
8389     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8390     this.data = data;
8391 };
8392
8393 /**
8394  * Generate a constructor for a specific record layout.
8395  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8396  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8397  * Each field definition object may contain the following properties: <ul>
8398  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
8399  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8400  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8401  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8402  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8403  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8404  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8405  * this may be omitted.</p></li>
8406  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8407  * <ul><li>auto (Default, implies no conversion)</li>
8408  * <li>string</li>
8409  * <li>int</li>
8410  * <li>float</li>
8411  * <li>boolean</li>
8412  * <li>date</li></ul></p></li>
8413  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8414  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8415  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8416  * by the Reader into an object that will be stored in the Record. It is passed the
8417  * following parameters:<ul>
8418  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8419  * </ul></p></li>
8420  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8421  * </ul>
8422  * <br>usage:<br><pre><code>
8423 var TopicRecord = Roo.data.Record.create(
8424     {name: 'title', mapping: 'topic_title'},
8425     {name: 'author', mapping: 'username'},
8426     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8427     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8428     {name: 'lastPoster', mapping: 'user2'},
8429     {name: 'excerpt', mapping: 'post_text'}
8430 );
8431
8432 var myNewRecord = new TopicRecord({
8433     title: 'Do my job please',
8434     author: 'noobie',
8435     totalPosts: 1,
8436     lastPost: new Date(),
8437     lastPoster: 'Animal',
8438     excerpt: 'No way dude!'
8439 });
8440 myStore.add(myNewRecord);
8441 </code></pre>
8442  * @method create
8443  * @static
8444  */
8445 Roo.data.Record.create = function(o){
8446     var f = function(){
8447         f.superclass.constructor.apply(this, arguments);
8448     };
8449     Roo.extend(f, Roo.data.Record);
8450     var p = f.prototype;
8451     p.fields = new Roo.util.MixedCollection(false, function(field){
8452         return field.name;
8453     });
8454     for(var i = 0, len = o.length; i < len; i++){
8455         p.fields.add(new Roo.data.Field(o[i]));
8456     }
8457     f.getField = function(name){
8458         return p.fields.get(name);  
8459     };
8460     return f;
8461 };
8462
8463 Roo.data.Record.AUTO_ID = 1000;
8464 Roo.data.Record.EDIT = 'edit';
8465 Roo.data.Record.REJECT = 'reject';
8466 Roo.data.Record.COMMIT = 'commit';
8467
8468 Roo.data.Record.prototype = {
8469     /**
8470      * Readonly flag - true if this record has been modified.
8471      * @type Boolean
8472      */
8473     dirty : false,
8474     editing : false,
8475     error: null,
8476     modified: null,
8477
8478     // private
8479     join : function(store){
8480         this.store = store;
8481     },
8482
8483     /**
8484      * Set the named field to the specified value.
8485      * @param {String} name The name of the field to set.
8486      * @param {Object} value The value to set the field to.
8487      */
8488     set : function(name, value){
8489         if(this.data[name] == value){
8490             return;
8491         }
8492         this.dirty = true;
8493         if(!this.modified){
8494             this.modified = {};
8495         }
8496         if(typeof this.modified[name] == 'undefined'){
8497             this.modified[name] = this.data[name];
8498         }
8499         this.data[name] = value;
8500         if(!this.editing && this.store){
8501             this.store.afterEdit(this);
8502         }       
8503     },
8504
8505     /**
8506      * Get the value of the named field.
8507      * @param {String} name The name of the field to get the value of.
8508      * @return {Object} The value of the field.
8509      */
8510     get : function(name){
8511         return this.data[name]; 
8512     },
8513
8514     // private
8515     beginEdit : function(){
8516         this.editing = true;
8517         this.modified = {}; 
8518     },
8519
8520     // private
8521     cancelEdit : function(){
8522         this.editing = false;
8523         delete this.modified;
8524     },
8525
8526     // private
8527     endEdit : function(){
8528         this.editing = false;
8529         if(this.dirty && this.store){
8530             this.store.afterEdit(this);
8531         }
8532     },
8533
8534     /**
8535      * Usually called by the {@link Roo.data.Store} which owns the Record.
8536      * Rejects all changes made to the Record since either creation, or the last commit operation.
8537      * Modified fields are reverted to their original values.
8538      * <p>
8539      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8540      * of reject operations.
8541      */
8542     reject : function(){
8543         var m = this.modified;
8544         for(var n in m){
8545             if(typeof m[n] != "function"){
8546                 this.data[n] = m[n];
8547             }
8548         }
8549         this.dirty = false;
8550         delete this.modified;
8551         this.editing = false;
8552         if(this.store){
8553             this.store.afterReject(this);
8554         }
8555     },
8556
8557     /**
8558      * Usually called by the {@link Roo.data.Store} which owns the Record.
8559      * Commits all changes made to the Record since either creation, or the last commit operation.
8560      * <p>
8561      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8562      * of commit operations.
8563      */
8564     commit : function(){
8565         this.dirty = false;
8566         delete this.modified;
8567         this.editing = false;
8568         if(this.store){
8569             this.store.afterCommit(this);
8570         }
8571     },
8572
8573     // private
8574     hasError : function(){
8575         return this.error != null;
8576     },
8577
8578     // private
8579     clearError : function(){
8580         this.error = null;
8581     },
8582
8583     /**
8584      * Creates a copy of this record.
8585      * @param {String} id (optional) A new record id if you don't want to use this record's id
8586      * @return {Record}
8587      */
8588     copy : function(newId) {
8589         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8590     }
8591 };/*
8592  * Based on:
8593  * Ext JS Library 1.1.1
8594  * Copyright(c) 2006-2007, Ext JS, LLC.
8595  *
8596  * Originally Released Under LGPL - original licence link has changed is not relivant.
8597  *
8598  * Fork - LGPL
8599  * <script type="text/javascript">
8600  */
8601
8602
8603
8604 /**
8605  * @class Roo.data.Store
8606  * @extends Roo.util.Observable
8607  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8608  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8609  * <p>
8610  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
8611  * has no knowledge of the format of the data returned by the Proxy.<br>
8612  * <p>
8613  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8614  * instances from the data object. These records are cached and made available through accessor functions.
8615  * @constructor
8616  * Creates a new Store.
8617  * @param {Object} config A config object containing the objects needed for the Store to access data,
8618  * and read the data into Records.
8619  */
8620 Roo.data.Store = function(config){
8621     this.data = new Roo.util.MixedCollection(false);
8622     this.data.getKey = function(o){
8623         return o.id;
8624     };
8625     this.baseParams = {};
8626     // private
8627     this.paramNames = {
8628         "start" : "start",
8629         "limit" : "limit",
8630         "sort" : "sort",
8631         "dir" : "dir",
8632         "multisort" : "_multisort"
8633     };
8634
8635     if(config && config.data){
8636         this.inlineData = config.data;
8637         delete config.data;
8638     }
8639
8640     Roo.apply(this, config);
8641     
8642     if(this.reader){ // reader passed
8643         this.reader = Roo.factory(this.reader, Roo.data);
8644         this.reader.xmodule = this.xmodule || false;
8645         if(!this.recordType){
8646             this.recordType = this.reader.recordType;
8647         }
8648         if(this.reader.onMetaChange){
8649             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8650         }
8651     }
8652
8653     if(this.recordType){
8654         this.fields = this.recordType.prototype.fields;
8655     }
8656     this.modified = [];
8657
8658     this.addEvents({
8659         /**
8660          * @event datachanged
8661          * Fires when the data cache has changed, and a widget which is using this Store
8662          * as a Record cache should refresh its view.
8663          * @param {Store} this
8664          */
8665         datachanged : true,
8666         /**
8667          * @event metachange
8668          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8669          * @param {Store} this
8670          * @param {Object} meta The JSON metadata
8671          */
8672         metachange : true,
8673         /**
8674          * @event add
8675          * Fires when Records have been added to the Store
8676          * @param {Store} this
8677          * @param {Roo.data.Record[]} records The array of Records added
8678          * @param {Number} index The index at which the record(s) were added
8679          */
8680         add : true,
8681         /**
8682          * @event remove
8683          * Fires when a Record has been removed from the Store
8684          * @param {Store} this
8685          * @param {Roo.data.Record} record The Record that was removed
8686          * @param {Number} index The index at which the record was removed
8687          */
8688         remove : true,
8689         /**
8690          * @event update
8691          * Fires when a Record has been updated
8692          * @param {Store} this
8693          * @param {Roo.data.Record} record The Record that was updated
8694          * @param {String} operation The update operation being performed.  Value may be one of:
8695          * <pre><code>
8696  Roo.data.Record.EDIT
8697  Roo.data.Record.REJECT
8698  Roo.data.Record.COMMIT
8699          * </code></pre>
8700          */
8701         update : true,
8702         /**
8703          * @event clear
8704          * Fires when the data cache has been cleared.
8705          * @param {Store} this
8706          */
8707         clear : true,
8708         /**
8709          * @event beforeload
8710          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8711          * the load action will be canceled.
8712          * @param {Store} this
8713          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8714          */
8715         beforeload : true,
8716         /**
8717          * @event beforeloadadd
8718          * Fires after a new set of Records has been loaded.
8719          * @param {Store} this
8720          * @param {Roo.data.Record[]} records The Records that were loaded
8721          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8722          */
8723         beforeloadadd : true,
8724         /**
8725          * @event load
8726          * Fires after a new set of Records has been loaded, before they are added to the store.
8727          * @param {Store} this
8728          * @param {Roo.data.Record[]} records The Records that were loaded
8729          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8730          * @params {Object} return from reader
8731          */
8732         load : true,
8733         /**
8734          * @event loadexception
8735          * Fires if an exception occurs in the Proxy during loading.
8736          * Called with the signature of the Proxy's "loadexception" event.
8737          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8738          * 
8739          * @param {Proxy} 
8740          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8741          * @param {Object} load options 
8742          * @param {Object} jsonData from your request (normally this contains the Exception)
8743          */
8744         loadexception : true
8745     });
8746     
8747     if(this.proxy){
8748         this.proxy = Roo.factory(this.proxy, Roo.data);
8749         this.proxy.xmodule = this.xmodule || false;
8750         this.relayEvents(this.proxy,  ["loadexception"]);
8751     }
8752     this.sortToggle = {};
8753     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8754
8755     Roo.data.Store.superclass.constructor.call(this);
8756
8757     if(this.inlineData){
8758         this.loadData(this.inlineData);
8759         delete this.inlineData;
8760     }
8761 };
8762
8763 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8764      /**
8765     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8766     * without a remote query - used by combo/forms at present.
8767     */
8768     
8769     /**
8770     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8771     */
8772     /**
8773     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8774     */
8775     /**
8776     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8777     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8778     */
8779     /**
8780     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8781     * on any HTTP request
8782     */
8783     /**
8784     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8785     */
8786     /**
8787     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8788     */
8789     multiSort: false,
8790     /**
8791     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8792     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8793     */
8794     remoteSort : false,
8795
8796     /**
8797     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8798      * loaded or when a record is removed. (defaults to false).
8799     */
8800     pruneModifiedRecords : false,
8801
8802     // private
8803     lastOptions : null,
8804
8805     /**
8806      * Add Records to the Store and fires the add event.
8807      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8808      */
8809     add : function(records){
8810         records = [].concat(records);
8811         for(var i = 0, len = records.length; i < len; i++){
8812             records[i].join(this);
8813         }
8814         var index = this.data.length;
8815         this.data.addAll(records);
8816         this.fireEvent("add", this, records, index);
8817     },
8818
8819     /**
8820      * Remove a Record from the Store and fires the remove event.
8821      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8822      */
8823     remove : function(record){
8824         var index = this.data.indexOf(record);
8825         this.data.removeAt(index);
8826         if(this.pruneModifiedRecords){
8827             this.modified.remove(record);
8828         }
8829         this.fireEvent("remove", this, record, index);
8830     },
8831
8832     /**
8833      * Remove all Records from the Store and fires the clear event.
8834      */
8835     removeAll : function(){
8836         this.data.clear();
8837         if(this.pruneModifiedRecords){
8838             this.modified = [];
8839         }
8840         this.fireEvent("clear", this);
8841     },
8842
8843     /**
8844      * Inserts Records to the Store at the given index and fires the add event.
8845      * @param {Number} index The start index at which to insert the passed Records.
8846      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8847      */
8848     insert : function(index, records){
8849         records = [].concat(records);
8850         for(var i = 0, len = records.length; i < len; i++){
8851             this.data.insert(index, records[i]);
8852             records[i].join(this);
8853         }
8854         this.fireEvent("add", this, records, index);
8855     },
8856
8857     /**
8858      * Get the index within the cache of the passed Record.
8859      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8860      * @return {Number} The index of the passed Record. Returns -1 if not found.
8861      */
8862     indexOf : function(record){
8863         return this.data.indexOf(record);
8864     },
8865
8866     /**
8867      * Get the index within the cache of the Record with the passed id.
8868      * @param {String} id The id of the Record to find.
8869      * @return {Number} The index of the Record. Returns -1 if not found.
8870      */
8871     indexOfId : function(id){
8872         return this.data.indexOfKey(id);
8873     },
8874
8875     /**
8876      * Get the Record with the specified id.
8877      * @param {String} id The id of the Record to find.
8878      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8879      */
8880     getById : function(id){
8881         return this.data.key(id);
8882     },
8883
8884     /**
8885      * Get the Record at the specified index.
8886      * @param {Number} index The index of the Record to find.
8887      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8888      */
8889     getAt : function(index){
8890         return this.data.itemAt(index);
8891     },
8892
8893     /**
8894      * Returns a range of Records between specified indices.
8895      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8896      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8897      * @return {Roo.data.Record[]} An array of Records
8898      */
8899     getRange : function(start, end){
8900         return this.data.getRange(start, end);
8901     },
8902
8903     // private
8904     storeOptions : function(o){
8905         o = Roo.apply({}, o);
8906         delete o.callback;
8907         delete o.scope;
8908         this.lastOptions = o;
8909     },
8910
8911     /**
8912      * Loads the Record cache from the configured Proxy using the configured Reader.
8913      * <p>
8914      * If using remote paging, then the first load call must specify the <em>start</em>
8915      * and <em>limit</em> properties in the options.params property to establish the initial
8916      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8917      * <p>
8918      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8919      * and this call will return before the new data has been loaded. Perform any post-processing
8920      * in a callback function, or in a "load" event handler.</strong>
8921      * <p>
8922      * @param {Object} options An object containing properties which control loading options:<ul>
8923      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8924      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8925      * passed the following arguments:<ul>
8926      * <li>r : Roo.data.Record[]</li>
8927      * <li>options: Options object from the load call</li>
8928      * <li>success: Boolean success indicator</li></ul></li>
8929      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8930      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8931      * </ul>
8932      */
8933     load : function(options){
8934         options = options || {};
8935         if(this.fireEvent("beforeload", this, options) !== false){
8936             this.storeOptions(options);
8937             var p = Roo.apply(options.params || {}, this.baseParams);
8938             // if meta was not loaded from remote source.. try requesting it.
8939             if (!this.reader.metaFromRemote) {
8940                 p._requestMeta = 1;
8941             }
8942             if(this.sortInfo && this.remoteSort){
8943                 var pn = this.paramNames;
8944                 p[pn["sort"]] = this.sortInfo.field;
8945                 p[pn["dir"]] = this.sortInfo.direction;
8946             }
8947             if (this.multiSort) {
8948                 var pn = this.paramNames;
8949                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8950             }
8951             
8952             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8953         }
8954     },
8955
8956     /**
8957      * Reloads the Record cache from the configured Proxy using the configured Reader and
8958      * the options from the last load operation performed.
8959      * @param {Object} options (optional) An object containing properties which may override the options
8960      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8961      * the most recently used options are reused).
8962      */
8963     reload : function(options){
8964         this.load(Roo.applyIf(options||{}, this.lastOptions));
8965     },
8966
8967     // private
8968     // Called as a callback by the Reader during a load operation.
8969     loadRecords : function(o, options, success){
8970         if(!o || success === false){
8971             if(success !== false){
8972                 this.fireEvent("load", this, [], options, o);
8973             }
8974             if(options.callback){
8975                 options.callback.call(options.scope || this, [], options, false);
8976             }
8977             return;
8978         }
8979         // if data returned failure - throw an exception.
8980         if (o.success === false) {
8981             // show a message if no listener is registered.
8982             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8983                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8984             }
8985             // loadmask wil be hooked into this..
8986             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8987             return;
8988         }
8989         var r = o.records, t = o.totalRecords || r.length;
8990         
8991         this.fireEvent("beforeloadadd", this, r, options, o);
8992         
8993         if(!options || options.add !== true){
8994             if(this.pruneModifiedRecords){
8995                 this.modified = [];
8996             }
8997             for(var i = 0, len = r.length; i < len; i++){
8998                 r[i].join(this);
8999             }
9000             if(this.snapshot){
9001                 this.data = this.snapshot;
9002                 delete this.snapshot;
9003             }
9004             this.data.clear();
9005             this.data.addAll(r);
9006             this.totalLength = t;
9007             this.applySort();
9008             this.fireEvent("datachanged", this);
9009         }else{
9010             this.totalLength = Math.max(t, this.data.length+r.length);
9011             this.add(r);
9012         }
9013         this.fireEvent("load", this, r, options, o);
9014         if(options.callback){
9015             options.callback.call(options.scope || this, r, options, true);
9016         }
9017     },
9018
9019
9020     /**
9021      * Loads data from a passed data block. A Reader which understands the format of the data
9022      * must have been configured in the constructor.
9023      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9024      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9025      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9026      */
9027     loadData : function(o, append){
9028         var r = this.reader.readRecords(o);
9029         this.loadRecords(r, {add: append}, true);
9030     },
9031
9032     /**
9033      * Gets the number of cached records.
9034      * <p>
9035      * <em>If using paging, this may not be the total size of the dataset. If the data object
9036      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9037      * the data set size</em>
9038      */
9039     getCount : function(){
9040         return this.data.length || 0;
9041     },
9042
9043     /**
9044      * Gets the total number of records in the dataset as returned by the server.
9045      * <p>
9046      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9047      * the dataset size</em>
9048      */
9049     getTotalCount : function(){
9050         return this.totalLength || 0;
9051     },
9052
9053     /**
9054      * Returns the sort state of the Store as an object with two properties:
9055      * <pre><code>
9056  field {String} The name of the field by which the Records are sorted
9057  direction {String} The sort order, "ASC" or "DESC"
9058      * </code></pre>
9059      */
9060     getSortState : function(){
9061         return this.sortInfo;
9062     },
9063
9064     // private
9065     applySort : function(){
9066         if(this.sortInfo && !this.remoteSort){
9067             var s = this.sortInfo, f = s.field;
9068             var st = this.fields.get(f).sortType;
9069             var fn = function(r1, r2){
9070                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9071                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9072             };
9073             this.data.sort(s.direction, fn);
9074             if(this.snapshot && this.snapshot != this.data){
9075                 this.snapshot.sort(s.direction, fn);
9076             }
9077         }
9078     },
9079
9080     /**
9081      * Sets the default sort column and order to be used by the next load operation.
9082      * @param {String} fieldName The name of the field to sort by.
9083      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9084      */
9085     setDefaultSort : function(field, dir){
9086         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9087     },
9088
9089     /**
9090      * Sort the Records.
9091      * If remote sorting is used, the sort is performed on the server, and the cache is
9092      * reloaded. If local sorting is used, the cache is sorted internally.
9093      * @param {String} fieldName The name of the field to sort by.
9094      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9095      */
9096     sort : function(fieldName, dir){
9097         var f = this.fields.get(fieldName);
9098         if(!dir){
9099             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9100             
9101             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9102                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9103             }else{
9104                 dir = f.sortDir;
9105             }
9106         }
9107         this.sortToggle[f.name] = dir;
9108         this.sortInfo = {field: f.name, direction: dir};
9109         if(!this.remoteSort){
9110             this.applySort();
9111             this.fireEvent("datachanged", this);
9112         }else{
9113             this.load(this.lastOptions);
9114         }
9115     },
9116
9117     /**
9118      * Calls the specified function for each of the Records in the cache.
9119      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9120      * Returning <em>false</em> aborts and exits the iteration.
9121      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9122      */
9123     each : function(fn, scope){
9124         this.data.each(fn, scope);
9125     },
9126
9127     /**
9128      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9129      * (e.g., during paging).
9130      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9131      */
9132     getModifiedRecords : function(){
9133         return this.modified;
9134     },
9135
9136     // private
9137     createFilterFn : function(property, value, anyMatch){
9138         if(!value.exec){ // not a regex
9139             value = String(value);
9140             if(value.length == 0){
9141                 return false;
9142             }
9143             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9144         }
9145         return function(r){
9146             return value.test(r.data[property]);
9147         };
9148     },
9149
9150     /**
9151      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9152      * @param {String} property A field on your records
9153      * @param {Number} start The record index to start at (defaults to 0)
9154      * @param {Number} end The last record index to include (defaults to length - 1)
9155      * @return {Number} The sum
9156      */
9157     sum : function(property, start, end){
9158         var rs = this.data.items, v = 0;
9159         start = start || 0;
9160         end = (end || end === 0) ? end : rs.length-1;
9161
9162         for(var i = start; i <= end; i++){
9163             v += (rs[i].data[property] || 0);
9164         }
9165         return v;
9166     },
9167
9168     /**
9169      * Filter the records by a specified property.
9170      * @param {String} field A field on your records
9171      * @param {String/RegExp} value Either a string that the field
9172      * should start with or a RegExp to test against the field
9173      * @param {Boolean} anyMatch True to match any part not just the beginning
9174      */
9175     filter : function(property, value, anyMatch){
9176         var fn = this.createFilterFn(property, value, anyMatch);
9177         return fn ? this.filterBy(fn) : this.clearFilter();
9178     },
9179
9180     /**
9181      * Filter by a function. The specified function will be called with each
9182      * record in this data source. If the function returns true the record is included,
9183      * otherwise it is filtered.
9184      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9185      * @param {Object} scope (optional) The scope of the function (defaults to this)
9186      */
9187     filterBy : function(fn, scope){
9188         this.snapshot = this.snapshot || this.data;
9189         this.data = this.queryBy(fn, scope||this);
9190         this.fireEvent("datachanged", this);
9191     },
9192
9193     /**
9194      * Query the records by a specified property.
9195      * @param {String} field A field on your records
9196      * @param {String/RegExp} value Either a string that the field
9197      * should start with or a RegExp to test against the field
9198      * @param {Boolean} anyMatch True to match any part not just the beginning
9199      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9200      */
9201     query : function(property, value, anyMatch){
9202         var fn = this.createFilterFn(property, value, anyMatch);
9203         return fn ? this.queryBy(fn) : this.data.clone();
9204     },
9205
9206     /**
9207      * Query by a function. The specified function will be called with each
9208      * record in this data source. If the function returns true the record is included
9209      * in the results.
9210      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9211      * @param {Object} scope (optional) The scope of the function (defaults to this)
9212       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9213      **/
9214     queryBy : function(fn, scope){
9215         var data = this.snapshot || this.data;
9216         return data.filterBy(fn, scope||this);
9217     },
9218
9219     /**
9220      * Collects unique values for a particular dataIndex from this store.
9221      * @param {String} dataIndex The property to collect
9222      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9223      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9224      * @return {Array} An array of the unique values
9225      **/
9226     collect : function(dataIndex, allowNull, bypassFilter){
9227         var d = (bypassFilter === true && this.snapshot) ?
9228                 this.snapshot.items : this.data.items;
9229         var v, sv, r = [], l = {};
9230         for(var i = 0, len = d.length; i < len; i++){
9231             v = d[i].data[dataIndex];
9232             sv = String(v);
9233             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9234                 l[sv] = true;
9235                 r[r.length] = v;
9236             }
9237         }
9238         return r;
9239     },
9240
9241     /**
9242      * Revert to a view of the Record cache with no filtering applied.
9243      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9244      */
9245     clearFilter : function(suppressEvent){
9246         if(this.snapshot && this.snapshot != this.data){
9247             this.data = this.snapshot;
9248             delete this.snapshot;
9249             if(suppressEvent !== true){
9250                 this.fireEvent("datachanged", this);
9251             }
9252         }
9253     },
9254
9255     // private
9256     afterEdit : function(record){
9257         if(this.modified.indexOf(record) == -1){
9258             this.modified.push(record);
9259         }
9260         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9261     },
9262     
9263     // private
9264     afterReject : function(record){
9265         this.modified.remove(record);
9266         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9267     },
9268
9269     // private
9270     afterCommit : function(record){
9271         this.modified.remove(record);
9272         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9273     },
9274
9275     /**
9276      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9277      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9278      */
9279     commitChanges : function(){
9280         var m = this.modified.slice(0);
9281         this.modified = [];
9282         for(var i = 0, len = m.length; i < len; i++){
9283             m[i].commit();
9284         }
9285     },
9286
9287     /**
9288      * Cancel outstanding changes on all changed records.
9289      */
9290     rejectChanges : function(){
9291         var m = this.modified.slice(0);
9292         this.modified = [];
9293         for(var i = 0, len = m.length; i < len; i++){
9294             m[i].reject();
9295         }
9296     },
9297
9298     onMetaChange : function(meta, rtype, o){
9299         this.recordType = rtype;
9300         this.fields = rtype.prototype.fields;
9301         delete this.snapshot;
9302         this.sortInfo = meta.sortInfo || this.sortInfo;
9303         this.modified = [];
9304         this.fireEvent('metachange', this, this.reader.meta);
9305     },
9306     
9307     moveIndex : function(data, type)
9308     {
9309         var index = this.indexOf(data);
9310         
9311         var newIndex = index + type;
9312         
9313         this.remove(data);
9314         
9315         this.insert(newIndex, data);
9316         
9317     }
9318 });/*
9319  * Based on:
9320  * Ext JS Library 1.1.1
9321  * Copyright(c) 2006-2007, Ext JS, LLC.
9322  *
9323  * Originally Released Under LGPL - original licence link has changed is not relivant.
9324  *
9325  * Fork - LGPL
9326  * <script type="text/javascript">
9327  */
9328
9329 /**
9330  * @class Roo.data.SimpleStore
9331  * @extends Roo.data.Store
9332  * Small helper class to make creating Stores from Array data easier.
9333  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9334  * @cfg {Array} fields An array of field definition objects, or field name strings.
9335  * @cfg {Array} data The multi-dimensional array of data
9336  * @constructor
9337  * @param {Object} config
9338  */
9339 Roo.data.SimpleStore = function(config){
9340     Roo.data.SimpleStore.superclass.constructor.call(this, {
9341         isLocal : true,
9342         reader: new Roo.data.ArrayReader({
9343                 id: config.id
9344             },
9345             Roo.data.Record.create(config.fields)
9346         ),
9347         proxy : new Roo.data.MemoryProxy(config.data)
9348     });
9349     this.load();
9350 };
9351 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9352  * Based on:
9353  * Ext JS Library 1.1.1
9354  * Copyright(c) 2006-2007, Ext JS, LLC.
9355  *
9356  * Originally Released Under LGPL - original licence link has changed is not relivant.
9357  *
9358  * Fork - LGPL
9359  * <script type="text/javascript">
9360  */
9361
9362 /**
9363 /**
9364  * @extends Roo.data.Store
9365  * @class Roo.data.JsonStore
9366  * Small helper class to make creating Stores for JSON data easier. <br/>
9367 <pre><code>
9368 var store = new Roo.data.JsonStore({
9369     url: 'get-images.php',
9370     root: 'images',
9371     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9372 });
9373 </code></pre>
9374  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9375  * JsonReader and HttpProxy (unless inline data is provided).</b>
9376  * @cfg {Array} fields An array of field definition objects, or field name strings.
9377  * @constructor
9378  * @param {Object} config
9379  */
9380 Roo.data.JsonStore = function(c){
9381     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9382         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9383         reader: new Roo.data.JsonReader(c, c.fields)
9384     }));
9385 };
9386 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9387  * Based on:
9388  * Ext JS Library 1.1.1
9389  * Copyright(c) 2006-2007, Ext JS, LLC.
9390  *
9391  * Originally Released Under LGPL - original licence link has changed is not relivant.
9392  *
9393  * Fork - LGPL
9394  * <script type="text/javascript">
9395  */
9396
9397  
9398 Roo.data.Field = function(config){
9399     if(typeof config == "string"){
9400         config = {name: config};
9401     }
9402     Roo.apply(this, config);
9403     
9404     if(!this.type){
9405         this.type = "auto";
9406     }
9407     
9408     var st = Roo.data.SortTypes;
9409     // named sortTypes are supported, here we look them up
9410     if(typeof this.sortType == "string"){
9411         this.sortType = st[this.sortType];
9412     }
9413     
9414     // set default sortType for strings and dates
9415     if(!this.sortType){
9416         switch(this.type){
9417             case "string":
9418                 this.sortType = st.asUCString;
9419                 break;
9420             case "date":
9421                 this.sortType = st.asDate;
9422                 break;
9423             default:
9424                 this.sortType = st.none;
9425         }
9426     }
9427
9428     // define once
9429     var stripRe = /[\$,%]/g;
9430
9431     // prebuilt conversion function for this field, instead of
9432     // switching every time we're reading a value
9433     if(!this.convert){
9434         var cv, dateFormat = this.dateFormat;
9435         switch(this.type){
9436             case "":
9437             case "auto":
9438             case undefined:
9439                 cv = function(v){ return v; };
9440                 break;
9441             case "string":
9442                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9443                 break;
9444             case "int":
9445                 cv = function(v){
9446                     return v !== undefined && v !== null && v !== '' ?
9447                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9448                     };
9449                 break;
9450             case "float":
9451                 cv = function(v){
9452                     return v !== undefined && v !== null && v !== '' ?
9453                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9454                     };
9455                 break;
9456             case "bool":
9457             case "boolean":
9458                 cv = function(v){ return v === true || v === "true" || v == 1; };
9459                 break;
9460             case "date":
9461                 cv = function(v){
9462                     if(!v){
9463                         return '';
9464                     }
9465                     if(v instanceof Date){
9466                         return v;
9467                     }
9468                     if(dateFormat){
9469                         if(dateFormat == "timestamp"){
9470                             return new Date(v*1000);
9471                         }
9472                         return Date.parseDate(v, dateFormat);
9473                     }
9474                     var parsed = Date.parse(v);
9475                     return parsed ? new Date(parsed) : null;
9476                 };
9477              break;
9478             
9479         }
9480         this.convert = cv;
9481     }
9482 };
9483
9484 Roo.data.Field.prototype = {
9485     dateFormat: null,
9486     defaultValue: "",
9487     mapping: null,
9488     sortType : null,
9489     sortDir : "ASC"
9490 };/*
9491  * Based on:
9492  * Ext JS Library 1.1.1
9493  * Copyright(c) 2006-2007, Ext JS, LLC.
9494  *
9495  * Originally Released Under LGPL - original licence link has changed is not relivant.
9496  *
9497  * Fork - LGPL
9498  * <script type="text/javascript">
9499  */
9500  
9501 // Base class for reading structured data from a data source.  This class is intended to be
9502 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9503
9504 /**
9505  * @class Roo.data.DataReader
9506  * Base class for reading structured data from a data source.  This class is intended to be
9507  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9508  */
9509
9510 Roo.data.DataReader = function(meta, recordType){
9511     
9512     this.meta = meta;
9513     
9514     this.recordType = recordType instanceof Array ? 
9515         Roo.data.Record.create(recordType) : recordType;
9516 };
9517
9518 Roo.data.DataReader.prototype = {
9519      /**
9520      * Create an empty record
9521      * @param {Object} data (optional) - overlay some values
9522      * @return {Roo.data.Record} record created.
9523      */
9524     newRow :  function(d) {
9525         var da =  {};
9526         this.recordType.prototype.fields.each(function(c) {
9527             switch( c.type) {
9528                 case 'int' : da[c.name] = 0; break;
9529                 case 'date' : da[c.name] = new Date(); break;
9530                 case 'float' : da[c.name] = 0.0; break;
9531                 case 'boolean' : da[c.name] = false; break;
9532                 default : da[c.name] = ""; break;
9533             }
9534             
9535         });
9536         return new this.recordType(Roo.apply(da, d));
9537     }
9538     
9539 };/*
9540  * Based on:
9541  * Ext JS Library 1.1.1
9542  * Copyright(c) 2006-2007, Ext JS, LLC.
9543  *
9544  * Originally Released Under LGPL - original licence link has changed is not relivant.
9545  *
9546  * Fork - LGPL
9547  * <script type="text/javascript">
9548  */
9549
9550 /**
9551  * @class Roo.data.DataProxy
9552  * @extends Roo.data.Observable
9553  * This class is an abstract base class for implementations which provide retrieval of
9554  * unformatted data objects.<br>
9555  * <p>
9556  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9557  * (of the appropriate type which knows how to parse the data object) to provide a block of
9558  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9559  * <p>
9560  * Custom implementations must implement the load method as described in
9561  * {@link Roo.data.HttpProxy#load}.
9562  */
9563 Roo.data.DataProxy = function(){
9564     this.addEvents({
9565         /**
9566          * @event beforeload
9567          * Fires before a network request is made to retrieve a data object.
9568          * @param {Object} This DataProxy object.
9569          * @param {Object} params The params parameter to the load function.
9570          */
9571         beforeload : true,
9572         /**
9573          * @event load
9574          * Fires before the load method's callback is called.
9575          * @param {Object} This DataProxy object.
9576          * @param {Object} o The data object.
9577          * @param {Object} arg The callback argument object passed to the load function.
9578          */
9579         load : true,
9580         /**
9581          * @event loadexception
9582          * Fires if an Exception occurs during data retrieval.
9583          * @param {Object} This DataProxy object.
9584          * @param {Object} o The data object.
9585          * @param {Object} arg The callback argument object passed to the load function.
9586          * @param {Object} e The Exception.
9587          */
9588         loadexception : true
9589     });
9590     Roo.data.DataProxy.superclass.constructor.call(this);
9591 };
9592
9593 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9594
9595     /**
9596      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9597      */
9598 /*
9599  * Based on:
9600  * Ext JS Library 1.1.1
9601  * Copyright(c) 2006-2007, Ext JS, LLC.
9602  *
9603  * Originally Released Under LGPL - original licence link has changed is not relivant.
9604  *
9605  * Fork - LGPL
9606  * <script type="text/javascript">
9607  */
9608 /**
9609  * @class Roo.data.MemoryProxy
9610  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9611  * to the Reader when its load method is called.
9612  * @constructor
9613  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9614  */
9615 Roo.data.MemoryProxy = function(data){
9616     if (data.data) {
9617         data = data.data;
9618     }
9619     Roo.data.MemoryProxy.superclass.constructor.call(this);
9620     this.data = data;
9621 };
9622
9623 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9624     /**
9625      * Load data from the requested source (in this case an in-memory
9626      * data object passed to the constructor), read the data object into
9627      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9628      * process that block using the passed callback.
9629      * @param {Object} params This parameter is not used by the MemoryProxy class.
9630      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9631      * object into a block of Roo.data.Records.
9632      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9633      * The function must be passed <ul>
9634      * <li>The Record block object</li>
9635      * <li>The "arg" argument from the load function</li>
9636      * <li>A boolean success indicator</li>
9637      * </ul>
9638      * @param {Object} scope The scope in which to call the callback
9639      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9640      */
9641     load : function(params, reader, callback, scope, arg){
9642         params = params || {};
9643         var result;
9644         try {
9645             result = reader.readRecords(this.data);
9646         }catch(e){
9647             this.fireEvent("loadexception", this, arg, null, e);
9648             callback.call(scope, null, arg, false);
9649             return;
9650         }
9651         callback.call(scope, result, arg, true);
9652     },
9653     
9654     // private
9655     update : function(params, records){
9656         
9657     }
9658 });/*
9659  * Based on:
9660  * Ext JS Library 1.1.1
9661  * Copyright(c) 2006-2007, Ext JS, LLC.
9662  *
9663  * Originally Released Under LGPL - original licence link has changed is not relivant.
9664  *
9665  * Fork - LGPL
9666  * <script type="text/javascript">
9667  */
9668 /**
9669  * @class Roo.data.HttpProxy
9670  * @extends Roo.data.DataProxy
9671  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9672  * configured to reference a certain URL.<br><br>
9673  * <p>
9674  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9675  * from which the running page was served.<br><br>
9676  * <p>
9677  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9678  * <p>
9679  * Be aware that to enable the browser to parse an XML document, the server must set
9680  * the Content-Type header in the HTTP response to "text/xml".
9681  * @constructor
9682  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9683  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9684  * will be used to make the request.
9685  */
9686 Roo.data.HttpProxy = function(conn){
9687     Roo.data.HttpProxy.superclass.constructor.call(this);
9688     // is conn a conn config or a real conn?
9689     this.conn = conn;
9690     this.useAjax = !conn || !conn.events;
9691   
9692 };
9693
9694 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9695     // thse are take from connection...
9696     
9697     /**
9698      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9699      */
9700     /**
9701      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9702      * extra parameters to each request made by this object. (defaults to undefined)
9703      */
9704     /**
9705      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9706      *  to each request made by this object. (defaults to undefined)
9707      */
9708     /**
9709      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
9710      */
9711     /**
9712      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9713      */
9714      /**
9715      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9716      * @type Boolean
9717      */
9718   
9719
9720     /**
9721      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9722      * @type Boolean
9723      */
9724     /**
9725      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9726      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9727      * a finer-grained basis than the DataProxy events.
9728      */
9729     getConnection : function(){
9730         return this.useAjax ? Roo.Ajax : this.conn;
9731     },
9732
9733     /**
9734      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9735      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9736      * process that block using the passed callback.
9737      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9738      * for the request to the remote server.
9739      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9740      * object into a block of Roo.data.Records.
9741      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9742      * The function must be passed <ul>
9743      * <li>The Record block object</li>
9744      * <li>The "arg" argument from the load function</li>
9745      * <li>A boolean success indicator</li>
9746      * </ul>
9747      * @param {Object} scope The scope in which to call the callback
9748      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9749      */
9750     load : function(params, reader, callback, scope, arg){
9751         if(this.fireEvent("beforeload", this, params) !== false){
9752             var  o = {
9753                 params : params || {},
9754                 request: {
9755                     callback : callback,
9756                     scope : scope,
9757                     arg : arg
9758                 },
9759                 reader: reader,
9760                 callback : this.loadResponse,
9761                 scope: this
9762             };
9763             if(this.useAjax){
9764                 Roo.applyIf(o, this.conn);
9765                 if(this.activeRequest){
9766                     Roo.Ajax.abort(this.activeRequest);
9767                 }
9768                 this.activeRequest = Roo.Ajax.request(o);
9769             }else{
9770                 this.conn.request(o);
9771             }
9772         }else{
9773             callback.call(scope||this, null, arg, false);
9774         }
9775     },
9776
9777     // private
9778     loadResponse : function(o, success, response){
9779         delete this.activeRequest;
9780         if(!success){
9781             this.fireEvent("loadexception", this, o, response);
9782             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9783             return;
9784         }
9785         var result;
9786         try {
9787             result = o.reader.read(response);
9788         }catch(e){
9789             this.fireEvent("loadexception", this, o, response, e);
9790             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9791             return;
9792         }
9793         
9794         this.fireEvent("load", this, o, o.request.arg);
9795         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9796     },
9797
9798     // private
9799     update : function(dataSet){
9800
9801     },
9802
9803     // private
9804     updateResponse : function(dataSet){
9805
9806     }
9807 });/*
9808  * Based on:
9809  * Ext JS Library 1.1.1
9810  * Copyright(c) 2006-2007, Ext JS, LLC.
9811  *
9812  * Originally Released Under LGPL - original licence link has changed is not relivant.
9813  *
9814  * Fork - LGPL
9815  * <script type="text/javascript">
9816  */
9817
9818 /**
9819  * @class Roo.data.ScriptTagProxy
9820  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9821  * other than the originating domain of the running page.<br><br>
9822  * <p>
9823  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
9824  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9825  * <p>
9826  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9827  * source code that is used as the source inside a &lt;script> tag.<br><br>
9828  * <p>
9829  * In order for the browser to process the returned data, the server must wrap the data object
9830  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9831  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9832  * depending on whether the callback name was passed:
9833  * <p>
9834  * <pre><code>
9835 boolean scriptTag = false;
9836 String cb = request.getParameter("callback");
9837 if (cb != null) {
9838     scriptTag = true;
9839     response.setContentType("text/javascript");
9840 } else {
9841     response.setContentType("application/x-json");
9842 }
9843 Writer out = response.getWriter();
9844 if (scriptTag) {
9845     out.write(cb + "(");
9846 }
9847 out.print(dataBlock.toJsonString());
9848 if (scriptTag) {
9849     out.write(");");
9850 }
9851 </pre></code>
9852  *
9853  * @constructor
9854  * @param {Object} config A configuration object.
9855  */
9856 Roo.data.ScriptTagProxy = function(config){
9857     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9858     Roo.apply(this, config);
9859     this.head = document.getElementsByTagName("head")[0];
9860 };
9861
9862 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9863
9864 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9865     /**
9866      * @cfg {String} url The URL from which to request the data object.
9867      */
9868     /**
9869      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9870      */
9871     timeout : 30000,
9872     /**
9873      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9874      * the server the name of the callback function set up by the load call to process the returned data object.
9875      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9876      * javascript output which calls this named function passing the data object as its only parameter.
9877      */
9878     callbackParam : "callback",
9879     /**
9880      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9881      * name to the request.
9882      */
9883     nocache : true,
9884
9885     /**
9886      * Load data from the configured URL, read the data object into
9887      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9888      * process that block using the passed callback.
9889      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9890      * for the request to the remote server.
9891      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9892      * object into a block of Roo.data.Records.
9893      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9894      * The function must be passed <ul>
9895      * <li>The Record block object</li>
9896      * <li>The "arg" argument from the load function</li>
9897      * <li>A boolean success indicator</li>
9898      * </ul>
9899      * @param {Object} scope The scope in which to call the callback
9900      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9901      */
9902     load : function(params, reader, callback, scope, arg){
9903         if(this.fireEvent("beforeload", this, params) !== false){
9904
9905             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9906
9907             var url = this.url;
9908             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9909             if(this.nocache){
9910                 url += "&_dc=" + (new Date().getTime());
9911             }
9912             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9913             var trans = {
9914                 id : transId,
9915                 cb : "stcCallback"+transId,
9916                 scriptId : "stcScript"+transId,
9917                 params : params,
9918                 arg : arg,
9919                 url : url,
9920                 callback : callback,
9921                 scope : scope,
9922                 reader : reader
9923             };
9924             var conn = this;
9925
9926             window[trans.cb] = function(o){
9927                 conn.handleResponse(o, trans);
9928             };
9929
9930             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9931
9932             if(this.autoAbort !== false){
9933                 this.abort();
9934             }
9935
9936             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9937
9938             var script = document.createElement("script");
9939             script.setAttribute("src", url);
9940             script.setAttribute("type", "text/javascript");
9941             script.setAttribute("id", trans.scriptId);
9942             this.head.appendChild(script);
9943
9944             this.trans = trans;
9945         }else{
9946             callback.call(scope||this, null, arg, false);
9947         }
9948     },
9949
9950     // private
9951     isLoading : function(){
9952         return this.trans ? true : false;
9953     },
9954
9955     /**
9956      * Abort the current server request.
9957      */
9958     abort : function(){
9959         if(this.isLoading()){
9960             this.destroyTrans(this.trans);
9961         }
9962     },
9963
9964     // private
9965     destroyTrans : function(trans, isLoaded){
9966         this.head.removeChild(document.getElementById(trans.scriptId));
9967         clearTimeout(trans.timeoutId);
9968         if(isLoaded){
9969             window[trans.cb] = undefined;
9970             try{
9971                 delete window[trans.cb];
9972             }catch(e){}
9973         }else{
9974             // if hasn't been loaded, wait for load to remove it to prevent script error
9975             window[trans.cb] = function(){
9976                 window[trans.cb] = undefined;
9977                 try{
9978                     delete window[trans.cb];
9979                 }catch(e){}
9980             };
9981         }
9982     },
9983
9984     // private
9985     handleResponse : function(o, trans){
9986         this.trans = false;
9987         this.destroyTrans(trans, true);
9988         var result;
9989         try {
9990             result = trans.reader.readRecords(o);
9991         }catch(e){
9992             this.fireEvent("loadexception", this, o, trans.arg, e);
9993             trans.callback.call(trans.scope||window, null, trans.arg, false);
9994             return;
9995         }
9996         this.fireEvent("load", this, o, trans.arg);
9997         trans.callback.call(trans.scope||window, result, trans.arg, true);
9998     },
9999
10000     // private
10001     handleFailure : function(trans){
10002         this.trans = false;
10003         this.destroyTrans(trans, false);
10004         this.fireEvent("loadexception", this, null, trans.arg);
10005         trans.callback.call(trans.scope||window, null, trans.arg, false);
10006     }
10007 });/*
10008  * Based on:
10009  * Ext JS Library 1.1.1
10010  * Copyright(c) 2006-2007, Ext JS, LLC.
10011  *
10012  * Originally Released Under LGPL - original licence link has changed is not relivant.
10013  *
10014  * Fork - LGPL
10015  * <script type="text/javascript">
10016  */
10017
10018 /**
10019  * @class Roo.data.JsonReader
10020  * @extends Roo.data.DataReader
10021  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10022  * based on mappings in a provided Roo.data.Record constructor.
10023  * 
10024  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10025  * in the reply previously. 
10026  * 
10027  * <p>
10028  * Example code:
10029  * <pre><code>
10030 var RecordDef = Roo.data.Record.create([
10031     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10032     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10033 ]);
10034 var myReader = new Roo.data.JsonReader({
10035     totalProperty: "results",    // The property which contains the total dataset size (optional)
10036     root: "rows",                // The property which contains an Array of row objects
10037     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10038 }, RecordDef);
10039 </code></pre>
10040  * <p>
10041  * This would consume a JSON file like this:
10042  * <pre><code>
10043 { 'results': 2, 'rows': [
10044     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10045     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10046 }
10047 </code></pre>
10048  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10049  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10050  * paged from the remote server.
10051  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10052  * @cfg {String} root name of the property which contains the Array of row objects.
10053  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10054  * @constructor
10055  * Create a new JsonReader
10056  * @param {Object} meta Metadata configuration options
10057  * @param {Object} recordType Either an Array of field definition objects,
10058  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10059  */
10060 Roo.data.JsonReader = function(meta, recordType){
10061     
10062     meta = meta || {};
10063     // set some defaults:
10064     Roo.applyIf(meta, {
10065         totalProperty: 'total',
10066         successProperty : 'success',
10067         root : 'data',
10068         id : 'id'
10069     });
10070     
10071     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10072 };
10073 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10074     
10075     /**
10076      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10077      * Used by Store query builder to append _requestMeta to params.
10078      * 
10079      */
10080     metaFromRemote : false,
10081     /**
10082      * This method is only used by a DataProxy which has retrieved data from a remote server.
10083      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10084      * @return {Object} data A data block which is used by an Roo.data.Store object as
10085      * a cache of Roo.data.Records.
10086      */
10087     read : function(response){
10088         var json = response.responseText;
10089        
10090         var o = /* eval:var:o */ eval("("+json+")");
10091         if(!o) {
10092             throw {message: "JsonReader.read: Json object not found"};
10093         }
10094         
10095         if(o.metaData){
10096             
10097             delete this.ef;
10098             this.metaFromRemote = true;
10099             this.meta = o.metaData;
10100             this.recordType = Roo.data.Record.create(o.metaData.fields);
10101             this.onMetaChange(this.meta, this.recordType, o);
10102         }
10103         return this.readRecords(o);
10104     },
10105
10106     // private function a store will implement
10107     onMetaChange : function(meta, recordType, o){
10108
10109     },
10110
10111     /**
10112          * @ignore
10113          */
10114     simpleAccess: function(obj, subsc) {
10115         return obj[subsc];
10116     },
10117
10118         /**
10119          * @ignore
10120          */
10121     getJsonAccessor: function(){
10122         var re = /[\[\.]/;
10123         return function(expr) {
10124             try {
10125                 return(re.test(expr))
10126                     ? new Function("obj", "return obj." + expr)
10127                     : function(obj){
10128                         return obj[expr];
10129                     };
10130             } catch(e){}
10131             return Roo.emptyFn;
10132         };
10133     }(),
10134
10135     /**
10136      * Create a data block containing Roo.data.Records from an XML document.
10137      * @param {Object} o An object which contains an Array of row objects in the property specified
10138      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10139      * which contains the total size of the dataset.
10140      * @return {Object} data A data block which is used by an Roo.data.Store object as
10141      * a cache of Roo.data.Records.
10142      */
10143     readRecords : function(o){
10144         /**
10145          * After any data loads, the raw JSON data is available for further custom processing.
10146          * @type Object
10147          */
10148         this.o = o;
10149         var s = this.meta, Record = this.recordType,
10150             f = Record.prototype.fields, fi = f.items, fl = f.length;
10151
10152 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10153         if (!this.ef) {
10154             if(s.totalProperty) {
10155                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10156                 }
10157                 if(s.successProperty) {
10158                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10159                 }
10160                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10161                 if (s.id) {
10162                         var g = this.getJsonAccessor(s.id);
10163                         this.getId = function(rec) {
10164                                 var r = g(rec);  
10165                                 return (r === undefined || r === "") ? null : r;
10166                         };
10167                 } else {
10168                         this.getId = function(){return null;};
10169                 }
10170             this.ef = [];
10171             for(var jj = 0; jj < fl; jj++){
10172                 f = fi[jj];
10173                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10174                 this.ef[jj] = this.getJsonAccessor(map);
10175             }
10176         }
10177
10178         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10179         if(s.totalProperty){
10180             var vt = parseInt(this.getTotal(o), 10);
10181             if(!isNaN(vt)){
10182                 totalRecords = vt;
10183             }
10184         }
10185         if(s.successProperty){
10186             var vs = this.getSuccess(o);
10187             if(vs === false || vs === 'false'){
10188                 success = false;
10189             }
10190         }
10191         var records = [];
10192             for(var i = 0; i < c; i++){
10193                     var n = root[i];
10194                 var values = {};
10195                 var id = this.getId(n);
10196                 for(var j = 0; j < fl; j++){
10197                     f = fi[j];
10198                 var v = this.ef[j](n);
10199                 if (!f.convert) {
10200                     Roo.log('missing convert for ' + f.name);
10201                     Roo.log(f);
10202                     continue;
10203                 }
10204                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10205                 }
10206                 var record = new Record(values, id);
10207                 record.json = n;
10208                 records[i] = record;
10209             }
10210             return {
10211             raw : o,
10212                 success : success,
10213                 records : records,
10214                 totalRecords : totalRecords
10215             };
10216     }
10217 });/*
10218  * Based on:
10219  * Ext JS Library 1.1.1
10220  * Copyright(c) 2006-2007, Ext JS, LLC.
10221  *
10222  * Originally Released Under LGPL - original licence link has changed is not relivant.
10223  *
10224  * Fork - LGPL
10225  * <script type="text/javascript">
10226  */
10227
10228 /**
10229  * @class Roo.data.ArrayReader
10230  * @extends Roo.data.DataReader
10231  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10232  * Each element of that Array represents a row of data fields. The
10233  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10234  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10235  * <p>
10236  * Example code:.
10237  * <pre><code>
10238 var RecordDef = Roo.data.Record.create([
10239     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10240     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10241 ]);
10242 var myReader = new Roo.data.ArrayReader({
10243     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10244 }, RecordDef);
10245 </code></pre>
10246  * <p>
10247  * This would consume an Array like this:
10248  * <pre><code>
10249 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10250   </code></pre>
10251  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10252  * @constructor
10253  * Create a new JsonReader
10254  * @param {Object} meta Metadata configuration options.
10255  * @param {Object} recordType Either an Array of field definition objects
10256  * as specified to {@link Roo.data.Record#create},
10257  * or an {@link Roo.data.Record} object
10258  * created using {@link Roo.data.Record#create}.
10259  */
10260 Roo.data.ArrayReader = function(meta, recordType){
10261     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10262 };
10263
10264 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10265     /**
10266      * Create a data block containing Roo.data.Records from an XML document.
10267      * @param {Object} o An Array of row objects which represents the dataset.
10268      * @return {Object} data A data block which is used by an Roo.data.Store object as
10269      * a cache of Roo.data.Records.
10270      */
10271     readRecords : function(o){
10272         var sid = this.meta ? this.meta.id : null;
10273         var recordType = this.recordType, fields = recordType.prototype.fields;
10274         var records = [];
10275         var root = o;
10276             for(var i = 0; i < root.length; i++){
10277                     var n = root[i];
10278                 var values = {};
10279                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10280                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10281                 var f = fields.items[j];
10282                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10283                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10284                 v = f.convert(v);
10285                 values[f.name] = v;
10286             }
10287                 var record = new recordType(values, id);
10288                 record.json = n;
10289                 records[records.length] = record;
10290             }
10291             return {
10292                 records : records,
10293                 totalRecords : records.length
10294             };
10295     }
10296 });/*
10297  * - LGPL
10298  * * 
10299  */
10300
10301 /**
10302  * @class Roo.bootstrap.ComboBox
10303  * @extends Roo.bootstrap.TriggerField
10304  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10305  * @cfg {Boolean} append (true|false) default false
10306  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10307  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10308  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10309  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10310  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10311  * @constructor
10312  * Create a new ComboBox.
10313  * @param {Object} config Configuration options
10314  */
10315 Roo.bootstrap.ComboBox = function(config){
10316     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10317     this.addEvents({
10318         /**
10319          * @event expand
10320          * Fires when the dropdown list is expanded
10321              * @param {Roo.bootstrap.ComboBox} combo This combo box
10322              */
10323         'expand' : true,
10324         /**
10325          * @event collapse
10326          * Fires when the dropdown list is collapsed
10327              * @param {Roo.bootstrap.ComboBox} combo This combo box
10328              */
10329         'collapse' : true,
10330         /**
10331          * @event beforeselect
10332          * Fires before a list item is selected. Return false to cancel the selection.
10333              * @param {Roo.bootstrap.ComboBox} combo This combo box
10334              * @param {Roo.data.Record} record The data record returned from the underlying store
10335              * @param {Number} index The index of the selected item in the dropdown list
10336              */
10337         'beforeselect' : true,
10338         /**
10339          * @event select
10340          * Fires when a list item is selected
10341              * @param {Roo.bootstrap.ComboBox} combo This combo box
10342              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10343              * @param {Number} index The index of the selected item in the dropdown list
10344              */
10345         'select' : true,
10346         /**
10347          * @event beforequery
10348          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10349          * The event object passed has these properties:
10350              * @param {Roo.bootstrap.ComboBox} combo This combo box
10351              * @param {String} query The query
10352              * @param {Boolean} forceAll true to force "all" query
10353              * @param {Boolean} cancel true to cancel the query
10354              * @param {Object} e The query event object
10355              */
10356         'beforequery': true,
10357          /**
10358          * @event add
10359          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10360              * @param {Roo.bootstrap.ComboBox} combo This combo box
10361              */
10362         'add' : true,
10363         /**
10364          * @event edit
10365          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10366              * @param {Roo.bootstrap.ComboBox} combo This combo box
10367              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10368              */
10369         'edit' : true,
10370         /**
10371          * @event remove
10372          * Fires when the remove value from the combobox array
10373              * @param {Roo.bootstrap.ComboBox} combo This combo box
10374              */
10375         'remove' : true
10376         
10377     });
10378     
10379     this.item = [];
10380     this.tickItems = [];
10381     
10382     this.selectedIndex = -1;
10383     if(this.mode == 'local'){
10384         if(config.queryDelay === undefined){
10385             this.queryDelay = 10;
10386         }
10387         if(config.minChars === undefined){
10388             this.minChars = 0;
10389         }
10390     }
10391 };
10392
10393 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10394      
10395     /**
10396      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10397      * rendering into an Roo.Editor, defaults to false)
10398      */
10399     /**
10400      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10401      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10402      */
10403     /**
10404      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10405      */
10406     /**
10407      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10408      * the dropdown list (defaults to undefined, with no header element)
10409      */
10410
10411      /**
10412      * @cfg {String/Roo.Template} tpl The template to use to render the output
10413      */
10414      
10415      /**
10416      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10417      */
10418     listWidth: undefined,
10419     /**
10420      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10421      * mode = 'remote' or 'text' if mode = 'local')
10422      */
10423     displayField: undefined,
10424     /**
10425      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10426      * mode = 'remote' or 'value' if mode = 'local'). 
10427      * Note: use of a valueField requires the user make a selection
10428      * in order for a value to be mapped.
10429      */
10430     valueField: undefined,
10431     
10432     
10433     /**
10434      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10435      * field's data value (defaults to the underlying DOM element's name)
10436      */
10437     hiddenName: undefined,
10438     /**
10439      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10440      */
10441     listClass: '',
10442     /**
10443      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10444      */
10445     selectedClass: 'active',
10446     
10447     /**
10448      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10449      */
10450     shadow:'sides',
10451     /**
10452      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10453      * anchor positions (defaults to 'tl-bl')
10454      */
10455     listAlign: 'tl-bl?',
10456     /**
10457      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10458      */
10459     maxHeight: 300,
10460     /**
10461      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10462      * query specified by the allQuery config option (defaults to 'query')
10463      */
10464     triggerAction: 'query',
10465     /**
10466      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10467      * (defaults to 4, does not apply if editable = false)
10468      */
10469     minChars : 4,
10470     /**
10471      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10472      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10473      */
10474     typeAhead: false,
10475     /**
10476      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10477      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10478      */
10479     queryDelay: 500,
10480     /**
10481      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10482      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10483      */
10484     pageSize: 0,
10485     /**
10486      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10487      * when editable = true (defaults to false)
10488      */
10489     selectOnFocus:false,
10490     /**
10491      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10492      */
10493     queryParam: 'query',
10494     /**
10495      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10496      * when mode = 'remote' (defaults to 'Loading...')
10497      */
10498     loadingText: 'Loading...',
10499     /**
10500      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10501      */
10502     resizable: false,
10503     /**
10504      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10505      */
10506     handleHeight : 8,
10507     /**
10508      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10509      * traditional select (defaults to true)
10510      */
10511     editable: true,
10512     /**
10513      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10514      */
10515     allQuery: '',
10516     /**
10517      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10518      */
10519     mode: 'remote',
10520     /**
10521      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10522      * listWidth has a higher value)
10523      */
10524     minListWidth : 70,
10525     /**
10526      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10527      * allow the user to set arbitrary text into the field (defaults to false)
10528      */
10529     forceSelection:false,
10530     /**
10531      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10532      * if typeAhead = true (defaults to 250)
10533      */
10534     typeAheadDelay : 250,
10535     /**
10536      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10537      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10538      */
10539     valueNotFoundText : undefined,
10540     /**
10541      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10542      */
10543     blockFocus : false,
10544     
10545     /**
10546      * @cfg {Boolean} disableClear Disable showing of clear button.
10547      */
10548     disableClear : false,
10549     /**
10550      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10551      */
10552     alwaysQuery : false,
10553     
10554     /**
10555      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10556      */
10557     multiple : false,
10558     
10559     //private
10560     addicon : false,
10561     editicon: false,
10562     
10563     page: 0,
10564     hasQuery: false,
10565     append: false,
10566     loadNext: false,
10567     autoFocus : true,
10568     tickable : false,
10569     btnPosition : 'right',
10570     triggerList : true,
10571     showToggleBtn : true,
10572     // element that contains real text value.. (when hidden is used..)
10573     
10574     getAutoCreate : function()
10575     {
10576         var cfg = false;
10577         
10578         /*
10579          *  Normal ComboBox
10580          */
10581         if(!this.tickable){
10582             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10583             return cfg;
10584         }
10585         
10586         /*
10587          *  ComboBox with tickable selections
10588          */
10589              
10590         var align = this.labelAlign || this.parentLabelAlign();
10591         
10592         cfg = {
10593             cls : 'form-group roo-combobox-tickable' //input-group
10594         };
10595         
10596         
10597         var buttons = {
10598             tag : 'div',
10599             cls : 'tickable-buttons',
10600             cn : [
10601                 {
10602                     tag : 'button',
10603                     type : 'button',
10604                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10605                     html : 'Edit'
10606                 },
10607                 {
10608                     tag : 'button',
10609                     type : 'button',
10610                     name : 'ok',
10611                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10612                     html : 'Done'
10613                 },
10614                 {
10615                     tag : 'button',
10616                     type : 'button',
10617                     name : 'cancel',
10618                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10619                     html : 'Cancel'
10620                 }
10621             ]
10622         };
10623         
10624         var _this = this;
10625         Roo.each(buttons.cn, function(c){
10626             if (_this.size) {
10627                 c.cls += ' btn-' + _this.size;
10628             }
10629
10630             if (_this.disabled) {
10631                 c.disabled = true;
10632             }
10633         });
10634         
10635         var box = {
10636             tag: 'div',
10637             cn: [
10638                 {
10639                     tag: 'input',
10640                     type : 'hidden',
10641                     cls: 'form-hidden-field'
10642                 },
10643                 {
10644                     tag: 'ul',
10645                     cls: 'select2-choices',
10646                     cn:[
10647                         {
10648                             tag: 'li',
10649                             cls: 'select2-search-field',
10650                             cn: [
10651
10652                                 buttons
10653                             ]
10654                         }
10655                     ]
10656                 }
10657             ]
10658         }
10659         
10660         var combobox = {
10661             cls: 'select2-container input-group select2-container-multi',
10662             cn: [
10663                 box
10664 //                {
10665 //                    tag: 'ul',
10666 //                    cls: 'typeahead typeahead-long dropdown-menu',
10667 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10668 //                }
10669             ]
10670         };
10671         
10672         if (align ==='left' && this.fieldLabel.length) {
10673             
10674                 Roo.log("left and has label");
10675                 cfg.cn = [
10676                     
10677                     {
10678                         tag: 'label',
10679                         'for' :  id,
10680                         cls : 'control-label col-sm-' + this.labelWidth,
10681                         html : this.fieldLabel
10682                         
10683                     },
10684                     {
10685                         cls : "col-sm-" + (12 - this.labelWidth), 
10686                         cn: [
10687                             combobox
10688                         ]
10689                     }
10690                     
10691                 ];
10692         } else if ( this.fieldLabel.length) {
10693                 Roo.log(" label");
10694                  cfg.cn = [
10695                    
10696                     {
10697                         tag: 'label',
10698                         //cls : 'input-group-addon',
10699                         html : this.fieldLabel
10700                         
10701                     },
10702                     
10703                     combobox
10704                     
10705                 ];
10706
10707         } else {
10708             
10709                 Roo.log(" no label && no align");
10710                 cfg = combobox
10711                      
10712                 
10713         }
10714          
10715         var settings=this;
10716         ['xs','sm','md','lg'].map(function(size){
10717             if (settings[size]) {
10718                 cfg.cls += ' col-' + size + '-' + settings[size];
10719             }
10720         });
10721         
10722         return cfg;
10723         
10724     },
10725     
10726     // private
10727     initEvents: function()
10728     {
10729         
10730         if (!this.store) {
10731             throw "can not find store for combo";
10732         }
10733         this.store = Roo.factory(this.store, Roo.data);
10734         
10735         if(this.tickable){
10736             this.initTickableEvents();
10737             return;
10738         }
10739         
10740         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10741         
10742         if(this.hiddenName){
10743             
10744             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10745             
10746             this.hiddenField.dom.value =
10747                 this.hiddenValue !== undefined ? this.hiddenValue :
10748                 this.value !== undefined ? this.value : '';
10749
10750             // prevent input submission
10751             this.el.dom.removeAttribute('name');
10752             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10753              
10754              
10755         }
10756         //if(Roo.isGecko){
10757         //    this.el.dom.setAttribute('autocomplete', 'off');
10758         //}
10759         
10760         var cls = 'x-combo-list';
10761         
10762         //this.list = new Roo.Layer({
10763         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10764         //});
10765         
10766         var _this = this;
10767         
10768         (function(){
10769             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10770             _this.list.setWidth(lw);
10771         }).defer(100);
10772         
10773         this.list.on('mouseover', this.onViewOver, this);
10774         this.list.on('mousemove', this.onViewMove, this);
10775         
10776         this.list.on('scroll', this.onViewScroll, this);
10777         
10778         /*
10779         this.list.swallowEvent('mousewheel');
10780         this.assetHeight = 0;
10781
10782         if(this.title){
10783             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10784             this.assetHeight += this.header.getHeight();
10785         }
10786
10787         this.innerList = this.list.createChild({cls:cls+'-inner'});
10788         this.innerList.on('mouseover', this.onViewOver, this);
10789         this.innerList.on('mousemove', this.onViewMove, this);
10790         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10791         
10792         if(this.allowBlank && !this.pageSize && !this.disableClear){
10793             this.footer = this.list.createChild({cls:cls+'-ft'});
10794             this.pageTb = new Roo.Toolbar(this.footer);
10795            
10796         }
10797         if(this.pageSize){
10798             this.footer = this.list.createChild({cls:cls+'-ft'});
10799             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10800                     {pageSize: this.pageSize});
10801             
10802         }
10803         
10804         if (this.pageTb && this.allowBlank && !this.disableClear) {
10805             var _this = this;
10806             this.pageTb.add(new Roo.Toolbar.Fill(), {
10807                 cls: 'x-btn-icon x-btn-clear',
10808                 text: '&#160;',
10809                 handler: function()
10810                 {
10811                     _this.collapse();
10812                     _this.clearValue();
10813                     _this.onSelect(false, -1);
10814                 }
10815             });
10816         }
10817         if (this.footer) {
10818             this.assetHeight += this.footer.getHeight();
10819         }
10820         */
10821             
10822         if(!this.tpl){
10823             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10824         }
10825
10826         this.view = new Roo.View(this.list, this.tpl, {
10827             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10828         });
10829         //this.view.wrapEl.setDisplayed(false);
10830         this.view.on('click', this.onViewClick, this);
10831         
10832         
10833         
10834         this.store.on('beforeload', this.onBeforeLoad, this);
10835         this.store.on('load', this.onLoad, this);
10836         this.store.on('loadexception', this.onLoadException, this);
10837         /*
10838         if(this.resizable){
10839             this.resizer = new Roo.Resizable(this.list,  {
10840                pinned:true, handles:'se'
10841             });
10842             this.resizer.on('resize', function(r, w, h){
10843                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10844                 this.listWidth = w;
10845                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10846                 this.restrictHeight();
10847             }, this);
10848             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10849         }
10850         */
10851         if(!this.editable){
10852             this.editable = true;
10853             this.setEditable(false);
10854         }
10855         
10856         /*
10857         
10858         if (typeof(this.events.add.listeners) != 'undefined') {
10859             
10860             this.addicon = this.wrap.createChild(
10861                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10862        
10863             this.addicon.on('click', function(e) {
10864                 this.fireEvent('add', this);
10865             }, this);
10866         }
10867         if (typeof(this.events.edit.listeners) != 'undefined') {
10868             
10869             this.editicon = this.wrap.createChild(
10870                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10871             if (this.addicon) {
10872                 this.editicon.setStyle('margin-left', '40px');
10873             }
10874             this.editicon.on('click', function(e) {
10875                 
10876                 // we fire even  if inothing is selected..
10877                 this.fireEvent('edit', this, this.lastData );
10878                 
10879             }, this);
10880         }
10881         */
10882         
10883         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10884             "up" : function(e){
10885                 this.inKeyMode = true;
10886                 this.selectPrev();
10887             },
10888
10889             "down" : function(e){
10890                 if(!this.isExpanded()){
10891                     this.onTriggerClick();
10892                 }else{
10893                     this.inKeyMode = true;
10894                     this.selectNext();
10895                 }
10896             },
10897
10898             "enter" : function(e){
10899 //                this.onViewClick();
10900                 //return true;
10901                 this.collapse();
10902                 
10903                 if(this.fireEvent("specialkey", this, e)){
10904                     this.onViewClick(false);
10905                 }
10906                 
10907                 return true;
10908             },
10909
10910             "esc" : function(e){
10911                 this.collapse();
10912             },
10913
10914             "tab" : function(e){
10915                 this.collapse();
10916                 
10917                 if(this.fireEvent("specialkey", this, e)){
10918                     this.onViewClick(false);
10919                 }
10920                 
10921                 return true;
10922             },
10923
10924             scope : this,
10925
10926             doRelay : function(foo, bar, hname){
10927                 if(hname == 'down' || this.scope.isExpanded()){
10928                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10929                 }
10930                 return true;
10931             },
10932
10933             forceKeyDown: true
10934         });
10935         
10936         
10937         this.queryDelay = Math.max(this.queryDelay || 10,
10938                 this.mode == 'local' ? 10 : 250);
10939         
10940         
10941         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10942         
10943         if(this.typeAhead){
10944             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10945         }
10946         if(this.editable !== false){
10947             this.inputEl().on("keyup", this.onKeyUp, this);
10948         }
10949         if(this.forceSelection){
10950             this.inputEl().on('blur', this.doForce, this);
10951         }
10952         
10953         if(this.multiple){
10954             this.choices = this.el.select('ul.select2-choices', true).first();
10955             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10956         }
10957     },
10958     
10959     initTickableEvents: function()
10960     {   
10961         this.createList();
10962         
10963         if(this.hiddenName){
10964             
10965             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10966             
10967             this.hiddenField.dom.value =
10968                 this.hiddenValue !== undefined ? this.hiddenValue :
10969                 this.value !== undefined ? this.value : '';
10970
10971             // prevent input submission
10972             this.el.dom.removeAttribute('name');
10973             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10974              
10975              
10976         }
10977         
10978 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10979         
10980         this.choices = this.el.select('ul.select2-choices', true).first();
10981         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10982         if(this.triggerList){
10983             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10984         }
10985          
10986         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10987         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10988         
10989         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10990         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10991         
10992         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10993         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10994         
10995         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10996         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10997         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10998         
10999         this.okBtn.hide();
11000         this.cancelBtn.hide();
11001         
11002         var _this = this;
11003         
11004         (function(){
11005             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11006             _this.list.setWidth(lw);
11007         }).defer(100);
11008         
11009         this.list.on('mouseover', this.onViewOver, this);
11010         this.list.on('mousemove', this.onViewMove, this);
11011         
11012         this.list.on('scroll', this.onViewScroll, this);
11013         
11014         if(!this.tpl){
11015             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11016         }
11017
11018         this.view = new Roo.View(this.list, this.tpl, {
11019             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11020         });
11021         
11022         //this.view.wrapEl.setDisplayed(false);
11023         this.view.on('click', this.onViewClick, this);
11024         
11025         
11026         
11027         this.store.on('beforeload', this.onBeforeLoad, this);
11028         this.store.on('load', this.onLoad, this);
11029         this.store.on('loadexception', this.onLoadException, this);
11030         
11031 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11032 //            "up" : function(e){
11033 //                this.inKeyMode = true;
11034 //                this.selectPrev();
11035 //            },
11036 //
11037 //            "down" : function(e){
11038 //                if(!this.isExpanded()){
11039 //                    this.onTriggerClick();
11040 //                }else{
11041 //                    this.inKeyMode = true;
11042 //                    this.selectNext();
11043 //                }
11044 //            },
11045 //
11046 //            "enter" : function(e){
11047 ////                this.onViewClick();
11048 //                //return true;
11049 //                this.collapse();
11050 //                
11051 //                if(this.fireEvent("specialkey", this, e)){
11052 //                    this.onViewClick(false);
11053 //                }
11054 //                
11055 //                return true;
11056 //            },
11057 //
11058 //            "esc" : function(e){
11059 //                this.collapse();
11060 //            },
11061 //
11062 //            "tab" : function(e){
11063 //                this.collapse();
11064 //                
11065 //                if(this.fireEvent("specialkey", this, e)){
11066 //                    this.onViewClick(false);
11067 //                }
11068 //                
11069 //                return true;
11070 //            },
11071 //
11072 //            scope : this,
11073 //
11074 //            doRelay : function(foo, bar, hname){
11075 //                if(hname == 'down' || this.scope.isExpanded()){
11076 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11077 //                }
11078 //                return true;
11079 //            },
11080 //
11081 //            forceKeyDown: true
11082 //        });
11083         
11084         
11085         this.queryDelay = Math.max(this.queryDelay || 10,
11086                 this.mode == 'local' ? 10 : 250);
11087         
11088         
11089         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11090         
11091         if(this.typeAhead){
11092             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11093         }
11094     },
11095
11096     onDestroy : function(){
11097         if(this.view){
11098             this.view.setStore(null);
11099             this.view.el.removeAllListeners();
11100             this.view.el.remove();
11101             this.view.purgeListeners();
11102         }
11103         if(this.list){
11104             this.list.dom.innerHTML  = '';
11105         }
11106         
11107         if(this.store){
11108             this.store.un('beforeload', this.onBeforeLoad, this);
11109             this.store.un('load', this.onLoad, this);
11110             this.store.un('loadexception', this.onLoadException, this);
11111         }
11112         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11113     },
11114
11115     // private
11116     fireKey : function(e){
11117         if(e.isNavKeyPress() && !this.list.isVisible()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121
11122     // private
11123     onResize: function(w, h){
11124 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11125 //        
11126 //        if(typeof w != 'number'){
11127 //            // we do not handle it!?!?
11128 //            return;
11129 //        }
11130 //        var tw = this.trigger.getWidth();
11131 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11132 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11133 //        var x = w - tw;
11134 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11135 //            
11136 //        //this.trigger.setStyle('left', x+'px');
11137 //        
11138 //        if(this.list && this.listWidth === undefined){
11139 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11140 //            this.list.setWidth(lw);
11141 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11142 //        }
11143         
11144     
11145         
11146     },
11147
11148     /**
11149      * Allow or prevent the user from directly editing the field text.  If false is passed,
11150      * the user will only be able to select from the items defined in the dropdown list.  This method
11151      * is the runtime equivalent of setting the 'editable' config option at config time.
11152      * @param {Boolean} value True to allow the user to directly edit the field text
11153      */
11154     setEditable : function(value){
11155         if(value == this.editable){
11156             return;
11157         }
11158         this.editable = value;
11159         if(!value){
11160             this.inputEl().dom.setAttribute('readOnly', true);
11161             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11162             this.inputEl().addClass('x-combo-noedit');
11163         }else{
11164             this.inputEl().dom.setAttribute('readOnly', false);
11165             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11166             this.inputEl().removeClass('x-combo-noedit');
11167         }
11168     },
11169
11170     // private
11171     
11172     onBeforeLoad : function(combo,opts){
11173         if(!this.hasFocus){
11174             return;
11175         }
11176          if (!opts.add) {
11177             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11178          }
11179         this.restrictHeight();
11180         this.selectedIndex = -1;
11181     },
11182
11183     // private
11184     onLoad : function(){
11185         
11186         this.hasQuery = false;
11187         
11188         if(!this.hasFocus){
11189             return;
11190         }
11191         
11192         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11193             this.loading.hide();
11194         }
11195         
11196         if(this.store.getCount() > 0){
11197             this.expand();
11198 //            this.restrictHeight();
11199             if(this.lastQuery == this.allQuery){
11200                 if(this.editable && !this.tickable){
11201                     this.inputEl().dom.select();
11202                 }
11203                 
11204                 if(
11205                     !this.selectByValue(this.value, true) &&
11206                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11207                     this.store.lastOptions.add != true)
11208                 ){
11209                     this.select(0, true);
11210                 }
11211             }else{
11212                 if(this.autoFocus){
11213                     this.selectNext();
11214                 }
11215                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11216                     this.taTask.delay(this.typeAheadDelay);
11217                 }
11218             }
11219         }else{
11220             this.onEmptyResults();
11221         }
11222         
11223         //this.el.focus();
11224     },
11225     // private
11226     onLoadException : function()
11227     {
11228         this.hasQuery = false;
11229         
11230         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11231             this.loading.hide();
11232         }
11233         
11234         this.collapse();
11235         Roo.log(this.store.reader.jsonData);
11236         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11237             // fixme
11238             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11239         }
11240         
11241         
11242     },
11243     // private
11244     onTypeAhead : function(){
11245         if(this.store.getCount() > 0){
11246             var r = this.store.getAt(0);
11247             var newValue = r.data[this.displayField];
11248             var len = newValue.length;
11249             var selStart = this.getRawValue().length;
11250             
11251             if(selStart != len){
11252                 this.setRawValue(newValue);
11253                 this.selectText(selStart, newValue.length);
11254             }
11255         }
11256     },
11257
11258     // private
11259     onSelect : function(record, index){
11260         
11261         if(this.fireEvent('beforeselect', this, record, index) !== false){
11262         
11263             this.setFromData(index > -1 ? record.data : false);
11264             
11265             this.collapse();
11266             this.fireEvent('select', this, record, index);
11267         }
11268     },
11269
11270     /**
11271      * Returns the currently selected field value or empty string if no value is set.
11272      * @return {String} value The selected value
11273      */
11274     getValue : function(){
11275         
11276         if(this.multiple){
11277             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11278         }
11279         
11280         if(this.valueField){
11281             return typeof this.value != 'undefined' ? this.value : '';
11282         }else{
11283             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11284         }
11285     },
11286
11287     /**
11288      * Clears any text/value currently set in the field
11289      */
11290     clearValue : function(){
11291         if(this.hiddenField){
11292             this.hiddenField.dom.value = '';
11293         }
11294         this.value = '';
11295         this.setRawValue('');
11296         this.lastSelectionText = '';
11297         
11298     },
11299
11300     /**
11301      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11302      * will be displayed in the field.  If the value does not match the data value of an existing item,
11303      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11304      * Otherwise the field will be blank (although the value will still be set).
11305      * @param {String} value The value to match
11306      */
11307     setValue : function(v){
11308         if(this.multiple){
11309             this.syncValue();
11310             return;
11311         }
11312         
11313         var text = v;
11314         if(this.valueField){
11315             var r = this.findRecord(this.valueField, v);
11316             if(r){
11317                 text = r.data[this.displayField];
11318             }else if(this.valueNotFoundText !== undefined){
11319                 text = this.valueNotFoundText;
11320             }
11321         }
11322         this.lastSelectionText = text;
11323         if(this.hiddenField){
11324             this.hiddenField.dom.value = v;
11325         }
11326         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11327         this.value = v;
11328     },
11329     /**
11330      * @property {Object} the last set data for the element
11331      */
11332     
11333     lastData : false,
11334     /**
11335      * Sets the value of the field based on a object which is related to the record format for the store.
11336      * @param {Object} value the value to set as. or false on reset?
11337      */
11338     setFromData : function(o){
11339         
11340         if(this.multiple){
11341             this.addItem(o);
11342             return;
11343         }
11344             
11345         var dv = ''; // display value
11346         var vv = ''; // value value..
11347         this.lastData = o;
11348         if (this.displayField) {
11349             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11350         } else {
11351             // this is an error condition!!!
11352             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11353         }
11354         
11355         if(this.valueField){
11356             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11357         }
11358         
11359         if(this.hiddenField){
11360             this.hiddenField.dom.value = vv;
11361             
11362             this.lastSelectionText = dv;
11363             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11364             this.value = vv;
11365             return;
11366         }
11367         // no hidden field.. - we store the value in 'value', but still display
11368         // display field!!!!
11369         this.lastSelectionText = dv;
11370         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11371         this.value = vv;
11372         
11373         
11374     },
11375     // private
11376     reset : function(){
11377         // overridden so that last data is reset..
11378         this.setValue(this.originalValue);
11379         this.clearInvalid();
11380         this.lastData = false;
11381         if (this.view) {
11382             this.view.clearSelections();
11383         }
11384     },
11385     // private
11386     findRecord : function(prop, value){
11387         var record;
11388         if(this.store.getCount() > 0){
11389             this.store.each(function(r){
11390                 if(r.data[prop] == value){
11391                     record = r;
11392                     return false;
11393                 }
11394                 return true;
11395             });
11396         }
11397         return record;
11398     },
11399     
11400     getName: function()
11401     {
11402         // returns hidden if it's set..
11403         if (!this.rendered) {return ''};
11404         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11405         
11406     },
11407     // private
11408     onViewMove : function(e, t){
11409         this.inKeyMode = false;
11410     },
11411
11412     // private
11413     onViewOver : function(e, t){
11414         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11415             return;
11416         }
11417         var item = this.view.findItemFromChild(t);
11418         
11419         if(item){
11420             var index = this.view.indexOf(item);
11421             this.select(index, false);
11422         }
11423     },
11424
11425     // private
11426     onViewClick : function(view, doFocus, el, e)
11427     {
11428         var index = this.view.getSelectedIndexes()[0];
11429         
11430         var r = this.store.getAt(index);
11431         
11432         if(this.tickable){
11433             
11434             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11435                 return;
11436             }
11437             
11438             var rm = false;
11439             var _this = this;
11440             
11441             Roo.each(this.tickItems, function(v,k){
11442                 
11443                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11444                     _this.tickItems.splice(k, 1);
11445                     rm = true;
11446                     return;
11447                 }
11448             })
11449             
11450             if(rm){
11451                 return;
11452             }
11453             
11454             this.tickItems.push(r.data);
11455             return;
11456         }
11457         
11458         if(r){
11459             this.onSelect(r, index);
11460         }
11461         if(doFocus !== false && !this.blockFocus){
11462             this.inputEl().focus();
11463         }
11464     },
11465
11466     // private
11467     restrictHeight : function(){
11468         //this.innerList.dom.style.height = '';
11469         //var inner = this.innerList.dom;
11470         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11471         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11472         //this.list.beginUpdate();
11473         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11474         this.list.alignTo(this.inputEl(), this.listAlign);
11475         this.list.alignTo(this.inputEl(), this.listAlign);
11476         //this.list.endUpdate();
11477     },
11478
11479     // private
11480     onEmptyResults : function(){
11481         this.collapse();
11482     },
11483
11484     /**
11485      * Returns true if the dropdown list is expanded, else false.
11486      */
11487     isExpanded : function(){
11488         return this.list.isVisible();
11489     },
11490
11491     /**
11492      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11493      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11494      * @param {String} value The data value of the item to select
11495      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11496      * selected item if it is not currently in view (defaults to true)
11497      * @return {Boolean} True if the value matched an item in the list, else false
11498      */
11499     selectByValue : function(v, scrollIntoView){
11500         if(v !== undefined && v !== null){
11501             var r = this.findRecord(this.valueField || this.displayField, v);
11502             if(r){
11503                 this.select(this.store.indexOf(r), scrollIntoView);
11504                 return true;
11505             }
11506         }
11507         return false;
11508     },
11509
11510     /**
11511      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11512      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11513      * @param {Number} index The zero-based index of the list item to select
11514      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11515      * selected item if it is not currently in view (defaults to true)
11516      */
11517     select : function(index, scrollIntoView){
11518         this.selectedIndex = index;
11519         this.view.select(index);
11520         if(scrollIntoView !== false){
11521             var el = this.view.getNode(index);
11522             if(el && !this.multiple && !this.tickable){
11523                 this.list.scrollChildIntoView(el, false);
11524             }
11525         }
11526     },
11527
11528     // private
11529     selectNext : function(){
11530         var ct = this.store.getCount();
11531         if(ct > 0){
11532             if(this.selectedIndex == -1){
11533                 this.select(0);
11534             }else if(this.selectedIndex < ct-1){
11535                 this.select(this.selectedIndex+1);
11536             }
11537         }
11538     },
11539
11540     // private
11541     selectPrev : function(){
11542         var ct = this.store.getCount();
11543         if(ct > 0){
11544             if(this.selectedIndex == -1){
11545                 this.select(0);
11546             }else if(this.selectedIndex != 0){
11547                 this.select(this.selectedIndex-1);
11548             }
11549         }
11550     },
11551
11552     // private
11553     onKeyUp : function(e){
11554         if(this.editable !== false && !e.isSpecialKey()){
11555             this.lastKey = e.getKey();
11556             this.dqTask.delay(this.queryDelay);
11557         }
11558     },
11559
11560     // private
11561     validateBlur : function(){
11562         return !this.list || !this.list.isVisible();   
11563     },
11564
11565     // private
11566     initQuery : function(){
11567         this.doQuery(this.getRawValue());
11568     },
11569
11570     // private
11571     doForce : function(){
11572         if(this.inputEl().dom.value.length > 0){
11573             this.inputEl().dom.value =
11574                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11575              
11576         }
11577     },
11578
11579     /**
11580      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11581      * query allowing the query action to be canceled if needed.
11582      * @param {String} query The SQL query to execute
11583      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11584      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11585      * saved in the current store (defaults to false)
11586      */
11587     doQuery : function(q, forceAll){
11588         
11589         if(q === undefined || q === null){
11590             q = '';
11591         }
11592         var qe = {
11593             query: q,
11594             forceAll: forceAll,
11595             combo: this,
11596             cancel:false
11597         };
11598         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11599             return false;
11600         }
11601         q = qe.query;
11602         
11603         forceAll = qe.forceAll;
11604         if(forceAll === true || (q.length >= this.minChars)){
11605             
11606             this.hasQuery = true;
11607             
11608             if(this.lastQuery != q || this.alwaysQuery){
11609                 this.lastQuery = q;
11610                 if(this.mode == 'local'){
11611                     this.selectedIndex = -1;
11612                     if(forceAll){
11613                         this.store.clearFilter();
11614                     }else{
11615                         this.store.filter(this.displayField, q);
11616                     }
11617                     this.onLoad();
11618                 }else{
11619                     this.store.baseParams[this.queryParam] = q;
11620                     
11621                     var options = {params : this.getParams(q)};
11622                     
11623                     if(this.loadNext){
11624                         options.add = true;
11625                         options.params.start = this.page * this.pageSize;
11626                     }
11627                     
11628                     this.store.load(options);
11629                     /*
11630                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11631                      *  we should expand the list on onLoad
11632                      *  so command out it
11633                      */
11634 //                    this.expand();
11635                 }
11636             }else{
11637                 this.selectedIndex = -1;
11638                 this.onLoad();   
11639             }
11640         }
11641         
11642         this.loadNext = false;
11643     },
11644
11645     // private
11646     getParams : function(q){
11647         var p = {};
11648         //p[this.queryParam] = q;
11649         
11650         if(this.pageSize){
11651             p.start = 0;
11652             p.limit = this.pageSize;
11653         }
11654         return p;
11655     },
11656
11657     /**
11658      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11659      */
11660     collapse : function(){
11661         if(!this.isExpanded()){
11662             return;
11663         }
11664         
11665         this.list.hide();
11666         
11667         if(this.tickable){
11668             this.okBtn.hide();
11669             this.cancelBtn.hide();
11670             this.trigger.show();
11671         }
11672         
11673         Roo.get(document).un('mousedown', this.collapseIf, this);
11674         Roo.get(document).un('mousewheel', this.collapseIf, this);
11675         if (!this.editable) {
11676             Roo.get(document).un('keydown', this.listKeyPress, this);
11677         }
11678         this.fireEvent('collapse', this);
11679     },
11680
11681     // private
11682     collapseIf : function(e){
11683         var in_combo  = e.within(this.el);
11684         var in_list =  e.within(this.list);
11685         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11686         
11687         if (in_combo || in_list || is_list) {
11688             //e.stopPropagation();
11689             return;
11690         }
11691         
11692         if(this.tickable){
11693             this.onTickableFooterButtonClick(e, false, false);
11694         }
11695
11696         this.collapse();
11697         
11698     },
11699
11700     /**
11701      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11702      */
11703     expand : function(){
11704        
11705         if(this.isExpanded() || !this.hasFocus){
11706             return;
11707         }
11708         
11709         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11710         this.list.setWidth(lw);
11711         
11712         
11713          Roo.log('expand');
11714         
11715         this.list.show();
11716         
11717         this.restrictHeight();
11718         
11719         if(this.tickable){
11720             
11721             this.tickItems = Roo.apply([], this.item);
11722             
11723             this.okBtn.show();
11724             this.cancelBtn.show();
11725             this.trigger.hide();
11726             
11727         }
11728         
11729         Roo.get(document).on('mousedown', this.collapseIf, this);
11730         Roo.get(document).on('mousewheel', this.collapseIf, this);
11731         if (!this.editable) {
11732             Roo.get(document).on('keydown', this.listKeyPress, this);
11733         }
11734         
11735         this.fireEvent('expand', this);
11736     },
11737
11738     // private
11739     // Implements the default empty TriggerField.onTriggerClick function
11740     onTriggerClick : function(e)
11741     {
11742         Roo.log('trigger click');
11743         
11744         if(this.disabled || !this.triggerList){
11745             return;
11746         }
11747         
11748         this.page = 0;
11749         this.loadNext = false;
11750         
11751         if(this.isExpanded()){
11752             this.collapse();
11753             if (!this.blockFocus) {
11754                 this.inputEl().focus();
11755             }
11756             
11757         }else {
11758             this.hasFocus = true;
11759             if(this.triggerAction == 'all') {
11760                 this.doQuery(this.allQuery, true);
11761             } else {
11762                 this.doQuery(this.getRawValue());
11763             }
11764             if (!this.blockFocus) {
11765                 this.inputEl().focus();
11766             }
11767         }
11768     },
11769     
11770     onTickableTriggerClick : function(e)
11771     {
11772         if(this.disabled){
11773             return;
11774         }
11775         
11776         this.page = 0;
11777         this.loadNext = false;
11778         this.hasFocus = true;
11779         
11780         if(this.triggerAction == 'all') {
11781             this.doQuery(this.allQuery, true);
11782         } else {
11783             this.doQuery(this.getRawValue());
11784         }
11785     },
11786     
11787     onSearchFieldClick : function(e)
11788     {
11789         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11790             return;
11791         }
11792         
11793         this.page = 0;
11794         this.loadNext = false;
11795         this.hasFocus = true;
11796         
11797         if(this.triggerAction == 'all') {
11798             this.doQuery(this.allQuery, true);
11799         } else {
11800             this.doQuery(this.getRawValue());
11801         }
11802     },
11803     
11804     listKeyPress : function(e)
11805     {
11806         //Roo.log('listkeypress');
11807         // scroll to first matching element based on key pres..
11808         if (e.isSpecialKey()) {
11809             return false;
11810         }
11811         var k = String.fromCharCode(e.getKey()).toUpperCase();
11812         //Roo.log(k);
11813         var match  = false;
11814         var csel = this.view.getSelectedNodes();
11815         var cselitem = false;
11816         if (csel.length) {
11817             var ix = this.view.indexOf(csel[0]);
11818             cselitem  = this.store.getAt(ix);
11819             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11820                 cselitem = false;
11821             }
11822             
11823         }
11824         
11825         this.store.each(function(v) { 
11826             if (cselitem) {
11827                 // start at existing selection.
11828                 if (cselitem.id == v.id) {
11829                     cselitem = false;
11830                 }
11831                 return true;
11832             }
11833                 
11834             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11835                 match = this.store.indexOf(v);
11836                 return false;
11837             }
11838             return true;
11839         }, this);
11840         
11841         if (match === false) {
11842             return true; // no more action?
11843         }
11844         // scroll to?
11845         this.view.select(match);
11846         var sn = Roo.get(this.view.getSelectedNodes()[0])
11847         sn.scrollIntoView(sn.dom.parentNode, false);
11848     },
11849     
11850     onViewScroll : function(e, t){
11851         
11852         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11853             return;
11854         }
11855         
11856         this.hasQuery = true;
11857         
11858         this.loading = this.list.select('.loading', true).first();
11859         
11860         if(this.loading === null){
11861             this.list.createChild({
11862                 tag: 'div',
11863                 cls: 'loading select2-more-results select2-active',
11864                 html: 'Loading more results...'
11865             })
11866             
11867             this.loading = this.list.select('.loading', true).first();
11868             
11869             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11870             
11871             this.loading.hide();
11872         }
11873         
11874         this.loading.show();
11875         
11876         var _combo = this;
11877         
11878         this.page++;
11879         this.loadNext = true;
11880         
11881         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11882         
11883         return;
11884     },
11885     
11886     addItem : function(o)
11887     {   
11888         var dv = ''; // display value
11889         
11890         if (this.displayField) {
11891             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11892         } else {
11893             // this is an error condition!!!
11894             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11895         }
11896         
11897         if(!dv.length){
11898             return;
11899         }
11900         
11901         var choice = this.choices.createChild({
11902             tag: 'li',
11903             cls: 'select2-search-choice',
11904             cn: [
11905                 {
11906                     tag: 'div',
11907                     html: dv
11908                 },
11909                 {
11910                     tag: 'a',
11911                     href: '#',
11912                     cls: 'select2-search-choice-close',
11913                     tabindex: '-1'
11914                 }
11915             ]
11916             
11917         }, this.searchField);
11918         
11919         var close = choice.select('a.select2-search-choice-close', true).first()
11920         
11921         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11922         
11923         this.item.push(o);
11924         
11925         this.lastData = o;
11926         
11927         this.syncValue();
11928         
11929         this.inputEl().dom.value = '';
11930         
11931     },
11932     
11933     onRemoveItem : function(e, _self, o)
11934     {
11935         e.preventDefault();
11936         var index = this.item.indexOf(o.data) * 1;
11937         
11938         if( index < 0){
11939             Roo.log('not this item?!');
11940             return;
11941         }
11942         
11943         this.item.splice(index, 1);
11944         o.item.remove();
11945         
11946         this.syncValue();
11947         
11948         this.fireEvent('remove', this, e);
11949         
11950     },
11951     
11952     syncValue : function()
11953     {
11954         if(!this.item.length){
11955             this.clearValue();
11956             return;
11957         }
11958             
11959         var value = [];
11960         var _this = this;
11961         Roo.each(this.item, function(i){
11962             if(_this.valueField){
11963                 value.push(i[_this.valueField]);
11964                 return;
11965             }
11966
11967             value.push(i);
11968         });
11969
11970         this.value = value.join(',');
11971
11972         if(this.hiddenField){
11973             this.hiddenField.dom.value = this.value;
11974         }
11975     },
11976     
11977     clearItem : function()
11978     {
11979         if(!this.multiple){
11980             return;
11981         }
11982         
11983         this.item = [];
11984         
11985         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11986            c.remove();
11987         });
11988         
11989         this.syncValue();
11990     },
11991     
11992     inputEl: function ()
11993     {
11994         if(this.tickable){
11995             return this.searchField;
11996         }
11997         return this.el.select('input.form-control',true).first();
11998     },
11999     
12000     
12001     onTickableFooterButtonClick : function(e, btn, el)
12002     {
12003         e.preventDefault();
12004         
12005         if(btn && btn.name == 'cancel'){
12006             this.tickItems = Roo.apply([], this.item);
12007             this.collapse();
12008             return;
12009         }
12010         
12011         this.clearItem();
12012         
12013         var _this = this;
12014         
12015         Roo.each(this.tickItems, function(o){
12016             _this.addItem(o);
12017         });
12018         
12019         this.collapse();
12020         
12021     }
12022     
12023     
12024
12025     /** 
12026     * @cfg {Boolean} grow 
12027     * @hide 
12028     */
12029     /** 
12030     * @cfg {Number} growMin 
12031     * @hide 
12032     */
12033     /** 
12034     * @cfg {Number} growMax 
12035     * @hide 
12036     */
12037     /**
12038      * @hide
12039      * @method autoSize
12040      */
12041 });
12042 /*
12043  * Based on:
12044  * Ext JS Library 1.1.1
12045  * Copyright(c) 2006-2007, Ext JS, LLC.
12046  *
12047  * Originally Released Under LGPL - original licence link has changed is not relivant.
12048  *
12049  * Fork - LGPL
12050  * <script type="text/javascript">
12051  */
12052
12053 /**
12054  * @class Roo.View
12055  * @extends Roo.util.Observable
12056  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12057  * This class also supports single and multi selection modes. <br>
12058  * Create a data model bound view:
12059  <pre><code>
12060  var store = new Roo.data.Store(...);
12061
12062  var view = new Roo.View({
12063     el : "my-element",
12064     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12065  
12066     singleSelect: true,
12067     selectedClass: "ydataview-selected",
12068     store: store
12069  });
12070
12071  // listen for node click?
12072  view.on("click", function(vw, index, node, e){
12073  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12074  });
12075
12076  // load XML data
12077  dataModel.load("foobar.xml");
12078  </code></pre>
12079  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12080  * <br><br>
12081  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12082  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12083  * 
12084  * Note: old style constructor is still suported (container, template, config)
12085  * 
12086  * @constructor
12087  * Create a new View
12088  * @param {Object} config The config object
12089  * 
12090  */
12091 Roo.View = function(config, depreciated_tpl, depreciated_config){
12092     
12093     this.parent = false;
12094     
12095     if (typeof(depreciated_tpl) == 'undefined') {
12096         // new way.. - universal constructor.
12097         Roo.apply(this, config);
12098         this.el  = Roo.get(this.el);
12099     } else {
12100         // old format..
12101         this.el  = Roo.get(config);
12102         this.tpl = depreciated_tpl;
12103         Roo.apply(this, depreciated_config);
12104     }
12105     this.wrapEl  = this.el.wrap().wrap();
12106     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12107     
12108     
12109     if(typeof(this.tpl) == "string"){
12110         this.tpl = new Roo.Template(this.tpl);
12111     } else {
12112         // support xtype ctors..
12113         this.tpl = new Roo.factory(this.tpl, Roo);
12114     }
12115     
12116     
12117     this.tpl.compile();
12118     
12119     /** @private */
12120     this.addEvents({
12121         /**
12122          * @event beforeclick
12123          * Fires before a click is processed. Returns false to cancel the default action.
12124          * @param {Roo.View} this
12125          * @param {Number} index The index of the target node
12126          * @param {HTMLElement} node The target node
12127          * @param {Roo.EventObject} e The raw event object
12128          */
12129             "beforeclick" : true,
12130         /**
12131          * @event click
12132          * Fires when a template node is clicked.
12133          * @param {Roo.View} this
12134          * @param {Number} index The index of the target node
12135          * @param {HTMLElement} node The target node
12136          * @param {Roo.EventObject} e The raw event object
12137          */
12138             "click" : true,
12139         /**
12140          * @event dblclick
12141          * Fires when a template node is double clicked.
12142          * @param {Roo.View} this
12143          * @param {Number} index The index of the target node
12144          * @param {HTMLElement} node The target node
12145          * @param {Roo.EventObject} e The raw event object
12146          */
12147             "dblclick" : true,
12148         /**
12149          * @event contextmenu
12150          * Fires when a template node is right clicked.
12151          * @param {Roo.View} this
12152          * @param {Number} index The index of the target node
12153          * @param {HTMLElement} node The target node
12154          * @param {Roo.EventObject} e The raw event object
12155          */
12156             "contextmenu" : true,
12157         /**
12158          * @event selectionchange
12159          * Fires when the selected nodes change.
12160          * @param {Roo.View} this
12161          * @param {Array} selections Array of the selected nodes
12162          */
12163             "selectionchange" : true,
12164     
12165         /**
12166          * @event beforeselect
12167          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12168          * @param {Roo.View} this
12169          * @param {HTMLElement} node The node to be selected
12170          * @param {Array} selections Array of currently selected nodes
12171          */
12172             "beforeselect" : true,
12173         /**
12174          * @event preparedata
12175          * Fires on every row to render, to allow you to change the data.
12176          * @param {Roo.View} this
12177          * @param {Object} data to be rendered (change this)
12178          */
12179           "preparedata" : true
12180           
12181           
12182         });
12183
12184
12185
12186     this.el.on({
12187         "click": this.onClick,
12188         "dblclick": this.onDblClick,
12189         "contextmenu": this.onContextMenu,
12190         scope:this
12191     });
12192
12193     this.selections = [];
12194     this.nodes = [];
12195     this.cmp = new Roo.CompositeElementLite([]);
12196     if(this.store){
12197         this.store = Roo.factory(this.store, Roo.data);
12198         this.setStore(this.store, true);
12199     }
12200     
12201     if ( this.footer && this.footer.xtype) {
12202            
12203          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12204         
12205         this.footer.dataSource = this.store
12206         this.footer.container = fctr;
12207         this.footer = Roo.factory(this.footer, Roo);
12208         fctr.insertFirst(this.el);
12209         
12210         // this is a bit insane - as the paging toolbar seems to detach the el..
12211 //        dom.parentNode.parentNode.parentNode
12212          // they get detached?
12213     }
12214     
12215     
12216     Roo.View.superclass.constructor.call(this);
12217     
12218     
12219 };
12220
12221 Roo.extend(Roo.View, Roo.util.Observable, {
12222     
12223      /**
12224      * @cfg {Roo.data.Store} store Data store to load data from.
12225      */
12226     store : false,
12227     
12228     /**
12229      * @cfg {String|Roo.Element} el The container element.
12230      */
12231     el : '',
12232     
12233     /**
12234      * @cfg {String|Roo.Template} tpl The template used by this View 
12235      */
12236     tpl : false,
12237     /**
12238      * @cfg {String} dataName the named area of the template to use as the data area
12239      *                          Works with domtemplates roo-name="name"
12240      */
12241     dataName: false,
12242     /**
12243      * @cfg {String} selectedClass The css class to add to selected nodes
12244      */
12245     selectedClass : "x-view-selected",
12246      /**
12247      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12248      */
12249     emptyText : "",
12250     
12251     /**
12252      * @cfg {String} text to display on mask (default Loading)
12253      */
12254     mask : false,
12255     /**
12256      * @cfg {Boolean} multiSelect Allow multiple selection
12257      */
12258     multiSelect : false,
12259     /**
12260      * @cfg {Boolean} singleSelect Allow single selection
12261      */
12262     singleSelect:  false,
12263     
12264     /**
12265      * @cfg {Boolean} toggleSelect - selecting 
12266      */
12267     toggleSelect : false,
12268     
12269     /**
12270      * @cfg {Boolean} tickable - selecting 
12271      */
12272     tickable : false,
12273     
12274     /**
12275      * Returns the element this view is bound to.
12276      * @return {Roo.Element}
12277      */
12278     getEl : function(){
12279         return this.wrapEl;
12280     },
12281     
12282     
12283
12284     /**
12285      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12286      */
12287     refresh : function(){
12288         Roo.log('refresh');
12289         var t = this.tpl;
12290         
12291         // if we are using something like 'domtemplate', then
12292         // the what gets used is:
12293         // t.applySubtemplate(NAME, data, wrapping data..)
12294         // the outer template then get' applied with
12295         //     the store 'extra data'
12296         // and the body get's added to the
12297         //      roo-name="data" node?
12298         //      <span class='roo-tpl-{name}'></span> ?????
12299         
12300         
12301         
12302         this.clearSelections();
12303         this.el.update("");
12304         var html = [];
12305         var records = this.store.getRange();
12306         if(records.length < 1) {
12307             
12308             // is this valid??  = should it render a template??
12309             
12310             this.el.update(this.emptyText);
12311             return;
12312         }
12313         var el = this.el;
12314         if (this.dataName) {
12315             this.el.update(t.apply(this.store.meta)); //????
12316             el = this.el.child('.roo-tpl-' + this.dataName);
12317         }
12318         
12319         for(var i = 0, len = records.length; i < len; i++){
12320             var data = this.prepareData(records[i].data, i, records[i]);
12321             this.fireEvent("preparedata", this, data, i, records[i]);
12322             
12323             var d = Roo.apply({}, data);
12324             
12325             if(this.tickable){
12326                 Roo.apply(d, {'roo-id' : Roo.id()});
12327                 
12328                 var _this = this;
12329             
12330                 Roo.each(this.parent.item, function(item){
12331                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12332                         return;
12333                     }
12334                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12335                 });
12336             }
12337             
12338             html[html.length] = Roo.util.Format.trim(
12339                 this.dataName ?
12340                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12341                     t.apply(d)
12342             );
12343         }
12344         
12345         
12346         
12347         el.update(html.join(""));
12348         this.nodes = el.dom.childNodes;
12349         this.updateIndexes(0);
12350     },
12351     
12352
12353     /**
12354      * Function to override to reformat the data that is sent to
12355      * the template for each node.
12356      * DEPRICATED - use the preparedata event handler.
12357      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12358      * a JSON object for an UpdateManager bound view).
12359      */
12360     prepareData : function(data, index, record)
12361     {
12362         this.fireEvent("preparedata", this, data, index, record);
12363         return data;
12364     },
12365
12366     onUpdate : function(ds, record){
12367          Roo.log('on update');   
12368         this.clearSelections();
12369         var index = this.store.indexOf(record);
12370         var n = this.nodes[index];
12371         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12372         n.parentNode.removeChild(n);
12373         this.updateIndexes(index, index);
12374     },
12375
12376     
12377     
12378 // --------- FIXME     
12379     onAdd : function(ds, records, index)
12380     {
12381         Roo.log(['on Add', ds, records, index] );        
12382         this.clearSelections();
12383         if(this.nodes.length == 0){
12384             this.refresh();
12385             return;
12386         }
12387         var n = this.nodes[index];
12388         for(var i = 0, len = records.length; i < len; i++){
12389             var d = this.prepareData(records[i].data, i, records[i]);
12390             if(n){
12391                 this.tpl.insertBefore(n, d);
12392             }else{
12393                 
12394                 this.tpl.append(this.el, d);
12395             }
12396         }
12397         this.updateIndexes(index);
12398     },
12399
12400     onRemove : function(ds, record, index){
12401         Roo.log('onRemove');
12402         this.clearSelections();
12403         var el = this.dataName  ?
12404             this.el.child('.roo-tpl-' + this.dataName) :
12405             this.el; 
12406         
12407         el.dom.removeChild(this.nodes[index]);
12408         this.updateIndexes(index);
12409     },
12410
12411     /**
12412      * Refresh an individual node.
12413      * @param {Number} index
12414      */
12415     refreshNode : function(index){
12416         this.onUpdate(this.store, this.store.getAt(index));
12417     },
12418
12419     updateIndexes : function(startIndex, endIndex){
12420         var ns = this.nodes;
12421         startIndex = startIndex || 0;
12422         endIndex = endIndex || ns.length - 1;
12423         for(var i = startIndex; i <= endIndex; i++){
12424             ns[i].nodeIndex = i;
12425         }
12426     },
12427
12428     /**
12429      * Changes the data store this view uses and refresh the view.
12430      * @param {Store} store
12431      */
12432     setStore : function(store, initial){
12433         if(!initial && this.store){
12434             this.store.un("datachanged", this.refresh);
12435             this.store.un("add", this.onAdd);
12436             this.store.un("remove", this.onRemove);
12437             this.store.un("update", this.onUpdate);
12438             this.store.un("clear", this.refresh);
12439             this.store.un("beforeload", this.onBeforeLoad);
12440             this.store.un("load", this.onLoad);
12441             this.store.un("loadexception", this.onLoad);
12442         }
12443         if(store){
12444           
12445             store.on("datachanged", this.refresh, this);
12446             store.on("add", this.onAdd, this);
12447             store.on("remove", this.onRemove, this);
12448             store.on("update", this.onUpdate, this);
12449             store.on("clear", this.refresh, this);
12450             store.on("beforeload", this.onBeforeLoad, this);
12451             store.on("load", this.onLoad, this);
12452             store.on("loadexception", this.onLoad, this);
12453         }
12454         
12455         if(store){
12456             this.refresh();
12457         }
12458     },
12459     /**
12460      * onbeforeLoad - masks the loading area.
12461      *
12462      */
12463     onBeforeLoad : function(store,opts)
12464     {
12465          Roo.log('onBeforeLoad');   
12466         if (!opts.add) {
12467             this.el.update("");
12468         }
12469         this.el.mask(this.mask ? this.mask : "Loading" ); 
12470     },
12471     onLoad : function ()
12472     {
12473         this.el.unmask();
12474     },
12475     
12476
12477     /**
12478      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12479      * @param {HTMLElement} node
12480      * @return {HTMLElement} The template node
12481      */
12482     findItemFromChild : function(node){
12483         var el = this.dataName  ?
12484             this.el.child('.roo-tpl-' + this.dataName,true) :
12485             this.el.dom; 
12486         
12487         if(!node || node.parentNode == el){
12488                     return node;
12489             }
12490             var p = node.parentNode;
12491             while(p && p != el){
12492             if(p.parentNode == el){
12493                 return p;
12494             }
12495             p = p.parentNode;
12496         }
12497             return null;
12498     },
12499
12500     /** @ignore */
12501     onClick : function(e){
12502         var item = this.findItemFromChild(e.getTarget());
12503         if(item){
12504             var index = this.indexOf(item);
12505             if(this.onItemClick(item, index, e) !== false){
12506                 this.fireEvent("click", this, index, item, e);
12507             }
12508         }else{
12509             this.clearSelections();
12510         }
12511     },
12512
12513     /** @ignore */
12514     onContextMenu : function(e){
12515         var item = this.findItemFromChild(e.getTarget());
12516         if(item){
12517             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12518         }
12519     },
12520
12521     /** @ignore */
12522     onDblClick : function(e){
12523         var item = this.findItemFromChild(e.getTarget());
12524         if(item){
12525             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12526         }
12527     },
12528
12529     onItemClick : function(item, index, e)
12530     {
12531         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12532             return false;
12533         }
12534         if (this.toggleSelect) {
12535             var m = this.isSelected(item) ? 'unselect' : 'select';
12536             Roo.log(m);
12537             var _t = this;
12538             _t[m](item, true, false);
12539             return true;
12540         }
12541         if(this.multiSelect || this.singleSelect){
12542             if(this.multiSelect && e.shiftKey && this.lastSelection){
12543                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12544             }else{
12545                 this.select(item, this.multiSelect && e.ctrlKey);
12546                 this.lastSelection = item;
12547             }
12548             
12549             if(!this.tickable){
12550                 e.preventDefault();
12551             }
12552             
12553         }
12554         return true;
12555     },
12556
12557     /**
12558      * Get the number of selected nodes.
12559      * @return {Number}
12560      */
12561     getSelectionCount : function(){
12562         return this.selections.length;
12563     },
12564
12565     /**
12566      * Get the currently selected nodes.
12567      * @return {Array} An array of HTMLElements
12568      */
12569     getSelectedNodes : function(){
12570         return this.selections;
12571     },
12572
12573     /**
12574      * Get the indexes of the selected nodes.
12575      * @return {Array}
12576      */
12577     getSelectedIndexes : function(){
12578         var indexes = [], s = this.selections;
12579         for(var i = 0, len = s.length; i < len; i++){
12580             indexes.push(s[i].nodeIndex);
12581         }
12582         return indexes;
12583     },
12584
12585     /**
12586      * Clear all selections
12587      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12588      */
12589     clearSelections : function(suppressEvent){
12590         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12591             this.cmp.elements = this.selections;
12592             this.cmp.removeClass(this.selectedClass);
12593             this.selections = [];
12594             if(!suppressEvent){
12595                 this.fireEvent("selectionchange", this, this.selections);
12596             }
12597         }
12598     },
12599
12600     /**
12601      * Returns true if the passed node is selected
12602      * @param {HTMLElement/Number} node The node or node index
12603      * @return {Boolean}
12604      */
12605     isSelected : function(node){
12606         var s = this.selections;
12607         if(s.length < 1){
12608             return false;
12609         }
12610         node = this.getNode(node);
12611         return s.indexOf(node) !== -1;
12612     },
12613
12614     /**
12615      * Selects nodes.
12616      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
12617      * @param {Boolean} keepExisting (optional) true to keep existing selections
12618      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12619      */
12620     select : function(nodeInfo, keepExisting, suppressEvent){
12621         if(nodeInfo instanceof Array){
12622             if(!keepExisting){
12623                 this.clearSelections(true);
12624             }
12625             for(var i = 0, len = nodeInfo.length; i < len; i++){
12626                 this.select(nodeInfo[i], true, true);
12627             }
12628             return;
12629         } 
12630         var node = this.getNode(nodeInfo);
12631         if(!node || this.isSelected(node)){
12632             return; // already selected.
12633         }
12634         if(!keepExisting){
12635             this.clearSelections(true);
12636         }
12637         
12638         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12639             Roo.fly(node).addClass(this.selectedClass);
12640             this.selections.push(node);
12641             if(!suppressEvent){
12642                 this.fireEvent("selectionchange", this, this.selections);
12643             }
12644         }
12645         
12646         
12647     },
12648       /**
12649      * Unselects nodes.
12650      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
12651      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12652      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12653      */
12654     unselect : function(nodeInfo, keepExisting, suppressEvent)
12655     {
12656         if(nodeInfo instanceof Array){
12657             Roo.each(this.selections, function(s) {
12658                 this.unselect(s, nodeInfo);
12659             }, this);
12660             return;
12661         }
12662         var node = this.getNode(nodeInfo);
12663         if(!node || !this.isSelected(node)){
12664             Roo.log("not selected");
12665             return; // not selected.
12666         }
12667         // fireevent???
12668         var ns = [];
12669         Roo.each(this.selections, function(s) {
12670             if (s == node ) {
12671                 Roo.fly(node).removeClass(this.selectedClass);
12672
12673                 return;
12674             }
12675             ns.push(s);
12676         },this);
12677         
12678         this.selections= ns;
12679         this.fireEvent("selectionchange", this, this.selections);
12680     },
12681
12682     /**
12683      * Gets a template node.
12684      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12685      * @return {HTMLElement} The node or null if it wasn't found
12686      */
12687     getNode : function(nodeInfo){
12688         if(typeof nodeInfo == "string"){
12689             return document.getElementById(nodeInfo);
12690         }else if(typeof nodeInfo == "number"){
12691             return this.nodes[nodeInfo];
12692         }
12693         return nodeInfo;
12694     },
12695
12696     /**
12697      * Gets a range template nodes.
12698      * @param {Number} startIndex
12699      * @param {Number} endIndex
12700      * @return {Array} An array of nodes
12701      */
12702     getNodes : function(start, end){
12703         var ns = this.nodes;
12704         start = start || 0;
12705         end = typeof end == "undefined" ? ns.length - 1 : end;
12706         var nodes = [];
12707         if(start <= end){
12708             for(var i = start; i <= end; i++){
12709                 nodes.push(ns[i]);
12710             }
12711         } else{
12712             for(var i = start; i >= end; i--){
12713                 nodes.push(ns[i]);
12714             }
12715         }
12716         return nodes;
12717     },
12718
12719     /**
12720      * Finds the index of the passed node
12721      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12722      * @return {Number} The index of the node or -1
12723      */
12724     indexOf : function(node){
12725         node = this.getNode(node);
12726         if(typeof node.nodeIndex == "number"){
12727             return node.nodeIndex;
12728         }
12729         var ns = this.nodes;
12730         for(var i = 0, len = ns.length; i < len; i++){
12731             if(ns[i] == node){
12732                 return i;
12733             }
12734         }
12735         return -1;
12736     }
12737 });
12738 /*
12739  * - LGPL
12740  *
12741  * based on jquery fullcalendar
12742  * 
12743  */
12744
12745 Roo.bootstrap = Roo.bootstrap || {};
12746 /**
12747  * @class Roo.bootstrap.Calendar
12748  * @extends Roo.bootstrap.Component
12749  * Bootstrap Calendar class
12750  * @cfg {Boolean} loadMask (true|false) default false
12751  * @cfg {Object} header generate the user specific header of the calendar, default false
12752
12753  * @constructor
12754  * Create a new Container
12755  * @param {Object} config The config object
12756  */
12757
12758
12759
12760 Roo.bootstrap.Calendar = function(config){
12761     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12762      this.addEvents({
12763         /**
12764              * @event select
12765              * Fires when a date is selected
12766              * @param {DatePicker} this
12767              * @param {Date} date The selected date
12768              */
12769         'select': true,
12770         /**
12771              * @event monthchange
12772              * Fires when the displayed month changes 
12773              * @param {DatePicker} this
12774              * @param {Date} date The selected month
12775              */
12776         'monthchange': true,
12777         /**
12778              * @event evententer
12779              * Fires when mouse over an event
12780              * @param {Calendar} this
12781              * @param {event} Event
12782              */
12783         'evententer': true,
12784         /**
12785              * @event eventleave
12786              * Fires when the mouse leaves an
12787              * @param {Calendar} this
12788              * @param {event}
12789              */
12790         'eventleave': true,
12791         /**
12792              * @event eventclick
12793              * Fires when the mouse click an
12794              * @param {Calendar} this
12795              * @param {event}
12796              */
12797         'eventclick': true
12798         
12799     });
12800
12801 };
12802
12803 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12804     
12805      /**
12806      * @cfg {Number} startDay
12807      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12808      */
12809     startDay : 0,
12810     
12811     loadMask : false,
12812     
12813     header : false,
12814       
12815     getAutoCreate : function(){
12816         
12817         
12818         var fc_button = function(name, corner, style, content ) {
12819             return Roo.apply({},{
12820                 tag : 'span',
12821                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12822                          (corner.length ?
12823                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12824                             ''
12825                         ),
12826                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12827                 unselectable: 'on'
12828             });
12829         };
12830         
12831         var header = {};
12832         
12833         if(!this.header){
12834             header = {
12835                 tag : 'table',
12836                 cls : 'fc-header',
12837                 style : 'width:100%',
12838                 cn : [
12839                     {
12840                         tag: 'tr',
12841                         cn : [
12842                             {
12843                                 tag : 'td',
12844                                 cls : 'fc-header-left',
12845                                 cn : [
12846                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12847                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12848                                     { tag: 'span', cls: 'fc-header-space' },
12849                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12850
12851
12852                                 ]
12853                             },
12854
12855                             {
12856                                 tag : 'td',
12857                                 cls : 'fc-header-center',
12858                                 cn : [
12859                                     {
12860                                         tag: 'span',
12861                                         cls: 'fc-header-title',
12862                                         cn : {
12863                                             tag: 'H2',
12864                                             html : 'month / year'
12865                                         }
12866                                     }
12867
12868                                 ]
12869                             },
12870                             {
12871                                 tag : 'td',
12872                                 cls : 'fc-header-right',
12873                                 cn : [
12874                               /*      fc_button('month', 'left', '', 'month' ),
12875                                     fc_button('week', '', '', 'week' ),
12876                                     fc_button('day', 'right', '', 'day' )
12877                                 */    
12878
12879                                 ]
12880                             }
12881
12882                         ]
12883                     }
12884                 ]
12885             };
12886         }
12887         
12888         header = this.header;
12889         
12890        
12891         var cal_heads = function() {
12892             var ret = [];
12893             // fixme - handle this.
12894             
12895             for (var i =0; i < Date.dayNames.length; i++) {
12896                 var d = Date.dayNames[i];
12897                 ret.push({
12898                     tag: 'th',
12899                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12900                     html : d.substring(0,3)
12901                 });
12902                 
12903             }
12904             ret[0].cls += ' fc-first';
12905             ret[6].cls += ' fc-last';
12906             return ret;
12907         };
12908         var cal_cell = function(n) {
12909             return  {
12910                 tag: 'td',
12911                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12912                 cn : [
12913                     {
12914                         cn : [
12915                             {
12916                                 cls: 'fc-day-number',
12917                                 html: 'D'
12918                             },
12919                             {
12920                                 cls: 'fc-day-content',
12921                              
12922                                 cn : [
12923                                      {
12924                                         style: 'position: relative;' // height: 17px;
12925                                     }
12926                                 ]
12927                             }
12928                             
12929                             
12930                         ]
12931                     }
12932                 ]
12933                 
12934             }
12935         };
12936         var cal_rows = function() {
12937             
12938             var ret = []
12939             for (var r = 0; r < 6; r++) {
12940                 var row= {
12941                     tag : 'tr',
12942                     cls : 'fc-week',
12943                     cn : []
12944                 };
12945                 
12946                 for (var i =0; i < Date.dayNames.length; i++) {
12947                     var d = Date.dayNames[i];
12948                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12949
12950                 }
12951                 row.cn[0].cls+=' fc-first';
12952                 row.cn[0].cn[0].style = 'min-height:90px';
12953                 row.cn[6].cls+=' fc-last';
12954                 ret.push(row);
12955                 
12956             }
12957             ret[0].cls += ' fc-first';
12958             ret[4].cls += ' fc-prev-last';
12959             ret[5].cls += ' fc-last';
12960             return ret;
12961             
12962         };
12963         
12964         var cal_table = {
12965             tag: 'table',
12966             cls: 'fc-border-separate',
12967             style : 'width:100%',
12968             cellspacing  : 0,
12969             cn : [
12970                 { 
12971                     tag: 'thead',
12972                     cn : [
12973                         { 
12974                             tag: 'tr',
12975                             cls : 'fc-first fc-last',
12976                             cn : cal_heads()
12977                         }
12978                     ]
12979                 },
12980                 { 
12981                     tag: 'tbody',
12982                     cn : cal_rows()
12983                 }
12984                   
12985             ]
12986         };
12987          
12988          var cfg = {
12989             cls : 'fc fc-ltr',
12990             cn : [
12991                 header,
12992                 {
12993                     cls : 'fc-content',
12994                     style : "position: relative;",
12995                     cn : [
12996                         {
12997                             cls : 'fc-view fc-view-month fc-grid',
12998                             style : 'position: relative',
12999                             unselectable : 'on',
13000                             cn : [
13001                                 {
13002                                     cls : 'fc-event-container',
13003                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13004                                 },
13005                                 cal_table
13006                             ]
13007                         }
13008                     ]
13009     
13010                 }
13011            ] 
13012             
13013         };
13014         
13015          
13016         
13017         return cfg;
13018     },
13019     
13020     
13021     initEvents : function()
13022     {
13023         if(!this.store){
13024             throw "can not find store for calendar";
13025         }
13026         
13027         var mark = {
13028             tag: "div",
13029             cls:"x-dlg-mask",
13030             style: "text-align:center",
13031             cn: [
13032                 {
13033                     tag: "div",
13034                     style: "background-color:white;width:50%;margin:250 auto",
13035                     cn: [
13036                         {
13037                             tag: "img",
13038                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13039                         },
13040                         {
13041                             tag: "span",
13042                             html: "Loading"
13043                         }
13044                         
13045                     ]
13046                 }
13047             ]
13048         }
13049         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13050         
13051         var size = this.el.select('.fc-content', true).first().getSize();
13052         this.maskEl.setSize(size.width, size.height);
13053         this.maskEl.enableDisplayMode("block");
13054         if(!this.loadMask){
13055             this.maskEl.hide();
13056         }
13057         
13058         this.store = Roo.factory(this.store, Roo.data);
13059         this.store.on('load', this.onLoad, this);
13060         this.store.on('beforeload', this.onBeforeLoad, this);
13061         
13062         this.resize();
13063         
13064         this.cells = this.el.select('.fc-day',true);
13065         //Roo.log(this.cells);
13066         this.textNodes = this.el.query('.fc-day-number');
13067         this.cells.addClassOnOver('fc-state-hover');
13068         
13069         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13070         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13071         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13072         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13073         
13074         this.on('monthchange', this.onMonthChange, this);
13075         
13076         this.update(new Date().clearTime());
13077     },
13078     
13079     resize : function() {
13080         var sz  = this.el.getSize();
13081         
13082         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13083         this.el.select('.fc-day-content div',true).setHeight(34);
13084     },
13085     
13086     
13087     // private
13088     showPrevMonth : function(e){
13089         this.update(this.activeDate.add("mo", -1));
13090     },
13091     showToday : function(e){
13092         this.update(new Date().clearTime());
13093     },
13094     // private
13095     showNextMonth : function(e){
13096         this.update(this.activeDate.add("mo", 1));
13097     },
13098
13099     // private
13100     showPrevYear : function(){
13101         this.update(this.activeDate.add("y", -1));
13102     },
13103
13104     // private
13105     showNextYear : function(){
13106         this.update(this.activeDate.add("y", 1));
13107     },
13108
13109     
13110    // private
13111     update : function(date)
13112     {
13113         var vd = this.activeDate;
13114         this.activeDate = date;
13115 //        if(vd && this.el){
13116 //            var t = date.getTime();
13117 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13118 //                Roo.log('using add remove');
13119 //                
13120 //                this.fireEvent('monthchange', this, date);
13121 //                
13122 //                this.cells.removeClass("fc-state-highlight");
13123 //                this.cells.each(function(c){
13124 //                   if(c.dateValue == t){
13125 //                       c.addClass("fc-state-highlight");
13126 //                       setTimeout(function(){
13127 //                            try{c.dom.firstChild.focus();}catch(e){}
13128 //                       }, 50);
13129 //                       return false;
13130 //                   }
13131 //                   return true;
13132 //                });
13133 //                return;
13134 //            }
13135 //        }
13136         
13137         var days = date.getDaysInMonth();
13138         
13139         var firstOfMonth = date.getFirstDateOfMonth();
13140         var startingPos = firstOfMonth.getDay()-this.startDay;
13141         
13142         if(startingPos < this.startDay){
13143             startingPos += 7;
13144         }
13145         
13146         var pm = date.add(Date.MONTH, -1);
13147         var prevStart = pm.getDaysInMonth()-startingPos;
13148 //        
13149         this.cells = this.el.select('.fc-day',true);
13150         this.textNodes = this.el.query('.fc-day-number');
13151         this.cells.addClassOnOver('fc-state-hover');
13152         
13153         var cells = this.cells.elements;
13154         var textEls = this.textNodes;
13155         
13156         Roo.each(cells, function(cell){
13157             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13158         });
13159         
13160         days += startingPos;
13161
13162         // convert everything to numbers so it's fast
13163         var day = 86400000;
13164         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13165         //Roo.log(d);
13166         //Roo.log(pm);
13167         //Roo.log(prevStart);
13168         
13169         var today = new Date().clearTime().getTime();
13170         var sel = date.clearTime().getTime();
13171         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13172         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13173         var ddMatch = this.disabledDatesRE;
13174         var ddText = this.disabledDatesText;
13175         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13176         var ddaysText = this.disabledDaysText;
13177         var format = this.format;
13178         
13179         var setCellClass = function(cal, cell){
13180             cell.row = 0;
13181             cell.events = [];
13182             cell.more = [];
13183             //Roo.log('set Cell Class');
13184             cell.title = "";
13185             var t = d.getTime();
13186             
13187             //Roo.log(d);
13188             
13189             cell.dateValue = t;
13190             if(t == today){
13191                 cell.className += " fc-today";
13192                 cell.className += " fc-state-highlight";
13193                 cell.title = cal.todayText;
13194             }
13195             if(t == sel){
13196                 // disable highlight in other month..
13197                 //cell.className += " fc-state-highlight";
13198                 
13199             }
13200             // disabling
13201             if(t < min) {
13202                 cell.className = " fc-state-disabled";
13203                 cell.title = cal.minText;
13204                 return;
13205             }
13206             if(t > max) {
13207                 cell.className = " fc-state-disabled";
13208                 cell.title = cal.maxText;
13209                 return;
13210             }
13211             if(ddays){
13212                 if(ddays.indexOf(d.getDay()) != -1){
13213                     cell.title = ddaysText;
13214                     cell.className = " fc-state-disabled";
13215                 }
13216             }
13217             if(ddMatch && format){
13218                 var fvalue = d.dateFormat(format);
13219                 if(ddMatch.test(fvalue)){
13220                     cell.title = ddText.replace("%0", fvalue);
13221                     cell.className = " fc-state-disabled";
13222                 }
13223             }
13224             
13225             if (!cell.initialClassName) {
13226                 cell.initialClassName = cell.dom.className;
13227             }
13228             
13229             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13230         };
13231
13232         var i = 0;
13233         
13234         for(; i < startingPos; i++) {
13235             textEls[i].innerHTML = (++prevStart);
13236             d.setDate(d.getDate()+1);
13237             
13238             cells[i].className = "fc-past fc-other-month";
13239             setCellClass(this, cells[i]);
13240         }
13241         
13242         var intDay = 0;
13243         
13244         for(; i < days; i++){
13245             intDay = i - startingPos + 1;
13246             textEls[i].innerHTML = (intDay);
13247             d.setDate(d.getDate()+1);
13248             
13249             cells[i].className = ''; // "x-date-active";
13250             setCellClass(this, cells[i]);
13251         }
13252         var extraDays = 0;
13253         
13254         for(; i < 42; i++) {
13255             textEls[i].innerHTML = (++extraDays);
13256             d.setDate(d.getDate()+1);
13257             
13258             cells[i].className = "fc-future fc-other-month";
13259             setCellClass(this, cells[i]);
13260         }
13261         
13262         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13263         
13264         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13265         
13266         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13267         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13268         
13269         if(totalRows != 6){
13270             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13271             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13272         }
13273         
13274         this.fireEvent('monthchange', this, date);
13275         
13276         
13277         /*
13278         if(!this.internalRender){
13279             var main = this.el.dom.firstChild;
13280             var w = main.offsetWidth;
13281             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13282             Roo.fly(main).setWidth(w);
13283             this.internalRender = true;
13284             // opera does not respect the auto grow header center column
13285             // then, after it gets a width opera refuses to recalculate
13286             // without a second pass
13287             if(Roo.isOpera && !this.secondPass){
13288                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13289                 this.secondPass = true;
13290                 this.update.defer(10, this, [date]);
13291             }
13292         }
13293         */
13294         
13295     },
13296     
13297     findCell : function(dt) {
13298         dt = dt.clearTime().getTime();
13299         var ret = false;
13300         this.cells.each(function(c){
13301             //Roo.log("check " +c.dateValue + '?=' + dt);
13302             if(c.dateValue == dt){
13303                 ret = c;
13304                 return false;
13305             }
13306             return true;
13307         });
13308         
13309         return ret;
13310     },
13311     
13312     findCells : function(ev) {
13313         var s = ev.start.clone().clearTime().getTime();
13314        // Roo.log(s);
13315         var e= ev.end.clone().clearTime().getTime();
13316        // Roo.log(e);
13317         var ret = [];
13318         this.cells.each(function(c){
13319              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13320             
13321             if(c.dateValue > e){
13322                 return ;
13323             }
13324             if(c.dateValue < s){
13325                 return ;
13326             }
13327             ret.push(c);
13328         });
13329         
13330         return ret;    
13331     },
13332     
13333 //    findBestRow: function(cells)
13334 //    {
13335 //        var ret = 0;
13336 //        
13337 //        for (var i =0 ; i < cells.length;i++) {
13338 //            ret  = Math.max(cells[i].rows || 0,ret);
13339 //        }
13340 //        return ret;
13341 //        
13342 //    },
13343     
13344     
13345     addItem : function(ev)
13346     {
13347         // look for vertical location slot in
13348         var cells = this.findCells(ev);
13349         
13350 //        ev.row = this.findBestRow(cells);
13351         
13352         // work out the location.
13353         
13354         var crow = false;
13355         var rows = [];
13356         for(var i =0; i < cells.length; i++) {
13357             
13358             cells[i].row = cells[0].row;
13359             
13360             if(i == 0){
13361                 cells[i].row = cells[i].row + 1;
13362             }
13363             
13364             if (!crow) {
13365                 crow = {
13366                     start : cells[i],
13367                     end :  cells[i]
13368                 };
13369                 continue;
13370             }
13371             if (crow.start.getY() == cells[i].getY()) {
13372                 // on same row.
13373                 crow.end = cells[i];
13374                 continue;
13375             }
13376             // different row.
13377             rows.push(crow);
13378             crow = {
13379                 start: cells[i],
13380                 end : cells[i]
13381             };
13382             
13383         }
13384         
13385         rows.push(crow);
13386         ev.els = [];
13387         ev.rows = rows;
13388         ev.cells = cells;
13389         
13390         cells[0].events.push(ev);
13391         
13392         this.calevents.push(ev);
13393     },
13394     
13395     clearEvents: function() {
13396         
13397         if(!this.calevents){
13398             return;
13399         }
13400         
13401         Roo.each(this.cells.elements, function(c){
13402             c.row = 0;
13403             c.events = [];
13404             c.more = [];
13405         });
13406         
13407         Roo.each(this.calevents, function(e) {
13408             Roo.each(e.els, function(el) {
13409                 el.un('mouseenter' ,this.onEventEnter, this);
13410                 el.un('mouseleave' ,this.onEventLeave, this);
13411                 el.remove();
13412             },this);
13413         },this);
13414         
13415         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13416             e.remove();
13417         });
13418         
13419     },
13420     
13421     renderEvents: function()
13422     {   
13423         var _this = this;
13424         
13425         this.cells.each(function(c) {
13426             
13427             if(c.row < 5){
13428                 return;
13429             }
13430             
13431             var ev = c.events;
13432             
13433             var r = 4;
13434             if(c.row != c.events.length){
13435                 r = 4 - (4 - (c.row - c.events.length));
13436             }
13437             
13438             c.events = ev.slice(0, r);
13439             c.more = ev.slice(r);
13440             
13441             if(c.more.length && c.more.length == 1){
13442                 c.events.push(c.more.pop());
13443             }
13444             
13445             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13446             
13447         });
13448             
13449         this.cells.each(function(c) {
13450             
13451             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13452             
13453             
13454             for (var e = 0; e < c.events.length; e++){
13455                 var ev = c.events[e];
13456                 var rows = ev.rows;
13457                 
13458                 for(var i = 0; i < rows.length; i++) {
13459                 
13460                     // how many rows should it span..
13461
13462                     var  cfg = {
13463                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13464                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13465
13466                         unselectable : "on",
13467                         cn : [
13468                             {
13469                                 cls: 'fc-event-inner',
13470                                 cn : [
13471     //                                {
13472     //                                  tag:'span',
13473     //                                  cls: 'fc-event-time',
13474     //                                  html : cells.length > 1 ? '' : ev.time
13475     //                                },
13476                                     {
13477                                       tag:'span',
13478                                       cls: 'fc-event-title',
13479                                       html : String.format('{0}', ev.title)
13480                                     }
13481
13482
13483                                 ]
13484                             },
13485                             {
13486                                 cls: 'ui-resizable-handle ui-resizable-e',
13487                                 html : '&nbsp;&nbsp;&nbsp'
13488                             }
13489
13490                         ]
13491                     };
13492
13493                     if (i == 0) {
13494                         cfg.cls += ' fc-event-start';
13495                     }
13496                     if ((i+1) == rows.length) {
13497                         cfg.cls += ' fc-event-end';
13498                     }
13499
13500                     var ctr = _this.el.select('.fc-event-container',true).first();
13501                     var cg = ctr.createChild(cfg);
13502
13503                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13504                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13505
13506                     var r = (c.more.length) ? 1 : 0;
13507                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13508                     cg.setWidth(ebox.right - sbox.x -2);
13509
13510                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13511                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13512                     cg.on('click', _this.onEventClick, _this, ev);
13513
13514                     ev.els.push(cg);
13515                     
13516                 }
13517                 
13518             }
13519             
13520             
13521             if(c.more.length){
13522                 var  cfg = {
13523                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13524                     style : 'position: absolute',
13525                     unselectable : "on",
13526                     cn : [
13527                         {
13528                             cls: 'fc-event-inner',
13529                             cn : [
13530                                 {
13531                                   tag:'span',
13532                                   cls: 'fc-event-title',
13533                                   html : 'More'
13534                                 }
13535
13536
13537                             ]
13538                         },
13539                         {
13540                             cls: 'ui-resizable-handle ui-resizable-e',
13541                             html : '&nbsp;&nbsp;&nbsp'
13542                         }
13543
13544                     ]
13545                 };
13546
13547                 var ctr = _this.el.select('.fc-event-container',true).first();
13548                 var cg = ctr.createChild(cfg);
13549
13550                 var sbox = c.select('.fc-day-content',true).first().getBox();
13551                 var ebox = c.select('.fc-day-content',true).first().getBox();
13552                 //Roo.log(cg);
13553                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13554                 cg.setWidth(ebox.right - sbox.x -2);
13555
13556                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13557                 
13558             }
13559             
13560         });
13561         
13562         
13563         
13564     },
13565     
13566     onEventEnter: function (e, el,event,d) {
13567         this.fireEvent('evententer', this, el, event);
13568     },
13569     
13570     onEventLeave: function (e, el,event,d) {
13571         this.fireEvent('eventleave', this, el, event);
13572     },
13573     
13574     onEventClick: function (e, el,event,d) {
13575         this.fireEvent('eventclick', this, el, event);
13576     },
13577     
13578     onMonthChange: function () {
13579         this.store.load();
13580     },
13581     
13582     onMoreEventClick: function(e, el, more)
13583     {
13584         var _this = this;
13585         
13586         this.calpopover.placement = 'right';
13587         this.calpopover.setTitle('More');
13588         
13589         this.calpopover.setContent('');
13590         
13591         var ctr = this.calpopover.el.select('.popover-content', true).first();
13592         
13593         Roo.each(more, function(m){
13594             var cfg = {
13595                 cls : 'fc-event-hori fc-event-draggable',
13596                 html : m.title
13597             }
13598             var cg = ctr.createChild(cfg);
13599             
13600             cg.on('click', _this.onEventClick, _this, m);
13601         });
13602         
13603         this.calpopover.show(el);
13604         
13605         
13606     },
13607     
13608     onLoad: function () 
13609     {   
13610         this.calevents = [];
13611         var cal = this;
13612         
13613         if(this.store.getCount() > 0){
13614             this.store.data.each(function(d){
13615                cal.addItem({
13616                     id : d.data.id,
13617                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13618                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13619                     time : d.data.start_time,
13620                     title : d.data.title,
13621                     description : d.data.description,
13622                     venue : d.data.venue
13623                 });
13624             });
13625         }
13626         
13627         this.renderEvents();
13628         
13629         if(this.calevents.length && this.loadMask){
13630             this.maskEl.hide();
13631         }
13632     },
13633     
13634     onBeforeLoad: function()
13635     {
13636         this.clearEvents();
13637         if(this.loadMask){
13638             this.maskEl.show();
13639         }
13640     }
13641 });
13642
13643  
13644  /*
13645  * - LGPL
13646  *
13647  * element
13648  * 
13649  */
13650
13651 /**
13652  * @class Roo.bootstrap.Popover
13653  * @extends Roo.bootstrap.Component
13654  * Bootstrap Popover class
13655  * @cfg {String} html contents of the popover   (or false to use children..)
13656  * @cfg {String} title of popover (or false to hide)
13657  * @cfg {String} placement how it is placed
13658  * @cfg {String} trigger click || hover (or false to trigger manually)
13659  * @cfg {String} over what (parent or false to trigger manually.)
13660  * @cfg {Number} delay - delay before showing
13661  
13662  * @constructor
13663  * Create a new Popover
13664  * @param {Object} config The config object
13665  */
13666
13667 Roo.bootstrap.Popover = function(config){
13668     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13669 };
13670
13671 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13672     
13673     title: 'Fill in a title',
13674     html: false,
13675     
13676     placement : 'right',
13677     trigger : 'hover', // hover
13678     
13679     delay : 0,
13680     
13681     over: 'parent',
13682     
13683     can_build_overlaid : false,
13684     
13685     getChildContainer : function()
13686     {
13687         return this.el.select('.popover-content',true).first();
13688     },
13689     
13690     getAutoCreate : function(){
13691          Roo.log('make popover?');
13692         var cfg = {
13693            cls : 'popover roo-dynamic',
13694            style: 'display:block',
13695            cn : [
13696                 {
13697                     cls : 'arrow'
13698                 },
13699                 {
13700                     cls : 'popover-inner',
13701                     cn : [
13702                         {
13703                             tag: 'h3',
13704                             cls: 'popover-title',
13705                             html : this.title
13706                         },
13707                         {
13708                             cls : 'popover-content',
13709                             html : this.html
13710                         }
13711                     ]
13712                     
13713                 }
13714            ]
13715         };
13716         
13717         return cfg;
13718     },
13719     setTitle: function(str)
13720     {
13721         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13722     },
13723     setContent: function(str)
13724     {
13725         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13726     },
13727     // as it get's added to the bottom of the page.
13728     onRender : function(ct, position)
13729     {
13730         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13731         if(!this.el){
13732             var cfg = Roo.apply({},  this.getAutoCreate());
13733             cfg.id = Roo.id();
13734             
13735             if (this.cls) {
13736                 cfg.cls += ' ' + this.cls;
13737             }
13738             if (this.style) {
13739                 cfg.style = this.style;
13740             }
13741             Roo.log("adding to ")
13742             this.el = Roo.get(document.body).createChild(cfg, position);
13743             Roo.log(this.el);
13744         }
13745         this.initEvents();
13746     },
13747     
13748     initEvents : function()
13749     {
13750         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13751         this.el.enableDisplayMode('block');
13752         this.el.hide();
13753         if (this.over === false) {
13754             return; 
13755         }
13756         if (this.triggers === false) {
13757             return;
13758         }
13759         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13760         var triggers = this.trigger ? this.trigger.split(' ') : [];
13761         Roo.each(triggers, function(trigger) {
13762         
13763             if (trigger == 'click') {
13764                 on_el.on('click', this.toggle, this);
13765             } else if (trigger != 'manual') {
13766                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13767                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13768       
13769                 on_el.on(eventIn  ,this.enter, this);
13770                 on_el.on(eventOut, this.leave, this);
13771             }
13772         }, this);
13773         
13774     },
13775     
13776     
13777     // private
13778     timeout : null,
13779     hoverState : null,
13780     
13781     toggle : function () {
13782         this.hoverState == 'in' ? this.leave() : this.enter();
13783     },
13784     
13785     enter : function () {
13786        
13787     
13788         clearTimeout(this.timeout);
13789     
13790         this.hoverState = 'in'
13791     
13792         if (!this.delay || !this.delay.show) {
13793             this.show();
13794             return 
13795         }
13796         var _t = this;
13797         this.timeout = setTimeout(function () {
13798             if (_t.hoverState == 'in') {
13799                 _t.show();
13800             }
13801         }, this.delay.show)
13802     },
13803     leave : function() {
13804         clearTimeout(this.timeout);
13805     
13806         this.hoverState = 'out'
13807     
13808         if (!this.delay || !this.delay.hide) {
13809             this.hide();
13810             return 
13811         }
13812         var _t = this;
13813         this.timeout = setTimeout(function () {
13814             if (_t.hoverState == 'out') {
13815                 _t.hide();
13816             }
13817         }, this.delay.hide)
13818     },
13819     
13820     show : function (on_el)
13821     {
13822         if (!on_el) {
13823             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13824         }
13825         // set content.
13826         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13827         if (this.html !== false) {
13828             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13829         }
13830         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13831         if (!this.title.length) {
13832             this.el.select('.popover-title',true).hide();
13833         }
13834         
13835         var placement = typeof this.placement == 'function' ?
13836             this.placement.call(this, this.el, on_el) :
13837             this.placement;
13838             
13839         var autoToken = /\s?auto?\s?/i;
13840         var autoPlace = autoToken.test(placement);
13841         if (autoPlace) {
13842             placement = placement.replace(autoToken, '') || 'top';
13843         }
13844         
13845         //this.el.detach()
13846         //this.el.setXY([0,0]);
13847         this.el.show();
13848         this.el.dom.style.display='block';
13849         this.el.addClass(placement);
13850         
13851         //this.el.appendTo(on_el);
13852         
13853         var p = this.getPosition();
13854         var box = this.el.getBox();
13855         
13856         if (autoPlace) {
13857             // fixme..
13858         }
13859         var align = Roo.bootstrap.Popover.alignment[placement]
13860         this.el.alignTo(on_el, align[0],align[1]);
13861         //var arrow = this.el.select('.arrow',true).first();
13862         //arrow.set(align[2], 
13863         
13864         this.el.addClass('in');
13865         this.hoverState = null;
13866         
13867         if (this.el.hasClass('fade')) {
13868             // fade it?
13869         }
13870         
13871     },
13872     hide : function()
13873     {
13874         this.el.setXY([0,0]);
13875         this.el.removeClass('in');
13876         this.el.hide();
13877         
13878     }
13879     
13880 });
13881
13882 Roo.bootstrap.Popover.alignment = {
13883     'left' : ['r-l', [-10,0], 'right'],
13884     'right' : ['l-r', [10,0], 'left'],
13885     'bottom' : ['t-b', [0,10], 'top'],
13886     'top' : [ 'b-t', [0,-10], 'bottom']
13887 };
13888
13889  /*
13890  * - LGPL
13891  *
13892  * Progress
13893  * 
13894  */
13895
13896 /**
13897  * @class Roo.bootstrap.Progress
13898  * @extends Roo.bootstrap.Component
13899  * Bootstrap Progress class
13900  * @cfg {Boolean} striped striped of the progress bar
13901  * @cfg {Boolean} active animated of the progress bar
13902  * 
13903  * 
13904  * @constructor
13905  * Create a new Progress
13906  * @param {Object} config The config object
13907  */
13908
13909 Roo.bootstrap.Progress = function(config){
13910     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13911 };
13912
13913 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13914     
13915     striped : false,
13916     active: false,
13917     
13918     getAutoCreate : function(){
13919         var cfg = {
13920             tag: 'div',
13921             cls: 'progress'
13922         };
13923         
13924         
13925         if(this.striped){
13926             cfg.cls += ' progress-striped';
13927         }
13928       
13929         if(this.active){
13930             cfg.cls += ' active';
13931         }
13932         
13933         
13934         return cfg;
13935     }
13936    
13937 });
13938
13939  
13940
13941  /*
13942  * - LGPL
13943  *
13944  * ProgressBar
13945  * 
13946  */
13947
13948 /**
13949  * @class Roo.bootstrap.ProgressBar
13950  * @extends Roo.bootstrap.Component
13951  * Bootstrap ProgressBar class
13952  * @cfg {Number} aria_valuenow aria-value now
13953  * @cfg {Number} aria_valuemin aria-value min
13954  * @cfg {Number} aria_valuemax aria-value max
13955  * @cfg {String} label label for the progress bar
13956  * @cfg {String} panel (success | info | warning | danger )
13957  * @cfg {String} role role of the progress bar
13958  * @cfg {String} sr_only text
13959  * 
13960  * 
13961  * @constructor
13962  * Create a new ProgressBar
13963  * @param {Object} config The config object
13964  */
13965
13966 Roo.bootstrap.ProgressBar = function(config){
13967     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13968 };
13969
13970 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13971     
13972     aria_valuenow : 0,
13973     aria_valuemin : 0,
13974     aria_valuemax : 100,
13975     label : false,
13976     panel : false,
13977     role : false,
13978     sr_only: false,
13979     
13980     getAutoCreate : function()
13981     {
13982         
13983         var cfg = {
13984             tag: 'div',
13985             cls: 'progress-bar',
13986             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13987         };
13988         
13989         if(this.sr_only){
13990             cfg.cn = {
13991                 tag: 'span',
13992                 cls: 'sr-only',
13993                 html: this.sr_only
13994             }
13995         }
13996         
13997         if(this.role){
13998             cfg.role = this.role;
13999         }
14000         
14001         if(this.aria_valuenow){
14002             cfg['aria-valuenow'] = this.aria_valuenow;
14003         }
14004         
14005         if(this.aria_valuemin){
14006             cfg['aria-valuemin'] = this.aria_valuemin;
14007         }
14008         
14009         if(this.aria_valuemax){
14010             cfg['aria-valuemax'] = this.aria_valuemax;
14011         }
14012         
14013         if(this.label && !this.sr_only){
14014             cfg.html = this.label;
14015         }
14016         
14017         if(this.panel){
14018             cfg.cls += ' progress-bar-' + this.panel;
14019         }
14020         
14021         return cfg;
14022     },
14023     
14024     update : function(aria_valuenow)
14025     {
14026         this.aria_valuenow = aria_valuenow;
14027         
14028         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14029     }
14030    
14031 });
14032
14033  
14034
14035  /*
14036  * - LGPL
14037  *
14038  * column
14039  * 
14040  */
14041
14042 /**
14043  * @class Roo.bootstrap.TabGroup
14044  * @extends Roo.bootstrap.Column
14045  * Bootstrap Column class
14046  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14047  * @cfg {Boolean} carousel true to make the group behave like a carousel
14048  * 
14049  * @constructor
14050  * Create a new TabGroup
14051  * @param {Object} config The config object
14052  */
14053
14054 Roo.bootstrap.TabGroup = function(config){
14055     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14056     if (!this.navId) {
14057         this.navId = Roo.id();
14058     }
14059     this.tabs = [];
14060     Roo.bootstrap.TabGroup.register(this);
14061     
14062 };
14063
14064 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14065     
14066     carousel : false,
14067     transition : false,
14068      
14069     getAutoCreate : function()
14070     {
14071         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14072         
14073         cfg.cls += ' tab-content';
14074         
14075         if (this.carousel) {
14076             cfg.cls += ' carousel slide';
14077             cfg.cn = [{
14078                cls : 'carousel-inner'
14079             }]
14080         }
14081         
14082         
14083         return cfg;
14084     },
14085     getChildContainer : function()
14086     {
14087         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14088     },
14089     
14090     /**
14091     * register a Navigation item
14092     * @param {Roo.bootstrap.NavItem} the navitem to add
14093     */
14094     register : function(item)
14095     {
14096         this.tabs.push( item);
14097         item.navId = this.navId; // not really needed..
14098     
14099     },
14100     
14101     getActivePanel : function()
14102     {
14103         var r = false;
14104         Roo.each(this.tabs, function(t) {
14105             if (t.active) {
14106                 r = t;
14107                 return false;
14108             }
14109             return null;
14110         });
14111         return r;
14112         
14113     },
14114     getPanelByName : function(n)
14115     {
14116         var r = false;
14117         Roo.each(this.tabs, function(t) {
14118             if (t.tabId == n) {
14119                 r = t;
14120                 return false;
14121             }
14122             return null;
14123         });
14124         return r;
14125     },
14126     indexOfPanel : function(p)
14127     {
14128         var r = false;
14129         Roo.each(this.tabs, function(t,i) {
14130             if (t.tabId == p.tabId) {
14131                 r = i;
14132                 return false;
14133             }
14134             return null;
14135         });
14136         return r;
14137     },
14138     /**
14139      * show a specific panel
14140      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14141      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14142      */
14143     showPanel : function (pan)
14144     {
14145         
14146         if (typeof(pan) == 'number') {
14147             pan = this.tabs[pan];
14148         }
14149         if (typeof(pan) == 'string') {
14150             pan = this.getPanelByName(pan);
14151         }
14152         if (pan.tabId == this.getActivePanel().tabId) {
14153             return true;
14154         }
14155         var cur = this.getActivePanel();
14156         
14157         if (false === cur.fireEvent('beforedeactivate')) {
14158             return false;
14159         }
14160         
14161         if (this.carousel) {
14162             this.transition = true;
14163             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14164             var lr = dir == 'next' ? 'left' : 'right';
14165             pan.el.addClass(dir); // or prev
14166             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14167             cur.el.addClass(lr); // or right
14168             pan.el.addClass(lr);
14169             
14170             var _this = this;
14171             cur.el.on('transitionend', function() {
14172                 Roo.log("trans end?");
14173                 
14174                 pan.el.removeClass([lr,dir]);
14175                 pan.setActive(true);
14176                 
14177                 cur.el.removeClass([lr]);
14178                 cur.setActive(false);
14179                 
14180                 _this.transition = false;
14181                 
14182             }, this, { single:  true } );
14183             return true;
14184         }
14185         
14186         cur.setActive(false);
14187         pan.setActive(true);
14188         return true;
14189         
14190     },
14191     showPanelNext : function()
14192     {
14193         var i = this.indexOfPanel(this.getActivePanel());
14194         if (i > this.tabs.length) {
14195             return;
14196         }
14197         this.showPanel(this.tabs[i+1]);
14198     },
14199     showPanelPrev : function()
14200     {
14201         var i = this.indexOfPanel(this.getActivePanel());
14202         if (i  < 1) {
14203             return;
14204         }
14205         this.showPanel(this.tabs[i-1]);
14206     }
14207     
14208     
14209   
14210 });
14211
14212  
14213
14214  
14215  
14216 Roo.apply(Roo.bootstrap.TabGroup, {
14217     
14218     groups: {},
14219      /**
14220     * register a Navigation Group
14221     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14222     */
14223     register : function(navgrp)
14224     {
14225         this.groups[navgrp.navId] = navgrp;
14226         
14227     },
14228     /**
14229     * fetch a Navigation Group based on the navigation ID
14230     * if one does not exist , it will get created.
14231     * @param {string} the navgroup to add
14232     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14233     */
14234     get: function(navId) {
14235         if (typeof(this.groups[navId]) == 'undefined') {
14236             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14237         }
14238         return this.groups[navId] ;
14239     }
14240     
14241     
14242     
14243 });
14244
14245  /*
14246  * - LGPL
14247  *
14248  * TabPanel
14249  * 
14250  */
14251
14252 /**
14253  * @class Roo.bootstrap.TabPanel
14254  * @extends Roo.bootstrap.Component
14255  * Bootstrap TabPanel class
14256  * @cfg {Boolean} active panel active
14257  * @cfg {String} html panel content
14258  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14259  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14260  * 
14261  * 
14262  * @constructor
14263  * Create a new TabPanel
14264  * @param {Object} config The config object
14265  */
14266
14267 Roo.bootstrap.TabPanel = function(config){
14268     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14269     this.addEvents({
14270         /**
14271              * @event changed
14272              * Fires when the active status changes
14273              * @param {Roo.bootstrap.TabPanel} this
14274              * @param {Boolean} state the new state
14275             
14276          */
14277         'changed': true,
14278         /**
14279              * @event beforedeactivate
14280              * Fires before a tab is de-activated - can be used to do validation on a form.
14281              * @param {Roo.bootstrap.TabPanel} this
14282              * @return {Boolean} false if there is an error
14283             
14284          */
14285         'beforedeactivate': true
14286      });
14287     
14288     this.tabId = this.tabId || Roo.id();
14289   
14290 };
14291
14292 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14293     
14294     active: false,
14295     html: false,
14296     tabId: false,
14297     navId : false,
14298     
14299     getAutoCreate : function(){
14300         var cfg = {
14301             tag: 'div',
14302             // item is needed for carousel - not sure if it has any effect otherwise
14303             cls: 'tab-pane item',
14304             html: this.html || ''
14305         };
14306         
14307         if(this.active){
14308             cfg.cls += ' active';
14309         }
14310         
14311         if(this.tabId){
14312             cfg.tabId = this.tabId;
14313         }
14314         
14315         
14316         return cfg;
14317     },
14318     
14319     initEvents:  function()
14320     {
14321         Roo.log('-------- init events on tab panel ---------');
14322         
14323         var p = this.parent();
14324         this.navId = this.navId || p.navId;
14325         
14326         if (typeof(this.navId) != 'undefined') {
14327             // not really needed.. but just in case.. parent should be a NavGroup.
14328             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14329             Roo.log(['register', tg, this]);
14330             tg.register(this);
14331         }
14332     },
14333     
14334     
14335     onRender : function(ct, position)
14336     {
14337        // Roo.log("Call onRender: " + this.xtype);
14338         
14339         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14340         
14341         
14342         
14343         
14344         
14345     },
14346     
14347     setActive: function(state)
14348     {
14349         Roo.log("panel - set active " + this.tabId + "=" + state);
14350         
14351         this.active = state;
14352         if (!state) {
14353             this.el.removeClass('active');
14354             
14355         } else  if (!this.el.hasClass('active')) {
14356             this.el.addClass('active');
14357         }
14358         this.fireEvent('changed', this, state);
14359     }
14360     
14361     
14362 });
14363  
14364
14365  
14366
14367  /*
14368  * - LGPL
14369  *
14370  * DateField
14371  * 
14372  */
14373
14374 /**
14375  * @class Roo.bootstrap.DateField
14376  * @extends Roo.bootstrap.Input
14377  * Bootstrap DateField class
14378  * @cfg {Number} weekStart default 0
14379  * @cfg {String} viewMode default empty, (months|years)
14380  * @cfg {String} minViewMode default empty, (months|years)
14381  * @cfg {Number} startDate default -Infinity
14382  * @cfg {Number} endDate default Infinity
14383  * @cfg {Boolean} todayHighlight default false
14384  * @cfg {Boolean} todayBtn default false
14385  * @cfg {Boolean} calendarWeeks default false
14386  * @cfg {Object} daysOfWeekDisabled default empty
14387  * @cfg {Boolean} singleMode default false (true | false)
14388  * 
14389  * @cfg {Boolean} keyboardNavigation default true
14390  * @cfg {String} language default en
14391  * 
14392  * @constructor
14393  * Create a new DateField
14394  * @param {Object} config The config object
14395  */
14396
14397 Roo.bootstrap.DateField = function(config){
14398     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14399      this.addEvents({
14400             /**
14401              * @event show
14402              * Fires when this field show.
14403              * @param {Roo.bootstrap.DateField} this
14404              * @param {Mixed} date The date value
14405              */
14406             show : true,
14407             /**
14408              * @event show
14409              * Fires when this field hide.
14410              * @param {Roo.bootstrap.DateField} this
14411              * @param {Mixed} date The date value
14412              */
14413             hide : true,
14414             /**
14415              * @event select
14416              * Fires when select a date.
14417              * @param {Roo.bootstrap.DateField} this
14418              * @param {Mixed} date The date value
14419              */
14420             select : true
14421         });
14422 };
14423
14424 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14425     
14426     /**
14427      * @cfg {String} format
14428      * The default date format string which can be overriden for localization support.  The format must be
14429      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14430      */
14431     format : "m/d/y",
14432     /**
14433      * @cfg {String} altFormats
14434      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14435      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14436      */
14437     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14438     
14439     weekStart : 0,
14440     
14441     viewMode : '',
14442     
14443     minViewMode : '',
14444     
14445     todayHighlight : false,
14446     
14447     todayBtn: false,
14448     
14449     language: 'en',
14450     
14451     keyboardNavigation: true,
14452     
14453     calendarWeeks: false,
14454     
14455     startDate: -Infinity,
14456     
14457     endDate: Infinity,
14458     
14459     daysOfWeekDisabled: [],
14460     
14461     _events: [],
14462     
14463     singleMode : false,
14464     
14465     UTCDate: function()
14466     {
14467         return new Date(Date.UTC.apply(Date, arguments));
14468     },
14469     
14470     UTCToday: function()
14471     {
14472         var today = new Date();
14473         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14474     },
14475     
14476     getDate: function() {
14477             var d = this.getUTCDate();
14478             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14479     },
14480     
14481     getUTCDate: function() {
14482             return this.date;
14483     },
14484     
14485     setDate: function(d) {
14486             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14487     },
14488     
14489     setUTCDate: function(d) {
14490             this.date = d;
14491             this.setValue(this.formatDate(this.date));
14492     },
14493         
14494     onRender: function(ct, position)
14495     {
14496         
14497         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14498         
14499         this.language = this.language || 'en';
14500         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14501         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14502         
14503         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14504         this.format = this.format || 'm/d/y';
14505         this.isInline = false;
14506         this.isInput = true;
14507         this.component = this.el.select('.add-on', true).first() || false;
14508         this.component = (this.component && this.component.length === 0) ? false : this.component;
14509         this.hasInput = this.component && this.inputEL().length;
14510         
14511         if (typeof(this.minViewMode === 'string')) {
14512             switch (this.minViewMode) {
14513                 case 'months':
14514                     this.minViewMode = 1;
14515                     break;
14516                 case 'years':
14517                     this.minViewMode = 2;
14518                     break;
14519                 default:
14520                     this.minViewMode = 0;
14521                     break;
14522             }
14523         }
14524         
14525         if (typeof(this.viewMode === 'string')) {
14526             switch (this.viewMode) {
14527                 case 'months':
14528                     this.viewMode = 1;
14529                     break;
14530                 case 'years':
14531                     this.viewMode = 2;
14532                     break;
14533                 default:
14534                     this.viewMode = 0;
14535                     break;
14536             }
14537         }
14538                 
14539         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14540         
14541 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14542         
14543         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14544         
14545         this.picker().on('mousedown', this.onMousedown, this);
14546         this.picker().on('click', this.onClick, this);
14547         
14548         this.picker().addClass('datepicker-dropdown');
14549         
14550         this.startViewMode = this.viewMode;
14551         
14552         if(this.singleMode){
14553             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14554                 v.setVisibilityMode(Roo.Element.DISPLAY)
14555                 v.hide();
14556             })
14557             
14558             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14559                 v.setStyle('width', '189px');
14560             });
14561         }
14562         
14563         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14564             if(!this.calendarWeeks){
14565                 v.remove();
14566                 return;
14567             };
14568             
14569             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14570             v.attr('colspan', function(i, val){
14571                 return parseInt(val) + 1;
14572             });
14573         })
14574                         
14575         
14576         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14577         
14578         this.setStartDate(this.startDate);
14579         this.setEndDate(this.endDate);
14580         
14581         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14582         
14583         this.fillDow();
14584         this.fillMonths();
14585         this.update();
14586         this.showMode();
14587         
14588         if(this.isInline) {
14589             this.show();
14590         }
14591     },
14592     
14593     picker : function()
14594     {
14595         return this.pickerEl;
14596 //        return this.el.select('.datepicker', true).first();
14597     },
14598     
14599     fillDow: function()
14600     {
14601         var dowCnt = this.weekStart;
14602         
14603         var dow = {
14604             tag: 'tr',
14605             cn: [
14606                 
14607             ]
14608         };
14609         
14610         if(this.calendarWeeks){
14611             dow.cn.push({
14612                 tag: 'th',
14613                 cls: 'cw',
14614                 html: '&nbsp;'
14615             })
14616         }
14617         
14618         while (dowCnt < this.weekStart + 7) {
14619             dow.cn.push({
14620                 tag: 'th',
14621                 cls: 'dow',
14622                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14623             });
14624         }
14625         
14626         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14627     },
14628     
14629     fillMonths: function()
14630     {    
14631         var i = 0
14632         var months = this.picker().select('>.datepicker-months td', true).first();
14633         
14634         months.dom.innerHTML = '';
14635         
14636         while (i < 12) {
14637             var month = {
14638                 tag: 'span',
14639                 cls: 'month',
14640                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14641             }
14642             
14643             months.createChild(month);
14644         }
14645         
14646     },
14647     
14648     update: function()
14649     {
14650         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;
14651         
14652         if (this.date < this.startDate) {
14653             this.viewDate = new Date(this.startDate);
14654         } else if (this.date > this.endDate) {
14655             this.viewDate = new Date(this.endDate);
14656         } else {
14657             this.viewDate = new Date(this.date);
14658         }
14659         
14660         this.fill();
14661     },
14662     
14663     fill: function() 
14664     {
14665         var d = new Date(this.viewDate),
14666                 year = d.getUTCFullYear(),
14667                 month = d.getUTCMonth(),
14668                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14669                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14670                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14671                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14672                 currentDate = this.date && this.date.valueOf(),
14673                 today = this.UTCToday();
14674         
14675         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14676         
14677 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14678         
14679 //        this.picker.select('>tfoot th.today').
14680 //                                              .text(dates[this.language].today)
14681 //                                              .toggle(this.todayBtn !== false);
14682     
14683         this.updateNavArrows();
14684         this.fillMonths();
14685                                                 
14686         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14687         
14688         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14689          
14690         prevMonth.setUTCDate(day);
14691         
14692         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14693         
14694         var nextMonth = new Date(prevMonth);
14695         
14696         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14697         
14698         nextMonth = nextMonth.valueOf();
14699         
14700         var fillMonths = false;
14701         
14702         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14703         
14704         while(prevMonth.valueOf() < nextMonth) {
14705             var clsName = '';
14706             
14707             if (prevMonth.getUTCDay() === this.weekStart) {
14708                 if(fillMonths){
14709                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14710                 }
14711                     
14712                 fillMonths = {
14713                     tag: 'tr',
14714                     cn: []
14715                 };
14716                 
14717                 if(this.calendarWeeks){
14718                     // ISO 8601: First week contains first thursday.
14719                     // ISO also states week starts on Monday, but we can be more abstract here.
14720                     var
14721                     // Start of current week: based on weekstart/current date
14722                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14723                     // Thursday of this week
14724                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14725                     // First Thursday of year, year from thursday
14726                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14727                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14728                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14729                     
14730                     fillMonths.cn.push({
14731                         tag: 'td',
14732                         cls: 'cw',
14733                         html: calWeek
14734                     });
14735                 }
14736             }
14737             
14738             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14739                 clsName += ' old';
14740             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14741                 clsName += ' new';
14742             }
14743             if (this.todayHighlight &&
14744                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14745                 prevMonth.getUTCMonth() == today.getMonth() &&
14746                 prevMonth.getUTCDate() == today.getDate()) {
14747                 clsName += ' today';
14748             }
14749             
14750             if (currentDate && prevMonth.valueOf() === currentDate) {
14751                 clsName += ' active';
14752             }
14753             
14754             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14755                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14756                     clsName += ' disabled';
14757             }
14758             
14759             fillMonths.cn.push({
14760                 tag: 'td',
14761                 cls: 'day ' + clsName,
14762                 html: prevMonth.getDate()
14763             })
14764             
14765             prevMonth.setDate(prevMonth.getDate()+1);
14766         }
14767           
14768         var currentYear = this.date && this.date.getUTCFullYear();
14769         var currentMonth = this.date && this.date.getUTCMonth();
14770         
14771         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14772         
14773         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14774             v.removeClass('active');
14775             
14776             if(currentYear === year && k === currentMonth){
14777                 v.addClass('active');
14778             }
14779             
14780             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14781                 v.addClass('disabled');
14782             }
14783             
14784         });
14785         
14786         
14787         year = parseInt(year/10, 10) * 10;
14788         
14789         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14790         
14791         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14792         
14793         year -= 1;
14794         for (var i = -1; i < 11; i++) {
14795             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14796                 tag: 'span',
14797                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14798                 html: year
14799             })
14800             
14801             year += 1;
14802         }
14803     },
14804     
14805     showMode: function(dir) 
14806     {
14807         if (dir) {
14808             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14809         }
14810         
14811         Roo.each(this.picker().select('>div',true).elements, function(v){
14812             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14813             v.hide();
14814         });
14815         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14816     },
14817     
14818     place: function()
14819     {
14820         if(this.isInline) return;
14821         
14822         this.picker().removeClass(['bottom', 'top']);
14823         
14824         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14825             /*
14826              * place to the top of element!
14827              *
14828              */
14829             
14830             this.picker().addClass('top');
14831             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14832             
14833             return;
14834         }
14835         
14836         this.picker().addClass('bottom');
14837         
14838         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14839     },
14840     
14841     parseDate : function(value)
14842     {
14843         if(!value || value instanceof Date){
14844             return value;
14845         }
14846         var v = Date.parseDate(value, this.format);
14847         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14848             v = Date.parseDate(value, 'Y-m-d');
14849         }
14850         if(!v && this.altFormats){
14851             if(!this.altFormatsArray){
14852                 this.altFormatsArray = this.altFormats.split("|");
14853             }
14854             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14855                 v = Date.parseDate(value, this.altFormatsArray[i]);
14856             }
14857         }
14858         return v;
14859     },
14860     
14861     formatDate : function(date, fmt)
14862     {   
14863         return (!date || !(date instanceof Date)) ?
14864         date : date.dateFormat(fmt || this.format);
14865     },
14866     
14867     onFocus : function()
14868     {
14869         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14870         this.show();
14871     },
14872     
14873     onBlur : function()
14874     {
14875         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14876         
14877         var d = this.inputEl().getValue();
14878         
14879         this.setValue(d);
14880                 
14881         this.hide();
14882     },
14883     
14884     show : function()
14885     {
14886         this.picker().show();
14887         this.update();
14888         this.place();
14889         
14890         this.fireEvent('show', this, this.date);
14891     },
14892     
14893     hide : function()
14894     {
14895         if(this.isInline) return;
14896         this.picker().hide();
14897         this.viewMode = this.startViewMode;
14898         this.showMode();
14899         
14900         this.fireEvent('hide', this, this.date);
14901         
14902     },
14903     
14904     onMousedown: function(e)
14905     {
14906         e.stopPropagation();
14907         e.preventDefault();
14908     },
14909     
14910     keyup: function(e)
14911     {
14912         Roo.bootstrap.DateField.superclass.keyup.call(this);
14913         this.update();
14914     },
14915
14916     setValue: function(v)
14917     {
14918         
14919         // v can be a string or a date..
14920         
14921         
14922         var d = new Date(this.parseDate(v) ).clearTime();
14923         
14924         if(isNaN(d.getTime())){
14925             this.date = this.viewDate = '';
14926             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14927             return;
14928         }
14929         
14930         v = this.formatDate(d);
14931         
14932         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14933         
14934         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14935      
14936         this.update();
14937
14938         this.fireEvent('select', this, this.date);
14939         
14940     },
14941     
14942     getValue: function()
14943     {
14944         return this.formatDate(this.date);
14945     },
14946     
14947     fireKey: function(e)
14948     {
14949         if (!this.picker().isVisible()){
14950             if (e.keyCode == 27) // allow escape to hide and re-show picker
14951                 this.show();
14952             return;
14953         }
14954         
14955         var dateChanged = false,
14956         dir, day, month,
14957         newDate, newViewDate;
14958         
14959         switch(e.keyCode){
14960             case 27: // escape
14961                 this.hide();
14962                 e.preventDefault();
14963                 break;
14964             case 37: // left
14965             case 39: // right
14966                 if (!this.keyboardNavigation) break;
14967                 dir = e.keyCode == 37 ? -1 : 1;
14968                 
14969                 if (e.ctrlKey){
14970                     newDate = this.moveYear(this.date, dir);
14971                     newViewDate = this.moveYear(this.viewDate, dir);
14972                 } else if (e.shiftKey){
14973                     newDate = this.moveMonth(this.date, dir);
14974                     newViewDate = this.moveMonth(this.viewDate, dir);
14975                 } else {
14976                     newDate = new Date(this.date);
14977                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14978                     newViewDate = new Date(this.viewDate);
14979                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14980                 }
14981                 if (this.dateWithinRange(newDate)){
14982                     this.date = newDate;
14983                     this.viewDate = newViewDate;
14984                     this.setValue(this.formatDate(this.date));
14985 //                    this.update();
14986                     e.preventDefault();
14987                     dateChanged = true;
14988                 }
14989                 break;
14990             case 38: // up
14991             case 40: // down
14992                 if (!this.keyboardNavigation) break;
14993                 dir = e.keyCode == 38 ? -1 : 1;
14994                 if (e.ctrlKey){
14995                     newDate = this.moveYear(this.date, dir);
14996                     newViewDate = this.moveYear(this.viewDate, dir);
14997                 } else if (e.shiftKey){
14998                     newDate = this.moveMonth(this.date, dir);
14999                     newViewDate = this.moveMonth(this.viewDate, dir);
15000                 } else {
15001                     newDate = new Date(this.date);
15002                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15003                     newViewDate = new Date(this.viewDate);
15004                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15005                 }
15006                 if (this.dateWithinRange(newDate)){
15007                     this.date = newDate;
15008                     this.viewDate = newViewDate;
15009                     this.setValue(this.formatDate(this.date));
15010 //                    this.update();
15011                     e.preventDefault();
15012                     dateChanged = true;
15013                 }
15014                 break;
15015             case 13: // enter
15016                 this.setValue(this.formatDate(this.date));
15017                 this.hide();
15018                 e.preventDefault();
15019                 break;
15020             case 9: // tab
15021                 this.setValue(this.formatDate(this.date));
15022                 this.hide();
15023                 break;
15024             case 16: // shift
15025             case 17: // ctrl
15026             case 18: // alt
15027                 break;
15028             default :
15029                 this.hide();
15030                 
15031         }
15032     },
15033     
15034     
15035     onClick: function(e) 
15036     {
15037         e.stopPropagation();
15038         e.preventDefault();
15039         
15040         var target = e.getTarget();
15041         
15042         if(target.nodeName.toLowerCase() === 'i'){
15043             target = Roo.get(target).dom.parentNode;
15044         }
15045         
15046         var nodeName = target.nodeName;
15047         var className = target.className;
15048         var html = target.innerHTML;
15049         //Roo.log(nodeName);
15050         
15051         switch(nodeName.toLowerCase()) {
15052             case 'th':
15053                 switch(className) {
15054                     case 'switch':
15055                         this.showMode(1);
15056                         break;
15057                     case 'prev':
15058                     case 'next':
15059                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15060                         switch(this.viewMode){
15061                                 case 0:
15062                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15063                                         break;
15064                                 case 1:
15065                                 case 2:
15066                                         this.viewDate = this.moveYear(this.viewDate, dir);
15067                                         break;
15068                         }
15069                         this.fill();
15070                         break;
15071                     case 'today':
15072                         var date = new Date();
15073                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15074 //                        this.fill()
15075                         this.setValue(this.formatDate(this.date));
15076                         
15077                         this.hide();
15078                         break;
15079                 }
15080                 break;
15081             case 'span':
15082                 if (className.indexOf('disabled') < 0) {
15083                     this.viewDate.setUTCDate(1);
15084                     if (className.indexOf('month') > -1) {
15085                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15086                     } else {
15087                         var year = parseInt(html, 10) || 0;
15088                         this.viewDate.setUTCFullYear(year);
15089                         
15090                     }
15091                     
15092                     if(this.singleMode){
15093                         this.setValue(this.formatDate(this.viewDate));
15094                         this.hide();
15095                         return;
15096                     }
15097                     
15098                     this.showMode(-1);
15099                     this.fill();
15100                 }
15101                 break;
15102                 
15103             case 'td':
15104                 //Roo.log(className);
15105                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15106                     var day = parseInt(html, 10) || 1;
15107                     var year = this.viewDate.getUTCFullYear(),
15108                         month = this.viewDate.getUTCMonth();
15109
15110                     if (className.indexOf('old') > -1) {
15111                         if(month === 0 ){
15112                             month = 11;
15113                             year -= 1;
15114                         }else{
15115                             month -= 1;
15116                         }
15117                     } else if (className.indexOf('new') > -1) {
15118                         if (month == 11) {
15119                             month = 0;
15120                             year += 1;
15121                         } else {
15122                             month += 1;
15123                         }
15124                     }
15125                     //Roo.log([year,month,day]);
15126                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15127                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15128 //                    this.fill();
15129                     //Roo.log(this.formatDate(this.date));
15130                     this.setValue(this.formatDate(this.date));
15131                     this.hide();
15132                 }
15133                 break;
15134         }
15135     },
15136     
15137     setStartDate: function(startDate)
15138     {
15139         this.startDate = startDate || -Infinity;
15140         if (this.startDate !== -Infinity) {
15141             this.startDate = this.parseDate(this.startDate);
15142         }
15143         this.update();
15144         this.updateNavArrows();
15145     },
15146
15147     setEndDate: function(endDate)
15148     {
15149         this.endDate = endDate || Infinity;
15150         if (this.endDate !== Infinity) {
15151             this.endDate = this.parseDate(this.endDate);
15152         }
15153         this.update();
15154         this.updateNavArrows();
15155     },
15156     
15157     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15158     {
15159         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15160         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15161             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15162         }
15163         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15164             return parseInt(d, 10);
15165         });
15166         this.update();
15167         this.updateNavArrows();
15168     },
15169     
15170     updateNavArrows: function() 
15171     {
15172         if(this.singleMode){
15173             return;
15174         }
15175         
15176         var d = new Date(this.viewDate),
15177         year = d.getUTCFullYear(),
15178         month = d.getUTCMonth();
15179         
15180         Roo.each(this.picker().select('.prev', true).elements, function(v){
15181             v.show();
15182             switch (this.viewMode) {
15183                 case 0:
15184
15185                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15186                         v.hide();
15187                     }
15188                     break;
15189                 case 1:
15190                 case 2:
15191                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15192                         v.hide();
15193                     }
15194                     break;
15195             }
15196         });
15197         
15198         Roo.each(this.picker().select('.next', true).elements, function(v){
15199             v.show();
15200             switch (this.viewMode) {
15201                 case 0:
15202
15203                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15204                         v.hide();
15205                     }
15206                     break;
15207                 case 1:
15208                 case 2:
15209                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15210                         v.hide();
15211                     }
15212                     break;
15213             }
15214         })
15215     },
15216     
15217     moveMonth: function(date, dir)
15218     {
15219         if (!dir) return date;
15220         var new_date = new Date(date.valueOf()),
15221         day = new_date.getUTCDate(),
15222         month = new_date.getUTCMonth(),
15223         mag = Math.abs(dir),
15224         new_month, test;
15225         dir = dir > 0 ? 1 : -1;
15226         if (mag == 1){
15227             test = dir == -1
15228             // If going back one month, make sure month is not current month
15229             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15230             ? function(){
15231                 return new_date.getUTCMonth() == month;
15232             }
15233             // If going forward one month, make sure month is as expected
15234             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15235             : function(){
15236                 return new_date.getUTCMonth() != new_month;
15237             };
15238             new_month = month + dir;
15239             new_date.setUTCMonth(new_month);
15240             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15241             if (new_month < 0 || new_month > 11)
15242                 new_month = (new_month + 12) % 12;
15243         } else {
15244             // For magnitudes >1, move one month at a time...
15245             for (var i=0; i<mag; i++)
15246                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15247                 new_date = this.moveMonth(new_date, dir);
15248             // ...then reset the day, keeping it in the new month
15249             new_month = new_date.getUTCMonth();
15250             new_date.setUTCDate(day);
15251             test = function(){
15252                 return new_month != new_date.getUTCMonth();
15253             };
15254         }
15255         // Common date-resetting loop -- if date is beyond end of month, make it
15256         // end of month
15257         while (test()){
15258             new_date.setUTCDate(--day);
15259             new_date.setUTCMonth(new_month);
15260         }
15261         return new_date;
15262     },
15263
15264     moveYear: function(date, dir)
15265     {
15266         return this.moveMonth(date, dir*12);
15267     },
15268
15269     dateWithinRange: function(date)
15270     {
15271         return date >= this.startDate && date <= this.endDate;
15272     },
15273
15274     
15275     remove: function() 
15276     {
15277         this.picker().remove();
15278     }
15279    
15280 });
15281
15282 Roo.apply(Roo.bootstrap.DateField,  {
15283     
15284     head : {
15285         tag: 'thead',
15286         cn: [
15287         {
15288             tag: 'tr',
15289             cn: [
15290             {
15291                 tag: 'th',
15292                 cls: 'prev',
15293                 html: '<i class="fa fa-arrow-left"/>'
15294             },
15295             {
15296                 tag: 'th',
15297                 cls: 'switch',
15298                 colspan: '5'
15299             },
15300             {
15301                 tag: 'th',
15302                 cls: 'next',
15303                 html: '<i class="fa fa-arrow-right"/>'
15304             }
15305
15306             ]
15307         }
15308         ]
15309     },
15310     
15311     content : {
15312         tag: 'tbody',
15313         cn: [
15314         {
15315             tag: 'tr',
15316             cn: [
15317             {
15318                 tag: 'td',
15319                 colspan: '7'
15320             }
15321             ]
15322         }
15323         ]
15324     },
15325     
15326     footer : {
15327         tag: 'tfoot',
15328         cn: [
15329         {
15330             tag: 'tr',
15331             cn: [
15332             {
15333                 tag: 'th',
15334                 colspan: '7',
15335                 cls: 'today'
15336             }
15337                     
15338             ]
15339         }
15340         ]
15341     },
15342     
15343     dates:{
15344         en: {
15345             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15346             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15347             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15348             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15349             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15350             today: "Today"
15351         }
15352     },
15353     
15354     modes: [
15355     {
15356         clsName: 'days',
15357         navFnc: 'Month',
15358         navStep: 1
15359     },
15360     {
15361         clsName: 'months',
15362         navFnc: 'FullYear',
15363         navStep: 1
15364     },
15365     {
15366         clsName: 'years',
15367         navFnc: 'FullYear',
15368         navStep: 10
15369     }]
15370 });
15371
15372 Roo.apply(Roo.bootstrap.DateField,  {
15373   
15374     template : {
15375         tag: 'div',
15376         cls: 'datepicker dropdown-menu',
15377         cn: [
15378         {
15379             tag: 'div',
15380             cls: 'datepicker-days',
15381             cn: [
15382             {
15383                 tag: 'table',
15384                 cls: 'table-condensed',
15385                 cn:[
15386                 Roo.bootstrap.DateField.head,
15387                 {
15388                     tag: 'tbody'
15389                 },
15390                 Roo.bootstrap.DateField.footer
15391                 ]
15392             }
15393             ]
15394         },
15395         {
15396             tag: 'div',
15397             cls: 'datepicker-months',
15398             cn: [
15399             {
15400                 tag: 'table',
15401                 cls: 'table-condensed',
15402                 cn:[
15403                 Roo.bootstrap.DateField.head,
15404                 Roo.bootstrap.DateField.content,
15405                 Roo.bootstrap.DateField.footer
15406                 ]
15407             }
15408             ]
15409         },
15410         {
15411             tag: 'div',
15412             cls: 'datepicker-years',
15413             cn: [
15414             {
15415                 tag: 'table',
15416                 cls: 'table-condensed',
15417                 cn:[
15418                 Roo.bootstrap.DateField.head,
15419                 Roo.bootstrap.DateField.content,
15420                 Roo.bootstrap.DateField.footer
15421                 ]
15422             }
15423             ]
15424         }
15425         ]
15426     }
15427 });
15428
15429  
15430
15431  /*
15432  * - LGPL
15433  *
15434  * TimeField
15435  * 
15436  */
15437
15438 /**
15439  * @class Roo.bootstrap.TimeField
15440  * @extends Roo.bootstrap.Input
15441  * Bootstrap DateField class
15442  * 
15443  * 
15444  * @constructor
15445  * Create a new TimeField
15446  * @param {Object} config The config object
15447  */
15448
15449 Roo.bootstrap.TimeField = function(config){
15450     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15451     this.addEvents({
15452             /**
15453              * @event show
15454              * Fires when this field show.
15455              * @param {Roo.bootstrap.DateField} this
15456              * @param {Mixed} date The date value
15457              */
15458             show : true,
15459             /**
15460              * @event show
15461              * Fires when this field hide.
15462              * @param {Roo.bootstrap.DateField} this
15463              * @param {Mixed} date The date value
15464              */
15465             hide : true,
15466             /**
15467              * @event select
15468              * Fires when select a date.
15469              * @param {Roo.bootstrap.DateField} this
15470              * @param {Mixed} date The date value
15471              */
15472             select : true
15473         });
15474 };
15475
15476 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15477     
15478     /**
15479      * @cfg {String} format
15480      * The default time format string which can be overriden for localization support.  The format must be
15481      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15482      */
15483     format : "H:i",
15484        
15485     onRender: function(ct, position)
15486     {
15487         
15488         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15489                 
15490         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15491         
15492         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15493         
15494         this.pop = this.picker().select('>.datepicker-time',true).first();
15495         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15496         
15497         this.picker().on('mousedown', this.onMousedown, this);
15498         this.picker().on('click', this.onClick, this);
15499         
15500         this.picker().addClass('datepicker-dropdown');
15501     
15502         this.fillTime();
15503         this.update();
15504             
15505         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15506         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15507         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15508         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15509         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15510         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15511
15512     },
15513     
15514     fireKey: function(e){
15515         if (!this.picker().isVisible()){
15516             if (e.keyCode == 27) // allow escape to hide and re-show picker
15517                 this.show();
15518             return;
15519         }
15520
15521         e.preventDefault();
15522         
15523         switch(e.keyCode){
15524             case 27: // escape
15525                 this.hide();
15526                 break;
15527             case 37: // left
15528             case 39: // right
15529                 this.onTogglePeriod();
15530                 break;
15531             case 38: // up
15532                 this.onIncrementMinutes();
15533                 break;
15534             case 40: // down
15535                 this.onDecrementMinutes();
15536                 break;
15537             case 13: // enter
15538             case 9: // tab
15539                 this.setTime();
15540                 break;
15541         }
15542     },
15543     
15544     onClick: function(e) {
15545         e.stopPropagation();
15546         e.preventDefault();
15547     },
15548     
15549     picker : function()
15550     {
15551         return this.el.select('.datepicker', true).first();
15552     },
15553     
15554     fillTime: function()
15555     {    
15556         var time = this.pop.select('tbody', true).first();
15557         
15558         time.dom.innerHTML = '';
15559         
15560         time.createChild({
15561             tag: 'tr',
15562             cn: [
15563                 {
15564                     tag: 'td',
15565                     cn: [
15566                         {
15567                             tag: 'a',
15568                             href: '#',
15569                             cls: 'btn',
15570                             cn: [
15571                                 {
15572                                     tag: 'span',
15573                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15574                                 }
15575                             ]
15576                         } 
15577                     ]
15578                 },
15579                 {
15580                     tag: 'td',
15581                     cls: 'separator'
15582                 },
15583                 {
15584                     tag: 'td',
15585                     cn: [
15586                         {
15587                             tag: 'a',
15588                             href: '#',
15589                             cls: 'btn',
15590                             cn: [
15591                                 {
15592                                     tag: 'span',
15593                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15594                                 }
15595                             ]
15596                         }
15597                     ]
15598                 },
15599                 {
15600                     tag: 'td',
15601                     cls: 'separator'
15602                 }
15603             ]
15604         });
15605         
15606         time.createChild({
15607             tag: 'tr',
15608             cn: [
15609                 {
15610                     tag: 'td',
15611                     cn: [
15612                         {
15613                             tag: 'span',
15614                             cls: 'timepicker-hour',
15615                             html: '00'
15616                         }  
15617                     ]
15618                 },
15619                 {
15620                     tag: 'td',
15621                     cls: 'separator',
15622                     html: ':'
15623                 },
15624                 {
15625                     tag: 'td',
15626                     cn: [
15627                         {
15628                             tag: 'span',
15629                             cls: 'timepicker-minute',
15630                             html: '00'
15631                         }  
15632                     ]
15633                 },
15634                 {
15635                     tag: 'td',
15636                     cls: 'separator'
15637                 },
15638                 {
15639                     tag: 'td',
15640                     cn: [
15641                         {
15642                             tag: 'button',
15643                             type: 'button',
15644                             cls: 'btn btn-primary period',
15645                             html: 'AM'
15646                             
15647                         }
15648                     ]
15649                 }
15650             ]
15651         });
15652         
15653         time.createChild({
15654             tag: 'tr',
15655             cn: [
15656                 {
15657                     tag: 'td',
15658                     cn: [
15659                         {
15660                             tag: 'a',
15661                             href: '#',
15662                             cls: 'btn',
15663                             cn: [
15664                                 {
15665                                     tag: 'span',
15666                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15667                                 }
15668                             ]
15669                         }
15670                     ]
15671                 },
15672                 {
15673                     tag: 'td',
15674                     cls: 'separator'
15675                 },
15676                 {
15677                     tag: 'td',
15678                     cn: [
15679                         {
15680                             tag: 'a',
15681                             href: '#',
15682                             cls: 'btn',
15683                             cn: [
15684                                 {
15685                                     tag: 'span',
15686                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15687                                 }
15688                             ]
15689                         }
15690                     ]
15691                 },
15692                 {
15693                     tag: 'td',
15694                     cls: 'separator'
15695                 }
15696             ]
15697         });
15698         
15699     },
15700     
15701     update: function()
15702     {
15703         
15704         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15705         
15706         this.fill();
15707     },
15708     
15709     fill: function() 
15710     {
15711         var hours = this.time.getHours();
15712         var minutes = this.time.getMinutes();
15713         var period = 'AM';
15714         
15715         if(hours > 11){
15716             period = 'PM';
15717         }
15718         
15719         if(hours == 0){
15720             hours = 12;
15721         }
15722         
15723         
15724         if(hours > 12){
15725             hours = hours - 12;
15726         }
15727         
15728         if(hours < 10){
15729             hours = '0' + hours;
15730         }
15731         
15732         if(minutes < 10){
15733             minutes = '0' + minutes;
15734         }
15735         
15736         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15737         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15738         this.pop.select('button', true).first().dom.innerHTML = period;
15739         
15740     },
15741     
15742     place: function()
15743     {   
15744         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15745         
15746         var cls = ['bottom'];
15747         
15748         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15749             cls.pop();
15750             cls.push('top');
15751         }
15752         
15753         cls.push('right');
15754         
15755         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15756             cls.pop();
15757             cls.push('left');
15758         }
15759         
15760         this.picker().addClass(cls.join('-'));
15761         
15762         var _this = this;
15763         
15764         Roo.each(cls, function(c){
15765             if(c == 'bottom'){
15766                 _this.picker().setTop(_this.inputEl().getHeight());
15767                 return;
15768             }
15769             if(c == 'top'){
15770                 _this.picker().setTop(0 - _this.picker().getHeight());
15771                 return;
15772             }
15773             
15774             if(c == 'left'){
15775                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15776                 return;
15777             }
15778             if(c == 'right'){
15779                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15780                 return;
15781             }
15782         });
15783         
15784     },
15785   
15786     onFocus : function()
15787     {
15788         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15789         this.show();
15790     },
15791     
15792     onBlur : function()
15793     {
15794         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15795         this.hide();
15796     },
15797     
15798     show : function()
15799     {
15800         this.picker().show();
15801         this.pop.show();
15802         this.update();
15803         this.place();
15804         
15805         this.fireEvent('show', this, this.date);
15806     },
15807     
15808     hide : function()
15809     {
15810         this.picker().hide();
15811         this.pop.hide();
15812         
15813         this.fireEvent('hide', this, this.date);
15814     },
15815     
15816     setTime : function()
15817     {
15818         this.hide();
15819         this.setValue(this.time.format(this.format));
15820         
15821         this.fireEvent('select', this, this.date);
15822         
15823         
15824     },
15825     
15826     onMousedown: function(e){
15827         e.stopPropagation();
15828         e.preventDefault();
15829     },
15830     
15831     onIncrementHours: function()
15832     {
15833         Roo.log('onIncrementHours');
15834         this.time = this.time.add(Date.HOUR, 1);
15835         this.update();
15836         
15837     },
15838     
15839     onDecrementHours: function()
15840     {
15841         Roo.log('onDecrementHours');
15842         this.time = this.time.add(Date.HOUR, -1);
15843         this.update();
15844     },
15845     
15846     onIncrementMinutes: function()
15847     {
15848         Roo.log('onIncrementMinutes');
15849         this.time = this.time.add(Date.MINUTE, 1);
15850         this.update();
15851     },
15852     
15853     onDecrementMinutes: function()
15854     {
15855         Roo.log('onDecrementMinutes');
15856         this.time = this.time.add(Date.MINUTE, -1);
15857         this.update();
15858     },
15859     
15860     onTogglePeriod: function()
15861     {
15862         Roo.log('onTogglePeriod');
15863         this.time = this.time.add(Date.HOUR, 12);
15864         this.update();
15865     }
15866     
15867    
15868 });
15869
15870 Roo.apply(Roo.bootstrap.TimeField,  {
15871     
15872     content : {
15873         tag: 'tbody',
15874         cn: [
15875             {
15876                 tag: 'tr',
15877                 cn: [
15878                 {
15879                     tag: 'td',
15880                     colspan: '7'
15881                 }
15882                 ]
15883             }
15884         ]
15885     },
15886     
15887     footer : {
15888         tag: 'tfoot',
15889         cn: [
15890             {
15891                 tag: 'tr',
15892                 cn: [
15893                 {
15894                     tag: 'th',
15895                     colspan: '7',
15896                     cls: '',
15897                     cn: [
15898                         {
15899                             tag: 'button',
15900                             cls: 'btn btn-info ok',
15901                             html: 'OK'
15902                         }
15903                     ]
15904                 }
15905
15906                 ]
15907             }
15908         ]
15909     }
15910 });
15911
15912 Roo.apply(Roo.bootstrap.TimeField,  {
15913   
15914     template : {
15915         tag: 'div',
15916         cls: 'datepicker dropdown-menu',
15917         cn: [
15918             {
15919                 tag: 'div',
15920                 cls: 'datepicker-time',
15921                 cn: [
15922                 {
15923                     tag: 'table',
15924                     cls: 'table-condensed',
15925                     cn:[
15926                     Roo.bootstrap.TimeField.content,
15927                     Roo.bootstrap.TimeField.footer
15928                     ]
15929                 }
15930                 ]
15931             }
15932         ]
15933     }
15934 });
15935
15936  
15937
15938  /*
15939  * - LGPL
15940  *
15941  * CheckBox
15942  * 
15943  */
15944
15945 /**
15946  * @class Roo.bootstrap.CheckBox
15947  * @extends Roo.bootstrap.Input
15948  * Bootstrap CheckBox class
15949  * 
15950  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15951  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15952  * @cfg {String} boxLabel The text that appears beside the checkbox
15953  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15954  * @cfg {Boolean} checked initnal the element
15955  * 
15956  * 
15957  * @constructor
15958  * Create a new CheckBox
15959  * @param {Object} config The config object
15960  */
15961
15962 Roo.bootstrap.CheckBox = function(config){
15963     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15964    
15965         this.addEvents({
15966             /**
15967             * @event check
15968             * Fires when the element is checked or unchecked.
15969             * @param {Roo.bootstrap.CheckBox} this This input
15970             * @param {Boolean} checked The new checked value
15971             */
15972            check : true
15973         });
15974 };
15975
15976 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15977     
15978     inputType: 'checkbox',
15979     inputValue: 1,
15980     valueOff: 0,
15981     boxLabel: false,
15982     checked: false,
15983     weight : false,
15984     
15985     getAutoCreate : function()
15986     {
15987         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15988         
15989         var id = Roo.id();
15990         
15991         var cfg = {};
15992         
15993         cfg.cls = 'form-group checkbox' //input-group
15994         
15995         
15996         
15997         
15998         var input =  {
15999             tag: 'input',
16000             id : id,
16001             type : this.inputType,
16002             value : (!this.checked) ? this.valueOff : this.inputValue,
16003             cls : 'roo-checkbox', //'form-box',
16004             placeholder : this.placeholder || ''
16005             
16006         };
16007         
16008         if (this.weight) { // Validity check?
16009             cfg.cls += " checkbox-" + this.weight;
16010         }
16011         
16012         if (this.disabled) {
16013             input.disabled=true;
16014         }
16015         
16016         if(this.checked){
16017             input.checked = this.checked;
16018         }
16019         
16020         if (this.name) {
16021             input.name = this.name;
16022         }
16023         
16024         if (this.size) {
16025             input.cls += ' input-' + this.size;
16026         }
16027         
16028         var settings=this;
16029         ['xs','sm','md','lg'].map(function(size){
16030             if (settings[size]) {
16031                 cfg.cls += ' col-' + size + '-' + settings[size];
16032             }
16033         });
16034         
16035        
16036         
16037         var inputblock = input;
16038         
16039         
16040         
16041         
16042         if (this.before || this.after) {
16043             
16044             inputblock = {
16045                 cls : 'input-group',
16046                 cn :  [] 
16047             };
16048             if (this.before) {
16049                 inputblock.cn.push({
16050                     tag :'span',
16051                     cls : 'input-group-addon',
16052                     html : this.before
16053                 });
16054             }
16055             inputblock.cn.push(input);
16056             if (this.after) {
16057                 inputblock.cn.push({
16058                     tag :'span',
16059                     cls : 'input-group-addon',
16060                     html : this.after
16061                 });
16062             }
16063             
16064         };
16065         
16066         if (align ==='left' && this.fieldLabel.length) {
16067                 Roo.log("left and has label");
16068                 cfg.cn = [
16069                     
16070                     {
16071                         tag: 'label',
16072                         'for' :  id,
16073                         cls : 'control-label col-md-' + this.labelWidth,
16074                         html : this.fieldLabel
16075                         
16076                     },
16077                     {
16078                         cls : "col-md-" + (12 - this.labelWidth), 
16079                         cn: [
16080                             inputblock
16081                         ]
16082                     }
16083                     
16084                 ];
16085         } else if ( this.fieldLabel.length) {
16086                 Roo.log(" label");
16087                 cfg.cn = [
16088                    
16089                     {
16090                         tag: this.boxLabel ? 'span' : 'label',
16091                         'for': id,
16092                         cls: 'control-label box-input-label',
16093                         //cls : 'input-group-addon',
16094                         html : this.fieldLabel
16095                         
16096                     },
16097                     
16098                     inputblock
16099                     
16100                 ];
16101
16102         } else {
16103             
16104                 Roo.log(" no label && no align");
16105                 cfg.cn = [  inputblock ] ;
16106                 
16107                 
16108         };
16109          if(this.boxLabel){
16110             cfg.cn.push( {
16111                 tag: 'label',
16112                 'for': id,
16113                 cls: 'box-label',
16114                 html: this.boxLabel
16115                 
16116             });
16117         }
16118         
16119         
16120        
16121         return cfg;
16122         
16123     },
16124     
16125     /**
16126      * return the real input element.
16127      */
16128     inputEl: function ()
16129     {
16130         return this.el.select('input.roo-checkbox',true).first();
16131     },
16132     
16133     label: function()
16134     {
16135         return this.el.select('label.control-label',true).first();
16136     },
16137     
16138     initEvents : function()
16139     {
16140 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16141         
16142         this.inputEl().on('click', this.onClick,  this);
16143         
16144     },
16145     
16146     onClick : function()
16147     {   
16148         this.setChecked(!this.checked);
16149     },
16150     
16151     setChecked : function(state,suppressEvent)
16152     {
16153         this.checked = state;
16154         
16155         this.inputEl().dom.checked = state;
16156         
16157         if(suppressEvent !== true){
16158             this.fireEvent('check', this, state);
16159         }
16160         
16161         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16162         
16163     },
16164     
16165     setValue : function(v,suppressEvent)
16166     {
16167         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16168     }
16169     
16170 });
16171
16172  
16173 /*
16174  * - LGPL
16175  *
16176  * Radio
16177  * 
16178  */
16179
16180 /**
16181  * @class Roo.bootstrap.Radio
16182  * @extends Roo.bootstrap.CheckBox
16183  * Bootstrap Radio class
16184
16185  * @constructor
16186  * Create a new Radio
16187  * @param {Object} config The config object
16188  */
16189
16190 Roo.bootstrap.Radio = function(config){
16191     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16192    
16193 };
16194
16195 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16196     
16197     inputType: 'radio',
16198     inputValue: '',
16199     valueOff: '',
16200     
16201     getAutoCreate : function()
16202     {
16203         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16204         
16205         var id = Roo.id();
16206         
16207         var cfg = {};
16208         
16209         cfg.cls = 'form-group radio' //input-group
16210         
16211         var input =  {
16212             tag: 'input',
16213             id : id,
16214             type : this.inputType,
16215             value : (!this.checked) ? this.valueOff : this.inputValue,
16216             cls : 'roo-radio',
16217             placeholder : this.placeholder || ''
16218             
16219         };
16220           if (this.weight) { // Validity check?
16221             cfg.cls += " radio-" + this.weight;
16222         }
16223         if (this.disabled) {
16224             input.disabled=true;
16225         }
16226         
16227         if(this.checked){
16228             input.checked = this.checked;
16229         }
16230         
16231         if (this.name) {
16232             input.name = this.name;
16233         }
16234         
16235         if (this.size) {
16236             input.cls += ' input-' + this.size;
16237         }
16238         
16239         var settings=this;
16240         ['xs','sm','md','lg'].map(function(size){
16241             if (settings[size]) {
16242                 cfg.cls += ' col-' + size + '-' + settings[size];
16243             }
16244         });
16245         
16246         var inputblock = input;
16247         
16248         if (this.before || this.after) {
16249             
16250             inputblock = {
16251                 cls : 'input-group',
16252                 cn :  [] 
16253             };
16254             if (this.before) {
16255                 inputblock.cn.push({
16256                     tag :'span',
16257                     cls : 'input-group-addon',
16258                     html : this.before
16259                 });
16260             }
16261             inputblock.cn.push(input);
16262             if (this.after) {
16263                 inputblock.cn.push({
16264                     tag :'span',
16265                     cls : 'input-group-addon',
16266                     html : this.after
16267                 });
16268             }
16269             
16270         };
16271         
16272         if (align ==='left' && this.fieldLabel.length) {
16273                 Roo.log("left and has label");
16274                 cfg.cn = [
16275                     
16276                     {
16277                         tag: 'label',
16278                         'for' :  id,
16279                         cls : 'control-label col-md-' + this.labelWidth,
16280                         html : this.fieldLabel
16281                         
16282                     },
16283                     {
16284                         cls : "col-md-" + (12 - this.labelWidth), 
16285                         cn: [
16286                             inputblock
16287                         ]
16288                     }
16289                     
16290                 ];
16291         } else if ( this.fieldLabel.length) {
16292                 Roo.log(" label");
16293                  cfg.cn = [
16294                    
16295                     {
16296                         tag: 'label',
16297                         'for': id,
16298                         cls: 'control-label box-input-label',
16299                         //cls : 'input-group-addon',
16300                         html : this.fieldLabel
16301                         
16302                     },
16303                     
16304                     inputblock
16305                     
16306                 ];
16307
16308         } else {
16309             
16310                    Roo.log(" no label && no align");
16311                 cfg.cn = [
16312                     
16313                         inputblock
16314                     
16315                 ];
16316                 
16317                 
16318         };
16319         
16320         if(this.boxLabel){
16321             cfg.cn.push({
16322                 tag: 'label',
16323                 'for': id,
16324                 cls: 'box-label',
16325                 html: this.boxLabel
16326             })
16327         }
16328         
16329         return cfg;
16330         
16331     },
16332     inputEl: function ()
16333     {
16334         return this.el.select('input.roo-radio',true).first();
16335     },
16336     onClick : function()
16337     {   
16338         this.setChecked(true);
16339     },
16340     
16341     setChecked : function(state,suppressEvent)
16342     {
16343         if(state){
16344             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16345                 v.dom.checked = false;
16346             });
16347         }
16348         
16349         this.checked = state;
16350         this.inputEl().dom.checked = state;
16351         
16352         if(suppressEvent !== true){
16353             this.fireEvent('check', this, state);
16354         }
16355         
16356         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16357         
16358     },
16359     
16360     getGroupValue : function()
16361     {
16362         var value = ''
16363         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16364             if(v.dom.checked == true){
16365                 value = v.dom.value;
16366             }
16367         });
16368         
16369         return value;
16370     },
16371     
16372     /**
16373      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16374      * @return {Mixed} value The field value
16375      */
16376     getValue : function(){
16377         return this.getGroupValue();
16378     }
16379     
16380 });
16381
16382  
16383 //<script type="text/javascript">
16384
16385 /*
16386  * Based  Ext JS Library 1.1.1
16387  * Copyright(c) 2006-2007, Ext JS, LLC.
16388  * LGPL
16389  *
16390  */
16391  
16392 /**
16393  * @class Roo.HtmlEditorCore
16394  * @extends Roo.Component
16395  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16396  *
16397  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16398  */
16399
16400 Roo.HtmlEditorCore = function(config){
16401     
16402     
16403     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16404     
16405     
16406     this.addEvents({
16407         /**
16408          * @event initialize
16409          * Fires when the editor is fully initialized (including the iframe)
16410          * @param {Roo.HtmlEditorCore} this
16411          */
16412         initialize: true,
16413         /**
16414          * @event activate
16415          * Fires when the editor is first receives the focus. Any insertion must wait
16416          * until after this event.
16417          * @param {Roo.HtmlEditorCore} this
16418          */
16419         activate: true,
16420          /**
16421          * @event beforesync
16422          * Fires before the textarea is updated with content from the editor iframe. Return false
16423          * to cancel the sync.
16424          * @param {Roo.HtmlEditorCore} this
16425          * @param {String} html
16426          */
16427         beforesync: true,
16428          /**
16429          * @event beforepush
16430          * Fires before the iframe editor is updated with content from the textarea. Return false
16431          * to cancel the push.
16432          * @param {Roo.HtmlEditorCore} this
16433          * @param {String} html
16434          */
16435         beforepush: true,
16436          /**
16437          * @event sync
16438          * Fires when the textarea is updated with content from the editor iframe.
16439          * @param {Roo.HtmlEditorCore} this
16440          * @param {String} html
16441          */
16442         sync: true,
16443          /**
16444          * @event push
16445          * Fires when the iframe editor is updated with content from the textarea.
16446          * @param {Roo.HtmlEditorCore} this
16447          * @param {String} html
16448          */
16449         push: true,
16450         
16451         /**
16452          * @event editorevent
16453          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16454          * @param {Roo.HtmlEditorCore} this
16455          */
16456         editorevent: true
16457     });
16458     
16459     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16460     
16461     // defaults : white / black...
16462     this.applyBlacklists();
16463     
16464     
16465     
16466 };
16467
16468
16469 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16470
16471
16472      /**
16473      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16474      */
16475     
16476     owner : false,
16477     
16478      /**
16479      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16480      *                        Roo.resizable.
16481      */
16482     resizable : false,
16483      /**
16484      * @cfg {Number} height (in pixels)
16485      */   
16486     height: 300,
16487    /**
16488      * @cfg {Number} width (in pixels)
16489      */   
16490     width: 500,
16491     
16492     /**
16493      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16494      * 
16495      */
16496     stylesheets: false,
16497     
16498     // id of frame..
16499     frameId: false,
16500     
16501     // private properties
16502     validationEvent : false,
16503     deferHeight: true,
16504     initialized : false,
16505     activated : false,
16506     sourceEditMode : false,
16507     onFocus : Roo.emptyFn,
16508     iframePad:3,
16509     hideMode:'offsets',
16510     
16511     clearUp: true,
16512     
16513     // blacklist + whitelisted elements..
16514     black: false,
16515     white: false,
16516      
16517     
16518
16519     /**
16520      * Protected method that will not generally be called directly. It
16521      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16522      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16523      */
16524     getDocMarkup : function(){
16525         // body styles..
16526         var st = '';
16527         Roo.log(this.stylesheets);
16528         
16529         // inherit styels from page...?? 
16530         if (this.stylesheets === false) {
16531             
16532             Roo.get(document.head).select('style').each(function(node) {
16533                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16534             });
16535             
16536             Roo.get(document.head).select('link').each(function(node) { 
16537                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16538             });
16539             
16540         } else if (!this.stylesheets.length) {
16541                 // simple..
16542                 st = '<style type="text/css">' +
16543                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16544                    '</style>';
16545         } else {
16546             Roo.each(this.stylesheets, function(s) {
16547                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16548             });
16549             
16550         }
16551         
16552         st +=  '<style type="text/css">' +
16553             'IMG { cursor: pointer } ' +
16554         '</style>';
16555
16556         
16557         return '<html><head>' + st  +
16558             //<style type="text/css">' +
16559             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16560             //'</style>' +
16561             ' </head><body class="roo-htmleditor-body"></body></html>';
16562     },
16563
16564     // private
16565     onRender : function(ct, position)
16566     {
16567         var _t = this;
16568         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16569         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16570         
16571         
16572         this.el.dom.style.border = '0 none';
16573         this.el.dom.setAttribute('tabIndex', -1);
16574         this.el.addClass('x-hidden hide');
16575         
16576         
16577         
16578         if(Roo.isIE){ // fix IE 1px bogus margin
16579             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16580         }
16581        
16582         
16583         this.frameId = Roo.id();
16584         
16585          
16586         
16587         var iframe = this.owner.wrap.createChild({
16588             tag: 'iframe',
16589             cls: 'form-control', // bootstrap..
16590             id: this.frameId,
16591             name: this.frameId,
16592             frameBorder : 'no',
16593             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16594         }, this.el
16595         );
16596         
16597         
16598         this.iframe = iframe.dom;
16599
16600          this.assignDocWin();
16601         
16602         this.doc.designMode = 'on';
16603        
16604         this.doc.open();
16605         this.doc.write(this.getDocMarkup());
16606         this.doc.close();
16607
16608         
16609         var task = { // must defer to wait for browser to be ready
16610             run : function(){
16611                 //console.log("run task?" + this.doc.readyState);
16612                 this.assignDocWin();
16613                 if(this.doc.body || this.doc.readyState == 'complete'){
16614                     try {
16615                         this.doc.designMode="on";
16616                     } catch (e) {
16617                         return;
16618                     }
16619                     Roo.TaskMgr.stop(task);
16620                     this.initEditor.defer(10, this);
16621                 }
16622             },
16623             interval : 10,
16624             duration: 10000,
16625             scope: this
16626         };
16627         Roo.TaskMgr.start(task);
16628
16629         
16630          
16631     },
16632
16633     // private
16634     onResize : function(w, h)
16635     {
16636          Roo.log('resize: ' +w + ',' + h );
16637         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16638         if(!this.iframe){
16639             return;
16640         }
16641         if(typeof w == 'number'){
16642             
16643             this.iframe.style.width = w + 'px';
16644         }
16645         if(typeof h == 'number'){
16646             
16647             this.iframe.style.height = h + 'px';
16648             if(this.doc){
16649                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16650             }
16651         }
16652         
16653     },
16654
16655     /**
16656      * Toggles the editor between standard and source edit mode.
16657      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16658      */
16659     toggleSourceEdit : function(sourceEditMode){
16660         
16661         this.sourceEditMode = sourceEditMode === true;
16662         
16663         if(this.sourceEditMode){
16664  
16665             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16666             
16667         }else{
16668             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16669             //this.iframe.className = '';
16670             this.deferFocus();
16671         }
16672         //this.setSize(this.owner.wrap.getSize());
16673         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16674     },
16675
16676     
16677   
16678
16679     /**
16680      * Protected method that will not generally be called directly. If you need/want
16681      * custom HTML cleanup, this is the method you should override.
16682      * @param {String} html The HTML to be cleaned
16683      * return {String} The cleaned HTML
16684      */
16685     cleanHtml : function(html){
16686         html = String(html);
16687         if(html.length > 5){
16688             if(Roo.isSafari){ // strip safari nonsense
16689                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16690             }
16691         }
16692         if(html == '&nbsp;'){
16693             html = '';
16694         }
16695         return html;
16696     },
16697
16698     /**
16699      * HTML Editor -> Textarea
16700      * Protected method that will not generally be called directly. Syncs the contents
16701      * of the editor iframe with the textarea.
16702      */
16703     syncValue : function(){
16704         if(this.initialized){
16705             var bd = (this.doc.body || this.doc.documentElement);
16706             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16707             var html = bd.innerHTML;
16708             if(Roo.isSafari){
16709                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16710                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16711                 if(m && m[1]){
16712                     html = '<div style="'+m[0]+'">' + html + '</div>';
16713                 }
16714             }
16715             html = this.cleanHtml(html);
16716             // fix up the special chars.. normaly like back quotes in word...
16717             // however we do not want to do this with chinese..
16718             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16719                 var cc = b.charCodeAt();
16720                 if (
16721                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16722                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16723                     (cc >= 0xf900 && cc < 0xfb00 )
16724                 ) {
16725                         return b;
16726                 }
16727                 return "&#"+cc+";" 
16728             });
16729             if(this.owner.fireEvent('beforesync', this, html) !== false){
16730                 this.el.dom.value = html;
16731                 this.owner.fireEvent('sync', this, html);
16732             }
16733         }
16734     },
16735
16736     /**
16737      * Protected method that will not generally be called directly. Pushes the value of the textarea
16738      * into the iframe editor.
16739      */
16740     pushValue : function(){
16741         if(this.initialized){
16742             var v = this.el.dom.value.trim();
16743             
16744 //            if(v.length < 1){
16745 //                v = '&#160;';
16746 //            }
16747             
16748             if(this.owner.fireEvent('beforepush', this, v) !== false){
16749                 var d = (this.doc.body || this.doc.documentElement);
16750                 d.innerHTML = v;
16751                 this.cleanUpPaste();
16752                 this.el.dom.value = d.innerHTML;
16753                 this.owner.fireEvent('push', this, v);
16754             }
16755         }
16756     },
16757
16758     // private
16759     deferFocus : function(){
16760         this.focus.defer(10, this);
16761     },
16762
16763     // doc'ed in Field
16764     focus : function(){
16765         if(this.win && !this.sourceEditMode){
16766             this.win.focus();
16767         }else{
16768             this.el.focus();
16769         }
16770     },
16771     
16772     assignDocWin: function()
16773     {
16774         var iframe = this.iframe;
16775         
16776          if(Roo.isIE){
16777             this.doc = iframe.contentWindow.document;
16778             this.win = iframe.contentWindow;
16779         } else {
16780 //            if (!Roo.get(this.frameId)) {
16781 //                return;
16782 //            }
16783 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16784 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16785             
16786             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16787                 return;
16788             }
16789             
16790             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16791             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16792         }
16793     },
16794     
16795     // private
16796     initEditor : function(){
16797         //console.log("INIT EDITOR");
16798         this.assignDocWin();
16799         
16800         
16801         
16802         this.doc.designMode="on";
16803         this.doc.open();
16804         this.doc.write(this.getDocMarkup());
16805         this.doc.close();
16806         
16807         var dbody = (this.doc.body || this.doc.documentElement);
16808         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16809         // this copies styles from the containing element into thsi one..
16810         // not sure why we need all of this..
16811         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16812         
16813         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16814         //ss['background-attachment'] = 'fixed'; // w3c
16815         dbody.bgProperties = 'fixed'; // ie
16816         //Roo.DomHelper.applyStyles(dbody, ss);
16817         Roo.EventManager.on(this.doc, {
16818             //'mousedown': this.onEditorEvent,
16819             'mouseup': this.onEditorEvent,
16820             'dblclick': this.onEditorEvent,
16821             'click': this.onEditorEvent,
16822             'keyup': this.onEditorEvent,
16823             buffer:100,
16824             scope: this
16825         });
16826         if(Roo.isGecko){
16827             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16828         }
16829         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16830             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16831         }
16832         this.initialized = true;
16833
16834         this.owner.fireEvent('initialize', this);
16835         this.pushValue();
16836     },
16837
16838     // private
16839     onDestroy : function(){
16840         
16841         
16842         
16843         if(this.rendered){
16844             
16845             //for (var i =0; i < this.toolbars.length;i++) {
16846             //    // fixme - ask toolbars for heights?
16847             //    this.toolbars[i].onDestroy();
16848            // }
16849             
16850             //this.wrap.dom.innerHTML = '';
16851             //this.wrap.remove();
16852         }
16853     },
16854
16855     // private
16856     onFirstFocus : function(){
16857         
16858         this.assignDocWin();
16859         
16860         
16861         this.activated = true;
16862          
16863     
16864         if(Roo.isGecko){ // prevent silly gecko errors
16865             this.win.focus();
16866             var s = this.win.getSelection();
16867             if(!s.focusNode || s.focusNode.nodeType != 3){
16868                 var r = s.getRangeAt(0);
16869                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16870                 r.collapse(true);
16871                 this.deferFocus();
16872             }
16873             try{
16874                 this.execCmd('useCSS', true);
16875                 this.execCmd('styleWithCSS', false);
16876             }catch(e){}
16877         }
16878         this.owner.fireEvent('activate', this);
16879     },
16880
16881     // private
16882     adjustFont: function(btn){
16883         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16884         //if(Roo.isSafari){ // safari
16885         //    adjust *= 2;
16886        // }
16887         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16888         if(Roo.isSafari){ // safari
16889             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16890             v =  (v < 10) ? 10 : v;
16891             v =  (v > 48) ? 48 : v;
16892             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16893             
16894         }
16895         
16896         
16897         v = Math.max(1, v+adjust);
16898         
16899         this.execCmd('FontSize', v  );
16900     },
16901
16902     onEditorEvent : function(e){
16903         this.owner.fireEvent('editorevent', this, e);
16904       //  this.updateToolbar();
16905         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16906     },
16907
16908     insertTag : function(tg)
16909     {
16910         // could be a bit smarter... -> wrap the current selected tRoo..
16911         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16912             
16913             range = this.createRange(this.getSelection());
16914             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16915             wrappingNode.appendChild(range.extractContents());
16916             range.insertNode(wrappingNode);
16917
16918             return;
16919             
16920             
16921             
16922         }
16923         this.execCmd("formatblock",   tg);
16924         
16925     },
16926     
16927     insertText : function(txt)
16928     {
16929         
16930         
16931         var range = this.createRange();
16932         range.deleteContents();
16933                //alert(Sender.getAttribute('label'));
16934                
16935         range.insertNode(this.doc.createTextNode(txt));
16936     } ,
16937     
16938      
16939
16940     /**
16941      * Executes a Midas editor command on the editor document and performs necessary focus and
16942      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16943      * @param {String} cmd The Midas command
16944      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16945      */
16946     relayCmd : function(cmd, value){
16947         this.win.focus();
16948         this.execCmd(cmd, value);
16949         this.owner.fireEvent('editorevent', this);
16950         //this.updateToolbar();
16951         this.owner.deferFocus();
16952     },
16953
16954     /**
16955      * Executes a Midas editor command directly on the editor document.
16956      * For visual commands, you should use {@link #relayCmd} instead.
16957      * <b>This should only be called after the editor is initialized.</b>
16958      * @param {String} cmd The Midas command
16959      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16960      */
16961     execCmd : function(cmd, value){
16962         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16963         this.syncValue();
16964     },
16965  
16966  
16967    
16968     /**
16969      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16970      * to insert tRoo.
16971      * @param {String} text | dom node.. 
16972      */
16973     insertAtCursor : function(text)
16974     {
16975         
16976         
16977         
16978         if(!this.activated){
16979             return;
16980         }
16981         /*
16982         if(Roo.isIE){
16983             this.win.focus();
16984             var r = this.doc.selection.createRange();
16985             if(r){
16986                 r.collapse(true);
16987                 r.pasteHTML(text);
16988                 this.syncValue();
16989                 this.deferFocus();
16990             
16991             }
16992             return;
16993         }
16994         */
16995         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16996             this.win.focus();
16997             
16998             
16999             // from jquery ui (MIT licenced)
17000             var range, node;
17001             var win = this.win;
17002             
17003             if (win.getSelection && win.getSelection().getRangeAt) {
17004                 range = win.getSelection().getRangeAt(0);
17005                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17006                 range.insertNode(node);
17007             } else if (win.document.selection && win.document.selection.createRange) {
17008                 // no firefox support
17009                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17010                 win.document.selection.createRange().pasteHTML(txt);
17011             } else {
17012                 // no firefox support
17013                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17014                 this.execCmd('InsertHTML', txt);
17015             } 
17016             
17017             this.syncValue();
17018             
17019             this.deferFocus();
17020         }
17021     },
17022  // private
17023     mozKeyPress : function(e){
17024         if(e.ctrlKey){
17025             var c = e.getCharCode(), cmd;
17026           
17027             if(c > 0){
17028                 c = String.fromCharCode(c).toLowerCase();
17029                 switch(c){
17030                     case 'b':
17031                         cmd = 'bold';
17032                         break;
17033                     case 'i':
17034                         cmd = 'italic';
17035                         break;
17036                     
17037                     case 'u':
17038                         cmd = 'underline';
17039                         break;
17040                     
17041                     case 'v':
17042                         this.cleanUpPaste.defer(100, this);
17043                         return;
17044                         
17045                 }
17046                 if(cmd){
17047                     this.win.focus();
17048                     this.execCmd(cmd);
17049                     this.deferFocus();
17050                     e.preventDefault();
17051                 }
17052                 
17053             }
17054         }
17055     },
17056
17057     // private
17058     fixKeys : function(){ // load time branching for fastest keydown performance
17059         if(Roo.isIE){
17060             return function(e){
17061                 var k = e.getKey(), r;
17062                 if(k == e.TAB){
17063                     e.stopEvent();
17064                     r = this.doc.selection.createRange();
17065                     if(r){
17066                         r.collapse(true);
17067                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17068                         this.deferFocus();
17069                     }
17070                     return;
17071                 }
17072                 
17073                 if(k == e.ENTER){
17074                     r = this.doc.selection.createRange();
17075                     if(r){
17076                         var target = r.parentElement();
17077                         if(!target || target.tagName.toLowerCase() != 'li'){
17078                             e.stopEvent();
17079                             r.pasteHTML('<br />');
17080                             r.collapse(false);
17081                             r.select();
17082                         }
17083                     }
17084                 }
17085                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17086                     this.cleanUpPaste.defer(100, this);
17087                     return;
17088                 }
17089                 
17090                 
17091             };
17092         }else if(Roo.isOpera){
17093             return function(e){
17094                 var k = e.getKey();
17095                 if(k == e.TAB){
17096                     e.stopEvent();
17097                     this.win.focus();
17098                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17099                     this.deferFocus();
17100                 }
17101                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17102                     this.cleanUpPaste.defer(100, this);
17103                     return;
17104                 }
17105                 
17106             };
17107         }else if(Roo.isSafari){
17108             return function(e){
17109                 var k = e.getKey();
17110                 
17111                 if(k == e.TAB){
17112                     e.stopEvent();
17113                     this.execCmd('InsertText','\t');
17114                     this.deferFocus();
17115                     return;
17116                 }
17117                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17118                     this.cleanUpPaste.defer(100, this);
17119                     return;
17120                 }
17121                 
17122              };
17123         }
17124     }(),
17125     
17126     getAllAncestors: function()
17127     {
17128         var p = this.getSelectedNode();
17129         var a = [];
17130         if (!p) {
17131             a.push(p); // push blank onto stack..
17132             p = this.getParentElement();
17133         }
17134         
17135         
17136         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17137             a.push(p);
17138             p = p.parentNode;
17139         }
17140         a.push(this.doc.body);
17141         return a;
17142     },
17143     lastSel : false,
17144     lastSelNode : false,
17145     
17146     
17147     getSelection : function() 
17148     {
17149         this.assignDocWin();
17150         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17151     },
17152     
17153     getSelectedNode: function() 
17154     {
17155         // this may only work on Gecko!!!
17156         
17157         // should we cache this!!!!
17158         
17159         
17160         
17161          
17162         var range = this.createRange(this.getSelection()).cloneRange();
17163         
17164         if (Roo.isIE) {
17165             var parent = range.parentElement();
17166             while (true) {
17167                 var testRange = range.duplicate();
17168                 testRange.moveToElementText(parent);
17169                 if (testRange.inRange(range)) {
17170                     break;
17171                 }
17172                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17173                     break;
17174                 }
17175                 parent = parent.parentElement;
17176             }
17177             return parent;
17178         }
17179         
17180         // is ancestor a text element.
17181         var ac =  range.commonAncestorContainer;
17182         if (ac.nodeType == 3) {
17183             ac = ac.parentNode;
17184         }
17185         
17186         var ar = ac.childNodes;
17187          
17188         var nodes = [];
17189         var other_nodes = [];
17190         var has_other_nodes = false;
17191         for (var i=0;i<ar.length;i++) {
17192             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17193                 continue;
17194             }
17195             // fullly contained node.
17196             
17197             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17198                 nodes.push(ar[i]);
17199                 continue;
17200             }
17201             
17202             // probably selected..
17203             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17204                 other_nodes.push(ar[i]);
17205                 continue;
17206             }
17207             // outer..
17208             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17209                 continue;
17210             }
17211             
17212             
17213             has_other_nodes = true;
17214         }
17215         if (!nodes.length && other_nodes.length) {
17216             nodes= other_nodes;
17217         }
17218         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17219             return false;
17220         }
17221         
17222         return nodes[0];
17223     },
17224     createRange: function(sel)
17225     {
17226         // this has strange effects when using with 
17227         // top toolbar - not sure if it's a great idea.
17228         //this.editor.contentWindow.focus();
17229         if (typeof sel != "undefined") {
17230             try {
17231                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17232             } catch(e) {
17233                 return this.doc.createRange();
17234             }
17235         } else {
17236             return this.doc.createRange();
17237         }
17238     },
17239     getParentElement: function()
17240     {
17241         
17242         this.assignDocWin();
17243         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17244         
17245         var range = this.createRange(sel);
17246          
17247         try {
17248             var p = range.commonAncestorContainer;
17249             while (p.nodeType == 3) { // text node
17250                 p = p.parentNode;
17251             }
17252             return p;
17253         } catch (e) {
17254             return null;
17255         }
17256     
17257     },
17258     /***
17259      *
17260      * Range intersection.. the hard stuff...
17261      *  '-1' = before
17262      *  '0' = hits..
17263      *  '1' = after.
17264      *         [ -- selected range --- ]
17265      *   [fail]                        [fail]
17266      *
17267      *    basically..
17268      *      if end is before start or  hits it. fail.
17269      *      if start is after end or hits it fail.
17270      *
17271      *   if either hits (but other is outside. - then it's not 
17272      *   
17273      *    
17274      **/
17275     
17276     
17277     // @see http://www.thismuchiknow.co.uk/?p=64.
17278     rangeIntersectsNode : function(range, node)
17279     {
17280         var nodeRange = node.ownerDocument.createRange();
17281         try {
17282             nodeRange.selectNode(node);
17283         } catch (e) {
17284             nodeRange.selectNodeContents(node);
17285         }
17286     
17287         var rangeStartRange = range.cloneRange();
17288         rangeStartRange.collapse(true);
17289     
17290         var rangeEndRange = range.cloneRange();
17291         rangeEndRange.collapse(false);
17292     
17293         var nodeStartRange = nodeRange.cloneRange();
17294         nodeStartRange.collapse(true);
17295     
17296         var nodeEndRange = nodeRange.cloneRange();
17297         nodeEndRange.collapse(false);
17298     
17299         return rangeStartRange.compareBoundaryPoints(
17300                  Range.START_TO_START, nodeEndRange) == -1 &&
17301                rangeEndRange.compareBoundaryPoints(
17302                  Range.START_TO_START, nodeStartRange) == 1;
17303         
17304          
17305     },
17306     rangeCompareNode : function(range, node)
17307     {
17308         var nodeRange = node.ownerDocument.createRange();
17309         try {
17310             nodeRange.selectNode(node);
17311         } catch (e) {
17312             nodeRange.selectNodeContents(node);
17313         }
17314         
17315         
17316         range.collapse(true);
17317     
17318         nodeRange.collapse(true);
17319      
17320         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17321         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17322          
17323         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17324         
17325         var nodeIsBefore   =  ss == 1;
17326         var nodeIsAfter    = ee == -1;
17327         
17328         if (nodeIsBefore && nodeIsAfter)
17329             return 0; // outer
17330         if (!nodeIsBefore && nodeIsAfter)
17331             return 1; //right trailed.
17332         
17333         if (nodeIsBefore && !nodeIsAfter)
17334             return 2;  // left trailed.
17335         // fully contined.
17336         return 3;
17337     },
17338
17339     // private? - in a new class?
17340     cleanUpPaste :  function()
17341     {
17342         // cleans up the whole document..
17343         Roo.log('cleanuppaste');
17344         
17345         this.cleanUpChildren(this.doc.body);
17346         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17347         if (clean != this.doc.body.innerHTML) {
17348             this.doc.body.innerHTML = clean;
17349         }
17350         
17351     },
17352     
17353     cleanWordChars : function(input) {// change the chars to hex code
17354         var he = Roo.HtmlEditorCore;
17355         
17356         var output = input;
17357         Roo.each(he.swapCodes, function(sw) { 
17358             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17359             
17360             output = output.replace(swapper, sw[1]);
17361         });
17362         
17363         return output;
17364     },
17365     
17366     
17367     cleanUpChildren : function (n)
17368     {
17369         if (!n.childNodes.length) {
17370             return;
17371         }
17372         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17373            this.cleanUpChild(n.childNodes[i]);
17374         }
17375     },
17376     
17377     
17378         
17379     
17380     cleanUpChild : function (node)
17381     {
17382         var ed = this;
17383         //console.log(node);
17384         if (node.nodeName == "#text") {
17385             // clean up silly Windows -- stuff?
17386             return; 
17387         }
17388         if (node.nodeName == "#comment") {
17389             node.parentNode.removeChild(node);
17390             // clean up silly Windows -- stuff?
17391             return; 
17392         }
17393         var lcname = node.tagName.toLowerCase();
17394         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17395         // whitelist of tags..
17396         
17397         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17398             // remove node.
17399             node.parentNode.removeChild(node);
17400             return;
17401             
17402         }
17403         
17404         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17405         
17406         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17407         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17408         
17409         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17410         //    remove_keep_children = true;
17411         //}
17412         
17413         if (remove_keep_children) {
17414             this.cleanUpChildren(node);
17415             // inserts everything just before this node...
17416             while (node.childNodes.length) {
17417                 var cn = node.childNodes[0];
17418                 node.removeChild(cn);
17419                 node.parentNode.insertBefore(cn, node);
17420             }
17421             node.parentNode.removeChild(node);
17422             return;
17423         }
17424         
17425         if (!node.attributes || !node.attributes.length) {
17426             this.cleanUpChildren(node);
17427             return;
17428         }
17429         
17430         function cleanAttr(n,v)
17431         {
17432             
17433             if (v.match(/^\./) || v.match(/^\//)) {
17434                 return;
17435             }
17436             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17437                 return;
17438             }
17439             if (v.match(/^#/)) {
17440                 return;
17441             }
17442 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17443             node.removeAttribute(n);
17444             
17445         }
17446         
17447         var cwhite = this.cwhite;
17448         var cblack = this.cblack;
17449             
17450         function cleanStyle(n,v)
17451         {
17452             if (v.match(/expression/)) { //XSS?? should we even bother..
17453                 node.removeAttribute(n);
17454                 return;
17455             }
17456             
17457             var parts = v.split(/;/);
17458             var clean = [];
17459             
17460             Roo.each(parts, function(p) {
17461                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17462                 if (!p.length) {
17463                     return true;
17464                 }
17465                 var l = p.split(':').shift().replace(/\s+/g,'');
17466                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17467                 
17468                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17469 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17470                     //node.removeAttribute(n);
17471                     return true;
17472                 }
17473                 //Roo.log()
17474                 // only allow 'c whitelisted system attributes'
17475                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17476 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17477                     //node.removeAttribute(n);
17478                     return true;
17479                 }
17480                 
17481                 
17482                  
17483                 
17484                 clean.push(p);
17485                 return true;
17486             });
17487             if (clean.length) { 
17488                 node.setAttribute(n, clean.join(';'));
17489             } else {
17490                 node.removeAttribute(n);
17491             }
17492             
17493         }
17494         
17495         
17496         for (var i = node.attributes.length-1; i > -1 ; i--) {
17497             var a = node.attributes[i];
17498             //console.log(a);
17499             
17500             if (a.name.toLowerCase().substr(0,2)=='on')  {
17501                 node.removeAttribute(a.name);
17502                 continue;
17503             }
17504             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17505                 node.removeAttribute(a.name);
17506                 continue;
17507             }
17508             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17509                 cleanAttr(a.name,a.value); // fixme..
17510                 continue;
17511             }
17512             if (a.name == 'style') {
17513                 cleanStyle(a.name,a.value);
17514                 continue;
17515             }
17516             /// clean up MS crap..
17517             // tecnically this should be a list of valid class'es..
17518             
17519             
17520             if (a.name == 'class') {
17521                 if (a.value.match(/^Mso/)) {
17522                     node.className = '';
17523                 }
17524                 
17525                 if (a.value.match(/body/)) {
17526                     node.className = '';
17527                 }
17528                 continue;
17529             }
17530             
17531             // style cleanup!?
17532             // class cleanup?
17533             
17534         }
17535         
17536         
17537         this.cleanUpChildren(node);
17538         
17539         
17540     },
17541     /**
17542      * Clean up MS wordisms...
17543      */
17544     cleanWord : function(node)
17545     {
17546         var _t = this;
17547         var cleanWordChildren = function()
17548         {
17549             if (!node.childNodes.length) {
17550                 return;
17551             }
17552             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17553                _t.cleanWord(node.childNodes[i]);
17554             }
17555         }
17556         
17557         
17558         if (!node) {
17559             this.cleanWord(this.doc.body);
17560             return;
17561         }
17562         if (node.nodeName == "#text") {
17563             // clean up silly Windows -- stuff?
17564             return; 
17565         }
17566         if (node.nodeName == "#comment") {
17567             node.parentNode.removeChild(node);
17568             // clean up silly Windows -- stuff?
17569             return; 
17570         }
17571         
17572         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17573             node.parentNode.removeChild(node);
17574             return;
17575         }
17576         
17577         // remove - but keep children..
17578         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17579             while (node.childNodes.length) {
17580                 var cn = node.childNodes[0];
17581                 node.removeChild(cn);
17582                 node.parentNode.insertBefore(cn, node);
17583             }
17584             node.parentNode.removeChild(node);
17585             cleanWordChildren();
17586             return;
17587         }
17588         // clean styles
17589         if (node.className.length) {
17590             
17591             var cn = node.className.split(/\W+/);
17592             var cna = [];
17593             Roo.each(cn, function(cls) {
17594                 if (cls.match(/Mso[a-zA-Z]+/)) {
17595                     return;
17596                 }
17597                 cna.push(cls);
17598             });
17599             node.className = cna.length ? cna.join(' ') : '';
17600             if (!cna.length) {
17601                 node.removeAttribute("class");
17602             }
17603         }
17604         
17605         if (node.hasAttribute("lang")) {
17606             node.removeAttribute("lang");
17607         }
17608         
17609         if (node.hasAttribute("style")) {
17610             
17611             var styles = node.getAttribute("style").split(";");
17612             var nstyle = [];
17613             Roo.each(styles, function(s) {
17614                 if (!s.match(/:/)) {
17615                     return;
17616                 }
17617                 var kv = s.split(":");
17618                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17619                     return;
17620                 }
17621                 // what ever is left... we allow.
17622                 nstyle.push(s);
17623             });
17624             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17625             if (!nstyle.length) {
17626                 node.removeAttribute('style');
17627             }
17628         }
17629         
17630         cleanWordChildren();
17631         
17632         
17633     },
17634     domToHTML : function(currentElement, depth, nopadtext) {
17635         
17636         depth = depth || 0;
17637         nopadtext = nopadtext || false;
17638     
17639         if (!currentElement) {
17640             return this.domToHTML(this.doc.body);
17641         }
17642         
17643         //Roo.log(currentElement);
17644         var j;
17645         var allText = false;
17646         var nodeName = currentElement.nodeName;
17647         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17648         
17649         if  (nodeName == '#text') {
17650             return currentElement.nodeValue;
17651         }
17652         
17653         
17654         var ret = '';
17655         if (nodeName != 'BODY') {
17656              
17657             var i = 0;
17658             // Prints the node tagName, such as <A>, <IMG>, etc
17659             if (tagName) {
17660                 var attr = [];
17661                 for(i = 0; i < currentElement.attributes.length;i++) {
17662                     // quoting?
17663                     var aname = currentElement.attributes.item(i).name;
17664                     if (!currentElement.attributes.item(i).value.length) {
17665                         continue;
17666                     }
17667                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17668                 }
17669                 
17670                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17671             } 
17672             else {
17673                 
17674                 // eack
17675             }
17676         } else {
17677             tagName = false;
17678         }
17679         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17680             return ret;
17681         }
17682         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17683             nopadtext = true;
17684         }
17685         
17686         
17687         // Traverse the tree
17688         i = 0;
17689         var currentElementChild = currentElement.childNodes.item(i);
17690         var allText = true;
17691         var innerHTML  = '';
17692         lastnode = '';
17693         while (currentElementChild) {
17694             // Formatting code (indent the tree so it looks nice on the screen)
17695             var nopad = nopadtext;
17696             if (lastnode == 'SPAN') {
17697                 nopad  = true;
17698             }
17699             // text
17700             if  (currentElementChild.nodeName == '#text') {
17701                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17702                 if (!nopad && toadd.length > 80) {
17703                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17704                 }
17705                 innerHTML  += toadd;
17706                 
17707                 i++;
17708                 currentElementChild = currentElement.childNodes.item(i);
17709                 lastNode = '';
17710                 continue;
17711             }
17712             allText = false;
17713             
17714             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17715                 
17716             // Recursively traverse the tree structure of the child node
17717             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17718             lastnode = currentElementChild.nodeName;
17719             i++;
17720             currentElementChild=currentElement.childNodes.item(i);
17721         }
17722         
17723         ret += innerHTML;
17724         
17725         if (!allText) {
17726                 // The remaining code is mostly for formatting the tree
17727             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17728         }
17729         
17730         
17731         if (tagName) {
17732             ret+= "</"+tagName+">";
17733         }
17734         return ret;
17735         
17736     },
17737         
17738     applyBlacklists : function()
17739     {
17740         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17741         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17742         
17743         this.white = [];
17744         this.black = [];
17745         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17746             if (b.indexOf(tag) > -1) {
17747                 return;
17748             }
17749             this.white.push(tag);
17750             
17751         }, this);
17752         
17753         Roo.each(w, function(tag) {
17754             if (b.indexOf(tag) > -1) {
17755                 return;
17756             }
17757             if (this.white.indexOf(tag) > -1) {
17758                 return;
17759             }
17760             this.white.push(tag);
17761             
17762         }, this);
17763         
17764         
17765         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17766             if (w.indexOf(tag) > -1) {
17767                 return;
17768             }
17769             this.black.push(tag);
17770             
17771         }, this);
17772         
17773         Roo.each(b, function(tag) {
17774             if (w.indexOf(tag) > -1) {
17775                 return;
17776             }
17777             if (this.black.indexOf(tag) > -1) {
17778                 return;
17779             }
17780             this.black.push(tag);
17781             
17782         }, this);
17783         
17784         
17785         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17786         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17787         
17788         this.cwhite = [];
17789         this.cblack = [];
17790         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17791             if (b.indexOf(tag) > -1) {
17792                 return;
17793             }
17794             this.cwhite.push(tag);
17795             
17796         }, this);
17797         
17798         Roo.each(w, function(tag) {
17799             if (b.indexOf(tag) > -1) {
17800                 return;
17801             }
17802             if (this.cwhite.indexOf(tag) > -1) {
17803                 return;
17804             }
17805             this.cwhite.push(tag);
17806             
17807         }, this);
17808         
17809         
17810         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17811             if (w.indexOf(tag) > -1) {
17812                 return;
17813             }
17814             this.cblack.push(tag);
17815             
17816         }, this);
17817         
17818         Roo.each(b, function(tag) {
17819             if (w.indexOf(tag) > -1) {
17820                 return;
17821             }
17822             if (this.cblack.indexOf(tag) > -1) {
17823                 return;
17824             }
17825             this.cblack.push(tag);
17826             
17827         }, this);
17828     }
17829     
17830     // hide stuff that is not compatible
17831     /**
17832      * @event blur
17833      * @hide
17834      */
17835     /**
17836      * @event change
17837      * @hide
17838      */
17839     /**
17840      * @event focus
17841      * @hide
17842      */
17843     /**
17844      * @event specialkey
17845      * @hide
17846      */
17847     /**
17848      * @cfg {String} fieldClass @hide
17849      */
17850     /**
17851      * @cfg {String} focusClass @hide
17852      */
17853     /**
17854      * @cfg {String} autoCreate @hide
17855      */
17856     /**
17857      * @cfg {String} inputType @hide
17858      */
17859     /**
17860      * @cfg {String} invalidClass @hide
17861      */
17862     /**
17863      * @cfg {String} invalidText @hide
17864      */
17865     /**
17866      * @cfg {String} msgFx @hide
17867      */
17868     /**
17869      * @cfg {String} validateOnBlur @hide
17870      */
17871 });
17872
17873 Roo.HtmlEditorCore.white = [
17874         'area', 'br', 'img', 'input', 'hr', 'wbr',
17875         
17876        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17877        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17878        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17879        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17880        'table',   'ul',         'xmp', 
17881        
17882        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17883       'thead',   'tr', 
17884      
17885       'dir', 'menu', 'ol', 'ul', 'dl',
17886        
17887       'embed',  'object'
17888 ];
17889
17890
17891 Roo.HtmlEditorCore.black = [
17892     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17893         'applet', // 
17894         'base',   'basefont', 'bgsound', 'blink',  'body', 
17895         'frame',  'frameset', 'head',    'html',   'ilayer', 
17896         'iframe', 'layer',  'link',     'meta',    'object',   
17897         'script', 'style' ,'title',  'xml' // clean later..
17898 ];
17899 Roo.HtmlEditorCore.clean = [
17900     'script', 'style', 'title', 'xml'
17901 ];
17902 Roo.HtmlEditorCore.remove = [
17903     'font'
17904 ];
17905 // attributes..
17906
17907 Roo.HtmlEditorCore.ablack = [
17908     'on'
17909 ];
17910     
17911 Roo.HtmlEditorCore.aclean = [ 
17912     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17913 ];
17914
17915 // protocols..
17916 Roo.HtmlEditorCore.pwhite= [
17917         'http',  'https',  'mailto'
17918 ];
17919
17920 // white listed style attributes.
17921 Roo.HtmlEditorCore.cwhite= [
17922       //  'text-align', /// default is to allow most things..
17923       
17924          
17925 //        'font-size'//??
17926 ];
17927
17928 // black listed style attributes.
17929 Roo.HtmlEditorCore.cblack= [
17930       //  'font-size' -- this can be set by the project 
17931 ];
17932
17933
17934 Roo.HtmlEditorCore.swapCodes   =[ 
17935     [    8211, "--" ], 
17936     [    8212, "--" ], 
17937     [    8216,  "'" ],  
17938     [    8217, "'" ],  
17939     [    8220, '"' ],  
17940     [    8221, '"' ],  
17941     [    8226, "*" ],  
17942     [    8230, "..." ]
17943 ]; 
17944
17945     /*
17946  * - LGPL
17947  *
17948  * HtmlEditor
17949  * 
17950  */
17951
17952 /**
17953  * @class Roo.bootstrap.HtmlEditor
17954  * @extends Roo.bootstrap.TextArea
17955  * Bootstrap HtmlEditor class
17956
17957  * @constructor
17958  * Create a new HtmlEditor
17959  * @param {Object} config The config object
17960  */
17961
17962 Roo.bootstrap.HtmlEditor = function(config){
17963     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17964     if (!this.toolbars) {
17965         this.toolbars = [];
17966     }
17967     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17968     this.addEvents({
17969             /**
17970              * @event initialize
17971              * Fires when the editor is fully initialized (including the iframe)
17972              * @param {HtmlEditor} this
17973              */
17974             initialize: true,
17975             /**
17976              * @event activate
17977              * Fires when the editor is first receives the focus. Any insertion must wait
17978              * until after this event.
17979              * @param {HtmlEditor} this
17980              */
17981             activate: true,
17982              /**
17983              * @event beforesync
17984              * Fires before the textarea is updated with content from the editor iframe. Return false
17985              * to cancel the sync.
17986              * @param {HtmlEditor} this
17987              * @param {String} html
17988              */
17989             beforesync: true,
17990              /**
17991              * @event beforepush
17992              * Fires before the iframe editor is updated with content from the textarea. Return false
17993              * to cancel the push.
17994              * @param {HtmlEditor} this
17995              * @param {String} html
17996              */
17997             beforepush: true,
17998              /**
17999              * @event sync
18000              * Fires when the textarea is updated with content from the editor iframe.
18001              * @param {HtmlEditor} this
18002              * @param {String} html
18003              */
18004             sync: true,
18005              /**
18006              * @event push
18007              * Fires when the iframe editor is updated with content from the textarea.
18008              * @param {HtmlEditor} this
18009              * @param {String} html
18010              */
18011             push: true,
18012              /**
18013              * @event editmodechange
18014              * Fires when the editor switches edit modes
18015              * @param {HtmlEditor} this
18016              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18017              */
18018             editmodechange: true,
18019             /**
18020              * @event editorevent
18021              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18022              * @param {HtmlEditor} this
18023              */
18024             editorevent: true,
18025             /**
18026              * @event firstfocus
18027              * Fires when on first focus - needed by toolbars..
18028              * @param {HtmlEditor} this
18029              */
18030             firstfocus: true,
18031             /**
18032              * @event autosave
18033              * Auto save the htmlEditor value as a file into Events
18034              * @param {HtmlEditor} this
18035              */
18036             autosave: true,
18037             /**
18038              * @event savedpreview
18039              * preview the saved version of htmlEditor
18040              * @param {HtmlEditor} this
18041              */
18042             savedpreview: true
18043         });
18044 };
18045
18046
18047 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18048     
18049     
18050       /**
18051      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18052      */
18053     toolbars : false,
18054    
18055      /**
18056      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18057      *                        Roo.resizable.
18058      */
18059     resizable : false,
18060      /**
18061      * @cfg {Number} height (in pixels)
18062      */   
18063     height: 300,
18064    /**
18065      * @cfg {Number} width (in pixels)
18066      */   
18067     width: false,
18068     
18069     /**
18070      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18071      * 
18072      */
18073     stylesheets: false,
18074     
18075     // id of frame..
18076     frameId: false,
18077     
18078     // private properties
18079     validationEvent : false,
18080     deferHeight: true,
18081     initialized : false,
18082     activated : false,
18083     
18084     onFocus : Roo.emptyFn,
18085     iframePad:3,
18086     hideMode:'offsets',
18087     
18088     
18089     tbContainer : false,
18090     
18091     toolbarContainer :function() {
18092         return this.wrap.select('.x-html-editor-tb',true).first();
18093     },
18094
18095     /**
18096      * Protected method that will not generally be called directly. It
18097      * is called when the editor creates its toolbar. Override this method if you need to
18098      * add custom toolbar buttons.
18099      * @param {HtmlEditor} editor
18100      */
18101     createToolbar : function(){
18102         
18103         Roo.log("create toolbars");
18104         
18105         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18106         this.toolbars[0].render(this.toolbarContainer());
18107         
18108         return;
18109         
18110 //        if (!editor.toolbars || !editor.toolbars.length) {
18111 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18112 //        }
18113 //        
18114 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18115 //            editor.toolbars[i] = Roo.factory(
18116 //                    typeof(editor.toolbars[i]) == 'string' ?
18117 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18118 //                Roo.bootstrap.HtmlEditor);
18119 //            editor.toolbars[i].init(editor);
18120 //        }
18121     },
18122
18123      
18124     // private
18125     onRender : function(ct, position)
18126     {
18127        // Roo.log("Call onRender: " + this.xtype);
18128         var _t = this;
18129         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18130       
18131         this.wrap = this.inputEl().wrap({
18132             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18133         });
18134         
18135         this.editorcore.onRender(ct, position);
18136          
18137         if (this.resizable) {
18138             this.resizeEl = new Roo.Resizable(this.wrap, {
18139                 pinned : true,
18140                 wrap: true,
18141                 dynamic : true,
18142                 minHeight : this.height,
18143                 height: this.height,
18144                 handles : this.resizable,
18145                 width: this.width,
18146                 listeners : {
18147                     resize : function(r, w, h) {
18148                         _t.onResize(w,h); // -something
18149                     }
18150                 }
18151             });
18152             
18153         }
18154         this.createToolbar(this);
18155        
18156         
18157         if(!this.width && this.resizable){
18158             this.setSize(this.wrap.getSize());
18159         }
18160         if (this.resizeEl) {
18161             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18162             // should trigger onReize..
18163         }
18164         
18165     },
18166
18167     // private
18168     onResize : function(w, h)
18169     {
18170         Roo.log('resize: ' +w + ',' + h );
18171         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18172         var ew = false;
18173         var eh = false;
18174         
18175         if(this.inputEl() ){
18176             if(typeof w == 'number'){
18177                 var aw = w - this.wrap.getFrameWidth('lr');
18178                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18179                 ew = aw;
18180             }
18181             if(typeof h == 'number'){
18182                  var tbh = -11;  // fixme it needs to tool bar size!
18183                 for (var i =0; i < this.toolbars.length;i++) {
18184                     // fixme - ask toolbars for heights?
18185                     tbh += this.toolbars[i].el.getHeight();
18186                     //if (this.toolbars[i].footer) {
18187                     //    tbh += this.toolbars[i].footer.el.getHeight();
18188                     //}
18189                 }
18190               
18191                 
18192                 
18193                 
18194                 
18195                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18196                 ah -= 5; // knock a few pixes off for look..
18197                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18198                 var eh = ah;
18199             }
18200         }
18201         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18202         this.editorcore.onResize(ew,eh);
18203         
18204     },
18205
18206     /**
18207      * Toggles the editor between standard and source edit mode.
18208      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18209      */
18210     toggleSourceEdit : function(sourceEditMode)
18211     {
18212         this.editorcore.toggleSourceEdit(sourceEditMode);
18213         
18214         if(this.editorcore.sourceEditMode){
18215             Roo.log('editor - showing textarea');
18216             
18217 //            Roo.log('in');
18218 //            Roo.log(this.syncValue());
18219             this.syncValue();
18220             this.inputEl().removeClass(['hide', 'x-hidden']);
18221             this.inputEl().dom.removeAttribute('tabIndex');
18222             this.inputEl().focus();
18223         }else{
18224             Roo.log('editor - hiding textarea');
18225 //            Roo.log('out')
18226 //            Roo.log(this.pushValue()); 
18227             this.pushValue();
18228             
18229             this.inputEl().addClass(['hide', 'x-hidden']);
18230             this.inputEl().dom.setAttribute('tabIndex', -1);
18231             //this.deferFocus();
18232         }
18233          
18234         if(this.resizable){
18235             this.setSize(this.wrap.getSize());
18236         }
18237         
18238         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18239     },
18240  
18241     // private (for BoxComponent)
18242     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18243
18244     // private (for BoxComponent)
18245     getResizeEl : function(){
18246         return this.wrap;
18247     },
18248
18249     // private (for BoxComponent)
18250     getPositionEl : function(){
18251         return this.wrap;
18252     },
18253
18254     // private
18255     initEvents : function(){
18256         this.originalValue = this.getValue();
18257     },
18258
18259 //    /**
18260 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18261 //     * @method
18262 //     */
18263 //    markInvalid : Roo.emptyFn,
18264 //    /**
18265 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18266 //     * @method
18267 //     */
18268 //    clearInvalid : Roo.emptyFn,
18269
18270     setValue : function(v){
18271         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18272         this.editorcore.pushValue();
18273     },
18274
18275      
18276     // private
18277     deferFocus : function(){
18278         this.focus.defer(10, this);
18279     },
18280
18281     // doc'ed in Field
18282     focus : function(){
18283         this.editorcore.focus();
18284         
18285     },
18286       
18287
18288     // private
18289     onDestroy : function(){
18290         
18291         
18292         
18293         if(this.rendered){
18294             
18295             for (var i =0; i < this.toolbars.length;i++) {
18296                 // fixme - ask toolbars for heights?
18297                 this.toolbars[i].onDestroy();
18298             }
18299             
18300             this.wrap.dom.innerHTML = '';
18301             this.wrap.remove();
18302         }
18303     },
18304
18305     // private
18306     onFirstFocus : function(){
18307         //Roo.log("onFirstFocus");
18308         this.editorcore.onFirstFocus();
18309          for (var i =0; i < this.toolbars.length;i++) {
18310             this.toolbars[i].onFirstFocus();
18311         }
18312         
18313     },
18314     
18315     // private
18316     syncValue : function()
18317     {   
18318         this.editorcore.syncValue();
18319     },
18320     
18321     pushValue : function()
18322     {   
18323         this.editorcore.pushValue();
18324     }
18325      
18326     
18327     // hide stuff that is not compatible
18328     /**
18329      * @event blur
18330      * @hide
18331      */
18332     /**
18333      * @event change
18334      * @hide
18335      */
18336     /**
18337      * @event focus
18338      * @hide
18339      */
18340     /**
18341      * @event specialkey
18342      * @hide
18343      */
18344     /**
18345      * @cfg {String} fieldClass @hide
18346      */
18347     /**
18348      * @cfg {String} focusClass @hide
18349      */
18350     /**
18351      * @cfg {String} autoCreate @hide
18352      */
18353     /**
18354      * @cfg {String} inputType @hide
18355      */
18356     /**
18357      * @cfg {String} invalidClass @hide
18358      */
18359     /**
18360      * @cfg {String} invalidText @hide
18361      */
18362     /**
18363      * @cfg {String} msgFx @hide
18364      */
18365     /**
18366      * @cfg {String} validateOnBlur @hide
18367      */
18368 });
18369  
18370     
18371    
18372    
18373    
18374       
18375 Roo.namespace('Roo.bootstrap.htmleditor');
18376 /**
18377  * @class Roo.bootstrap.HtmlEditorToolbar1
18378  * Basic Toolbar
18379  * 
18380  * Usage:
18381  *
18382  new Roo.bootstrap.HtmlEditor({
18383     ....
18384     toolbars : [
18385         new Roo.bootstrap.HtmlEditorToolbar1({
18386             disable : { fonts: 1 , format: 1, ..., ... , ...],
18387             btns : [ .... ]
18388         })
18389     }
18390      
18391  * 
18392  * @cfg {Object} disable List of elements to disable..
18393  * @cfg {Array} btns List of additional buttons.
18394  * 
18395  * 
18396  * NEEDS Extra CSS? 
18397  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18398  */
18399  
18400 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18401 {
18402     
18403     Roo.apply(this, config);
18404     
18405     // default disabled, based on 'good practice'..
18406     this.disable = this.disable || {};
18407     Roo.applyIf(this.disable, {
18408         fontSize : true,
18409         colors : true,
18410         specialElements : true
18411     });
18412     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18413     
18414     this.editor = config.editor;
18415     this.editorcore = config.editor.editorcore;
18416     
18417     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18418     
18419     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18420     // dont call parent... till later.
18421 }
18422 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18423      
18424     bar : true,
18425     
18426     editor : false,
18427     editorcore : false,
18428     
18429     
18430     formats : [
18431         "p" ,  
18432         "h1","h2","h3","h4","h5","h6", 
18433         "pre", "code", 
18434         "abbr", "acronym", "address", "cite", "samp", "var",
18435         'div','span'
18436     ],
18437     
18438     onRender : function(ct, position)
18439     {
18440        // Roo.log("Call onRender: " + this.xtype);
18441         
18442        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18443        Roo.log(this.el);
18444        this.el.dom.style.marginBottom = '0';
18445        var _this = this;
18446        var editorcore = this.editorcore;
18447        var editor= this.editor;
18448        
18449        var children = [];
18450        var btn = function(id,cmd , toggle, handler){
18451        
18452             var  event = toggle ? 'toggle' : 'click';
18453        
18454             var a = {
18455                 size : 'sm',
18456                 xtype: 'Button',
18457                 xns: Roo.bootstrap,
18458                 glyphicon : id,
18459                 cmd : id || cmd,
18460                 enableToggle:toggle !== false,
18461                 //html : 'submit'
18462                 pressed : toggle ? false : null,
18463                 listeners : {}
18464             }
18465             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18466                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18467             }
18468             children.push(a);
18469             return a;
18470        }
18471         
18472         var style = {
18473                 xtype: 'Button',
18474                 size : 'sm',
18475                 xns: Roo.bootstrap,
18476                 glyphicon : 'font',
18477                 //html : 'submit'
18478                 menu : {
18479                     xtype: 'Menu',
18480                     xns: Roo.bootstrap,
18481                     items:  []
18482                 }
18483         };
18484         Roo.each(this.formats, function(f) {
18485             style.menu.items.push({
18486                 xtype :'MenuItem',
18487                 xns: Roo.bootstrap,
18488                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18489                 tagname : f,
18490                 listeners : {
18491                     click : function()
18492                     {
18493                         editorcore.insertTag(this.tagname);
18494                         editor.focus();
18495                     }
18496                 }
18497                 
18498             });
18499         });
18500          children.push(style);   
18501             
18502             
18503         btn('bold',false,true);
18504         btn('italic',false,true);
18505         btn('align-left', 'justifyleft',true);
18506         btn('align-center', 'justifycenter',true);
18507         btn('align-right' , 'justifyright',true);
18508         btn('link', false, false, function(btn) {
18509             //Roo.log("create link?");
18510             var url = prompt(this.createLinkText, this.defaultLinkValue);
18511             if(url && url != 'http:/'+'/'){
18512                 this.editorcore.relayCmd('createlink', url);
18513             }
18514         }),
18515         btn('list','insertunorderedlist',true);
18516         btn('pencil', false,true, function(btn){
18517                 Roo.log(this);
18518                 
18519                 this.toggleSourceEdit(btn.pressed);
18520         });
18521         /*
18522         var cog = {
18523                 xtype: 'Button',
18524                 size : 'sm',
18525                 xns: Roo.bootstrap,
18526                 glyphicon : 'cog',
18527                 //html : 'submit'
18528                 menu : {
18529                     xtype: 'Menu',
18530                     xns: Roo.bootstrap,
18531                     items:  []
18532                 }
18533         };
18534         
18535         cog.menu.items.push({
18536             xtype :'MenuItem',
18537             xns: Roo.bootstrap,
18538             html : Clean styles,
18539             tagname : f,
18540             listeners : {
18541                 click : function()
18542                 {
18543                     editorcore.insertTag(this.tagname);
18544                     editor.focus();
18545                 }
18546             }
18547             
18548         });
18549        */
18550         
18551          
18552        this.xtype = 'NavSimplebar';
18553         
18554         for(var i=0;i< children.length;i++) {
18555             
18556             this.buttons.add(this.addxtypeChild(children[i]));
18557             
18558         }
18559         
18560         editor.on('editorevent', this.updateToolbar, this);
18561     },
18562     onBtnClick : function(id)
18563     {
18564        this.editorcore.relayCmd(id);
18565        this.editorcore.focus();
18566     },
18567     
18568     /**
18569      * Protected method that will not generally be called directly. It triggers
18570      * a toolbar update by reading the markup state of the current selection in the editor.
18571      */
18572     updateToolbar: function(){
18573
18574         if(!this.editorcore.activated){
18575             this.editor.onFirstFocus(); // is this neeed?
18576             return;
18577         }
18578
18579         var btns = this.buttons; 
18580         var doc = this.editorcore.doc;
18581         btns.get('bold').setActive(doc.queryCommandState('bold'));
18582         btns.get('italic').setActive(doc.queryCommandState('italic'));
18583         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18584         
18585         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18586         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18587         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18588         
18589         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18590         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18591          /*
18592         
18593         var ans = this.editorcore.getAllAncestors();
18594         if (this.formatCombo) {
18595             
18596             
18597             var store = this.formatCombo.store;
18598             this.formatCombo.setValue("");
18599             for (var i =0; i < ans.length;i++) {
18600                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18601                     // select it..
18602                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18603                     break;
18604                 }
18605             }
18606         }
18607         
18608         
18609         
18610         // hides menus... - so this cant be on a menu...
18611         Roo.bootstrap.MenuMgr.hideAll();
18612         */
18613         Roo.bootstrap.MenuMgr.hideAll();
18614         //this.editorsyncValue();
18615     },
18616     onFirstFocus: function() {
18617         this.buttons.each(function(item){
18618            item.enable();
18619         });
18620     },
18621     toggleSourceEdit : function(sourceEditMode){
18622         
18623           
18624         if(sourceEditMode){
18625             Roo.log("disabling buttons");
18626            this.buttons.each( function(item){
18627                 if(item.cmd != 'pencil'){
18628                     item.disable();
18629                 }
18630             });
18631           
18632         }else{
18633             Roo.log("enabling buttons");
18634             if(this.editorcore.initialized){
18635                 this.buttons.each( function(item){
18636                     item.enable();
18637                 });
18638             }
18639             
18640         }
18641         Roo.log("calling toggole on editor");
18642         // tell the editor that it's been pressed..
18643         this.editor.toggleSourceEdit(sourceEditMode);
18644        
18645     }
18646 });
18647
18648
18649
18650
18651
18652 /**
18653  * @class Roo.bootstrap.Table.AbstractSelectionModel
18654  * @extends Roo.util.Observable
18655  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18656  * implemented by descendant classes.  This class should not be directly instantiated.
18657  * @constructor
18658  */
18659 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18660     this.locked = false;
18661     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18662 };
18663
18664
18665 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18666     /** @ignore Called by the grid automatically. Do not call directly. */
18667     init : function(grid){
18668         this.grid = grid;
18669         this.initEvents();
18670     },
18671
18672     /**
18673      * Locks the selections.
18674      */
18675     lock : function(){
18676         this.locked = true;
18677     },
18678
18679     /**
18680      * Unlocks the selections.
18681      */
18682     unlock : function(){
18683         this.locked = false;
18684     },
18685
18686     /**
18687      * Returns true if the selections are locked.
18688      * @return {Boolean}
18689      */
18690     isLocked : function(){
18691         return this.locked;
18692     }
18693 });
18694 /**
18695  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18696  * @class Roo.bootstrap.Table.RowSelectionModel
18697  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18698  * It supports multiple selections and keyboard selection/navigation. 
18699  * @constructor
18700  * @param {Object} config
18701  */
18702
18703 Roo.bootstrap.Table.RowSelectionModel = function(config){
18704     Roo.apply(this, config);
18705     this.selections = new Roo.util.MixedCollection(false, function(o){
18706         return o.id;
18707     });
18708
18709     this.last = false;
18710     this.lastActive = false;
18711
18712     this.addEvents({
18713         /**
18714              * @event selectionchange
18715              * Fires when the selection changes
18716              * @param {SelectionModel} this
18717              */
18718             "selectionchange" : true,
18719         /**
18720              * @event afterselectionchange
18721              * Fires after the selection changes (eg. by key press or clicking)
18722              * @param {SelectionModel} this
18723              */
18724             "afterselectionchange" : true,
18725         /**
18726              * @event beforerowselect
18727              * Fires when a row is selected being selected, return false to cancel.
18728              * @param {SelectionModel} this
18729              * @param {Number} rowIndex The selected index
18730              * @param {Boolean} keepExisting False if other selections will be cleared
18731              */
18732             "beforerowselect" : true,
18733         /**
18734              * @event rowselect
18735              * Fires when a row is selected.
18736              * @param {SelectionModel} this
18737              * @param {Number} rowIndex The selected index
18738              * @param {Roo.data.Record} r The record
18739              */
18740             "rowselect" : true,
18741         /**
18742              * @event rowdeselect
18743              * Fires when a row is deselected.
18744              * @param {SelectionModel} this
18745              * @param {Number} rowIndex The selected index
18746              */
18747         "rowdeselect" : true
18748     });
18749     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18750     this.locked = false;
18751 };
18752
18753 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18754     /**
18755      * @cfg {Boolean} singleSelect
18756      * True to allow selection of only one row at a time (defaults to false)
18757      */
18758     singleSelect : false,
18759
18760     // private
18761     initEvents : function(){
18762
18763         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18764             this.grid.on("mousedown", this.handleMouseDown, this);
18765         }else{ // allow click to work like normal
18766             this.grid.on("rowclick", this.handleDragableRowClick, this);
18767         }
18768
18769         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18770             "up" : function(e){
18771                 if(!e.shiftKey){
18772                     this.selectPrevious(e.shiftKey);
18773                 }else if(this.last !== false && this.lastActive !== false){
18774                     var last = this.last;
18775                     this.selectRange(this.last,  this.lastActive-1);
18776                     this.grid.getView().focusRow(this.lastActive);
18777                     if(last !== false){
18778                         this.last = last;
18779                     }
18780                 }else{
18781                     this.selectFirstRow();
18782                 }
18783                 this.fireEvent("afterselectionchange", this);
18784             },
18785             "down" : function(e){
18786                 if(!e.shiftKey){
18787                     this.selectNext(e.shiftKey);
18788                 }else if(this.last !== false && this.lastActive !== false){
18789                     var last = this.last;
18790                     this.selectRange(this.last,  this.lastActive+1);
18791                     this.grid.getView().focusRow(this.lastActive);
18792                     if(last !== false){
18793                         this.last = last;
18794                     }
18795                 }else{
18796                     this.selectFirstRow();
18797                 }
18798                 this.fireEvent("afterselectionchange", this);
18799             },
18800             scope: this
18801         });
18802
18803         var view = this.grid.view;
18804         view.on("refresh", this.onRefresh, this);
18805         view.on("rowupdated", this.onRowUpdated, this);
18806         view.on("rowremoved", this.onRemove, this);
18807     },
18808
18809     // private
18810     onRefresh : function(){
18811         var ds = this.grid.dataSource, i, v = this.grid.view;
18812         var s = this.selections;
18813         s.each(function(r){
18814             if((i = ds.indexOfId(r.id)) != -1){
18815                 v.onRowSelect(i);
18816             }else{
18817                 s.remove(r);
18818             }
18819         });
18820     },
18821
18822     // private
18823     onRemove : function(v, index, r){
18824         this.selections.remove(r);
18825     },
18826
18827     // private
18828     onRowUpdated : function(v, index, r){
18829         if(this.isSelected(r)){
18830             v.onRowSelect(index);
18831         }
18832     },
18833
18834     /**
18835      * Select records.
18836      * @param {Array} records The records to select
18837      * @param {Boolean} keepExisting (optional) True to keep existing selections
18838      */
18839     selectRecords : function(records, keepExisting){
18840         if(!keepExisting){
18841             this.clearSelections();
18842         }
18843         var ds = this.grid.dataSource;
18844         for(var i = 0, len = records.length; i < len; i++){
18845             this.selectRow(ds.indexOf(records[i]), true);
18846         }
18847     },
18848
18849     /**
18850      * Gets the number of selected rows.
18851      * @return {Number}
18852      */
18853     getCount : function(){
18854         return this.selections.length;
18855     },
18856
18857     /**
18858      * Selects the first row in the grid.
18859      */
18860     selectFirstRow : function(){
18861         this.selectRow(0);
18862     },
18863
18864     /**
18865      * Select the last row.
18866      * @param {Boolean} keepExisting (optional) True to keep existing selections
18867      */
18868     selectLastRow : function(keepExisting){
18869         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18870     },
18871
18872     /**
18873      * Selects the row immediately following the last selected row.
18874      * @param {Boolean} keepExisting (optional) True to keep existing selections
18875      */
18876     selectNext : function(keepExisting){
18877         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18878             this.selectRow(this.last+1, keepExisting);
18879             this.grid.getView().focusRow(this.last);
18880         }
18881     },
18882
18883     /**
18884      * Selects the row that precedes the last selected row.
18885      * @param {Boolean} keepExisting (optional) True to keep existing selections
18886      */
18887     selectPrevious : function(keepExisting){
18888         if(this.last){
18889             this.selectRow(this.last-1, keepExisting);
18890             this.grid.getView().focusRow(this.last);
18891         }
18892     },
18893
18894     /**
18895      * Returns the selected records
18896      * @return {Array} Array of selected records
18897      */
18898     getSelections : function(){
18899         return [].concat(this.selections.items);
18900     },
18901
18902     /**
18903      * Returns the first selected record.
18904      * @return {Record}
18905      */
18906     getSelected : function(){
18907         return this.selections.itemAt(0);
18908     },
18909
18910
18911     /**
18912      * Clears all selections.
18913      */
18914     clearSelections : function(fast){
18915         if(this.locked) return;
18916         if(fast !== true){
18917             var ds = this.grid.dataSource;
18918             var s = this.selections;
18919             s.each(function(r){
18920                 this.deselectRow(ds.indexOfId(r.id));
18921             }, this);
18922             s.clear();
18923         }else{
18924             this.selections.clear();
18925         }
18926         this.last = false;
18927     },
18928
18929
18930     /**
18931      * Selects all rows.
18932      */
18933     selectAll : function(){
18934         if(this.locked) return;
18935         this.selections.clear();
18936         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18937             this.selectRow(i, true);
18938         }
18939     },
18940
18941     /**
18942      * Returns True if there is a selection.
18943      * @return {Boolean}
18944      */
18945     hasSelection : function(){
18946         return this.selections.length > 0;
18947     },
18948
18949     /**
18950      * Returns True if the specified row is selected.
18951      * @param {Number/Record} record The record or index of the record to check
18952      * @return {Boolean}
18953      */
18954     isSelected : function(index){
18955         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18956         return (r && this.selections.key(r.id) ? true : false);
18957     },
18958
18959     /**
18960      * Returns True if the specified record id is selected.
18961      * @param {String} id The id of record to check
18962      * @return {Boolean}
18963      */
18964     isIdSelected : function(id){
18965         return (this.selections.key(id) ? true : false);
18966     },
18967
18968     // private
18969     handleMouseDown : function(e, t){
18970         var view = this.grid.getView(), rowIndex;
18971         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18972             return;
18973         };
18974         if(e.shiftKey && this.last !== false){
18975             var last = this.last;
18976             this.selectRange(last, rowIndex, e.ctrlKey);
18977             this.last = last; // reset the last
18978             view.focusRow(rowIndex);
18979         }else{
18980             var isSelected = this.isSelected(rowIndex);
18981             if(e.button !== 0 && isSelected){
18982                 view.focusRow(rowIndex);
18983             }else if(e.ctrlKey && isSelected){
18984                 this.deselectRow(rowIndex);
18985             }else if(!isSelected){
18986                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18987                 view.focusRow(rowIndex);
18988             }
18989         }
18990         this.fireEvent("afterselectionchange", this);
18991     },
18992     // private
18993     handleDragableRowClick :  function(grid, rowIndex, e) 
18994     {
18995         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18996             this.selectRow(rowIndex, false);
18997             grid.view.focusRow(rowIndex);
18998              this.fireEvent("afterselectionchange", this);
18999         }
19000     },
19001     
19002     /**
19003      * Selects multiple rows.
19004      * @param {Array} rows Array of the indexes of the row to select
19005      * @param {Boolean} keepExisting (optional) True to keep existing selections
19006      */
19007     selectRows : function(rows, keepExisting){
19008         if(!keepExisting){
19009             this.clearSelections();
19010         }
19011         for(var i = 0, len = rows.length; i < len; i++){
19012             this.selectRow(rows[i], true);
19013         }
19014     },
19015
19016     /**
19017      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19018      * @param {Number} startRow The index of the first row in the range
19019      * @param {Number} endRow The index of the last row in the range
19020      * @param {Boolean} keepExisting (optional) True to retain existing selections
19021      */
19022     selectRange : function(startRow, endRow, keepExisting){
19023         if(this.locked) return;
19024         if(!keepExisting){
19025             this.clearSelections();
19026         }
19027         if(startRow <= endRow){
19028             for(var i = startRow; i <= endRow; i++){
19029                 this.selectRow(i, true);
19030             }
19031         }else{
19032             for(var i = startRow; i >= endRow; i--){
19033                 this.selectRow(i, true);
19034             }
19035         }
19036     },
19037
19038     /**
19039      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19040      * @param {Number} startRow The index of the first row in the range
19041      * @param {Number} endRow The index of the last row in the range
19042      */
19043     deselectRange : function(startRow, endRow, preventViewNotify){
19044         if(this.locked) return;
19045         for(var i = startRow; i <= endRow; i++){
19046             this.deselectRow(i, preventViewNotify);
19047         }
19048     },
19049
19050     /**
19051      * Selects a row.
19052      * @param {Number} row The index of the row to select
19053      * @param {Boolean} keepExisting (optional) True to keep existing selections
19054      */
19055     selectRow : function(index, keepExisting, preventViewNotify){
19056         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19057         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19058             if(!keepExisting || this.singleSelect){
19059                 this.clearSelections();
19060             }
19061             var r = this.grid.dataSource.getAt(index);
19062             this.selections.add(r);
19063             this.last = this.lastActive = index;
19064             if(!preventViewNotify){
19065                 this.grid.getView().onRowSelect(index);
19066             }
19067             this.fireEvent("rowselect", this, index, r);
19068             this.fireEvent("selectionchange", this);
19069         }
19070     },
19071
19072     /**
19073      * Deselects a row.
19074      * @param {Number} row The index of the row to deselect
19075      */
19076     deselectRow : function(index, preventViewNotify){
19077         if(this.locked) return;
19078         if(this.last == index){
19079             this.last = false;
19080         }
19081         if(this.lastActive == index){
19082             this.lastActive = false;
19083         }
19084         var r = this.grid.dataSource.getAt(index);
19085         this.selections.remove(r);
19086         if(!preventViewNotify){
19087             this.grid.getView().onRowDeselect(index);
19088         }
19089         this.fireEvent("rowdeselect", this, index);
19090         this.fireEvent("selectionchange", this);
19091     },
19092
19093     // private
19094     restoreLast : function(){
19095         if(this._last){
19096             this.last = this._last;
19097         }
19098     },
19099
19100     // private
19101     acceptsNav : function(row, col, cm){
19102         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19103     },
19104
19105     // private
19106     onEditorKey : function(field, e){
19107         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19108         if(k == e.TAB){
19109             e.stopEvent();
19110             ed.completeEdit();
19111             if(e.shiftKey){
19112                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19113             }else{
19114                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19115             }
19116         }else if(k == e.ENTER && !e.ctrlKey){
19117             e.stopEvent();
19118             ed.completeEdit();
19119             if(e.shiftKey){
19120                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19121             }else{
19122                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19123             }
19124         }else if(k == e.ESC){
19125             ed.cancelEdit();
19126         }
19127         if(newCell){
19128             g.startEditing(newCell[0], newCell[1]);
19129         }
19130     }
19131 });/*
19132  * Based on:
19133  * Ext JS Library 1.1.1
19134  * Copyright(c) 2006-2007, Ext JS, LLC.
19135  *
19136  * Originally Released Under LGPL - original licence link has changed is not relivant.
19137  *
19138  * Fork - LGPL
19139  * <script type="text/javascript">
19140  */
19141  
19142 /**
19143  * @class Roo.bootstrap.PagingToolbar
19144  * @extends Roo.Row
19145  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19146  * @constructor
19147  * Create a new PagingToolbar
19148  * @param {Object} config The config object
19149  */
19150 Roo.bootstrap.PagingToolbar = function(config)
19151 {
19152     // old args format still supported... - xtype is prefered..
19153         // created from xtype...
19154     var ds = config.dataSource;
19155     this.toolbarItems = [];
19156     if (config.items) {
19157         this.toolbarItems = config.items;
19158 //        config.items = [];
19159     }
19160     
19161     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19162     this.ds = ds;
19163     this.cursor = 0;
19164     if (ds) { 
19165         this.bind(ds);
19166     }
19167     
19168     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19169     
19170 };
19171
19172 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19173     /**
19174      * @cfg {Roo.data.Store} dataSource
19175      * The underlying data store providing the paged data
19176      */
19177     /**
19178      * @cfg {String/HTMLElement/Element} container
19179      * container The id or element that will contain the toolbar
19180      */
19181     /**
19182      * @cfg {Boolean} displayInfo
19183      * True to display the displayMsg (defaults to false)
19184      */
19185     /**
19186      * @cfg {Number} pageSize
19187      * The number of records to display per page (defaults to 20)
19188      */
19189     pageSize: 20,
19190     /**
19191      * @cfg {String} displayMsg
19192      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19193      */
19194     displayMsg : 'Displaying {0} - {1} of {2}',
19195     /**
19196      * @cfg {String} emptyMsg
19197      * The message to display when no records are found (defaults to "No data to display")
19198      */
19199     emptyMsg : 'No data to display',
19200     /**
19201      * Customizable piece of the default paging text (defaults to "Page")
19202      * @type String
19203      */
19204     beforePageText : "Page",
19205     /**
19206      * Customizable piece of the default paging text (defaults to "of %0")
19207      * @type String
19208      */
19209     afterPageText : "of {0}",
19210     /**
19211      * Customizable piece of the default paging text (defaults to "First Page")
19212      * @type String
19213      */
19214     firstText : "First Page",
19215     /**
19216      * Customizable piece of the default paging text (defaults to "Previous Page")
19217      * @type String
19218      */
19219     prevText : "Previous Page",
19220     /**
19221      * Customizable piece of the default paging text (defaults to "Next Page")
19222      * @type String
19223      */
19224     nextText : "Next Page",
19225     /**
19226      * Customizable piece of the default paging text (defaults to "Last Page")
19227      * @type String
19228      */
19229     lastText : "Last Page",
19230     /**
19231      * Customizable piece of the default paging text (defaults to "Refresh")
19232      * @type String
19233      */
19234     refreshText : "Refresh",
19235
19236     buttons : false,
19237     // private
19238     onRender : function(ct, position) 
19239     {
19240         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19241         this.navgroup.parentId = this.id;
19242         this.navgroup.onRender(this.el, null);
19243         // add the buttons to the navgroup
19244         
19245         if(this.displayInfo){
19246             Roo.log(this.el.select('ul.navbar-nav',true).first());
19247             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19248             this.displayEl = this.el.select('.x-paging-info', true).first();
19249 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19250 //            this.displayEl = navel.el.select('span',true).first();
19251         }
19252         
19253         var _this = this;
19254         
19255         if(this.buttons){
19256             Roo.each(_this.buttons, function(e){
19257                Roo.factory(e).onRender(_this.el, null);
19258             });
19259         }
19260             
19261         Roo.each(_this.toolbarItems, function(e) {
19262             _this.navgroup.addItem(e);
19263         });
19264         
19265         this.first = this.navgroup.addItem({
19266             tooltip: this.firstText,
19267             cls: "prev",
19268             icon : 'fa fa-backward',
19269             disabled: true,
19270             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19271         });
19272         
19273         this.prev =  this.navgroup.addItem({
19274             tooltip: this.prevText,
19275             cls: "prev",
19276             icon : 'fa fa-step-backward',
19277             disabled: true,
19278             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19279         });
19280     //this.addSeparator();
19281         
19282         
19283         var field = this.navgroup.addItem( {
19284             tagtype : 'span',
19285             cls : 'x-paging-position',
19286             
19287             html : this.beforePageText  +
19288                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19289                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19290          } ); //?? escaped?
19291         
19292         this.field = field.el.select('input', true).first();
19293         this.field.on("keydown", this.onPagingKeydown, this);
19294         this.field.on("focus", function(){this.dom.select();});
19295     
19296     
19297         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19298         //this.field.setHeight(18);
19299         //this.addSeparator();
19300         this.next = this.navgroup.addItem({
19301             tooltip: this.nextText,
19302             cls: "next",
19303             html : ' <i class="fa fa-step-forward">',
19304             disabled: true,
19305             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19306         });
19307         this.last = this.navgroup.addItem({
19308             tooltip: this.lastText,
19309             icon : 'fa fa-forward',
19310             cls: "next",
19311             disabled: true,
19312             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19313         });
19314     //this.addSeparator();
19315         this.loading = this.navgroup.addItem({
19316             tooltip: this.refreshText,
19317             icon: 'fa fa-refresh',
19318             
19319             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19320         });
19321
19322     },
19323
19324     // private
19325     updateInfo : function(){
19326         if(this.displayEl){
19327             var count = this.ds.getCount();
19328             var msg = count == 0 ?
19329                 this.emptyMsg :
19330                 String.format(
19331                     this.displayMsg,
19332                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19333                 );
19334             this.displayEl.update(msg);
19335         }
19336     },
19337
19338     // private
19339     onLoad : function(ds, r, o){
19340        this.cursor = o.params ? o.params.start : 0;
19341        var d = this.getPageData(),
19342             ap = d.activePage,
19343             ps = d.pages;
19344         
19345        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19346        this.field.dom.value = ap;
19347        this.first.setDisabled(ap == 1);
19348        this.prev.setDisabled(ap == 1);
19349        this.next.setDisabled(ap == ps);
19350        this.last.setDisabled(ap == ps);
19351        this.loading.enable();
19352        this.updateInfo();
19353     },
19354
19355     // private
19356     getPageData : function(){
19357         var total = this.ds.getTotalCount();
19358         return {
19359             total : total,
19360             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19361             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19362         };
19363     },
19364
19365     // private
19366     onLoadError : function(){
19367         this.loading.enable();
19368     },
19369
19370     // private
19371     onPagingKeydown : function(e){
19372         var k = e.getKey();
19373         var d = this.getPageData();
19374         if(k == e.RETURN){
19375             var v = this.field.dom.value, pageNum;
19376             if(!v || isNaN(pageNum = parseInt(v, 10))){
19377                 this.field.dom.value = d.activePage;
19378                 return;
19379             }
19380             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19381             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19382             e.stopEvent();
19383         }
19384         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))
19385         {
19386           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19387           this.field.dom.value = pageNum;
19388           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19389           e.stopEvent();
19390         }
19391         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19392         {
19393           var v = this.field.dom.value, pageNum; 
19394           var increment = (e.shiftKey) ? 10 : 1;
19395           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19396             increment *= -1;
19397           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19398             this.field.dom.value = d.activePage;
19399             return;
19400           }
19401           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19402           {
19403             this.field.dom.value = parseInt(v, 10) + increment;
19404             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19405             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19406           }
19407           e.stopEvent();
19408         }
19409     },
19410
19411     // private
19412     beforeLoad : function(){
19413         if(this.loading){
19414             this.loading.disable();
19415         }
19416     },
19417
19418     // private
19419     onClick : function(which){
19420         var ds = this.ds;
19421         if (!ds) {
19422             return;
19423         }
19424         switch(which){
19425             case "first":
19426                 ds.load({params:{start: 0, limit: this.pageSize}});
19427             break;
19428             case "prev":
19429                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19430             break;
19431             case "next":
19432                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19433             break;
19434             case "last":
19435                 var total = ds.getTotalCount();
19436                 var extra = total % this.pageSize;
19437                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19438                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19439             break;
19440             case "refresh":
19441                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19442             break;
19443         }
19444     },
19445
19446     /**
19447      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19448      * @param {Roo.data.Store} store The data store to unbind
19449      */
19450     unbind : function(ds){
19451         ds.un("beforeload", this.beforeLoad, this);
19452         ds.un("load", this.onLoad, this);
19453         ds.un("loadexception", this.onLoadError, this);
19454         ds.un("remove", this.updateInfo, this);
19455         ds.un("add", this.updateInfo, this);
19456         this.ds = undefined;
19457     },
19458
19459     /**
19460      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19461      * @param {Roo.data.Store} store The data store to bind
19462      */
19463     bind : function(ds){
19464         ds.on("beforeload", this.beforeLoad, this);
19465         ds.on("load", this.onLoad, this);
19466         ds.on("loadexception", this.onLoadError, this);
19467         ds.on("remove", this.updateInfo, this);
19468         ds.on("add", this.updateInfo, this);
19469         this.ds = ds;
19470     }
19471 });/*
19472  * - LGPL
19473  *
19474  * element
19475  * 
19476  */
19477
19478 /**
19479  * @class Roo.bootstrap.MessageBar
19480  * @extends Roo.bootstrap.Component
19481  * Bootstrap MessageBar class
19482  * @cfg {String} html contents of the MessageBar
19483  * @cfg {String} weight (info | success | warning | danger) default info
19484  * @cfg {String} beforeClass insert the bar before the given class
19485  * @cfg {Boolean} closable (true | false) default false
19486  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19487  * 
19488  * @constructor
19489  * Create a new Element
19490  * @param {Object} config The config object
19491  */
19492
19493 Roo.bootstrap.MessageBar = function(config){
19494     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19495 };
19496
19497 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19498     
19499     html: '',
19500     weight: 'info',
19501     closable: false,
19502     fixed: false,
19503     beforeClass: 'bootstrap-sticky-wrap',
19504     
19505     getAutoCreate : function(){
19506         
19507         var cfg = {
19508             tag: 'div',
19509             cls: 'alert alert-dismissable alert-' + this.weight,
19510             cn: [
19511                 {
19512                     tag: 'span',
19513                     cls: 'message',
19514                     html: this.html || ''
19515                 }
19516             ]
19517         }
19518         
19519         if(this.fixed){
19520             cfg.cls += ' alert-messages-fixed';
19521         }
19522         
19523         if(this.closable){
19524             cfg.cn.push({
19525                 tag: 'button',
19526                 cls: 'close',
19527                 html: 'x'
19528             });
19529         }
19530         
19531         return cfg;
19532     },
19533     
19534     onRender : function(ct, position)
19535     {
19536         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19537         
19538         if(!this.el){
19539             var cfg = Roo.apply({},  this.getAutoCreate());
19540             cfg.id = Roo.id();
19541             
19542             if (this.cls) {
19543                 cfg.cls += ' ' + this.cls;
19544             }
19545             if (this.style) {
19546                 cfg.style = this.style;
19547             }
19548             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19549             
19550             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19551         }
19552         
19553         this.el.select('>button.close').on('click', this.hide, this);
19554         
19555     },
19556     
19557     show : function()
19558     {
19559         if (!this.rendered) {
19560             this.render();
19561         }
19562         
19563         this.el.show();
19564         
19565         this.fireEvent('show', this);
19566         
19567     },
19568     
19569     hide : function()
19570     {
19571         if (!this.rendered) {
19572             this.render();
19573         }
19574         
19575         this.el.hide();
19576         
19577         this.fireEvent('hide', this);
19578     },
19579     
19580     update : function()
19581     {
19582 //        var e = this.el.dom.firstChild;
19583 //        
19584 //        if(this.closable){
19585 //            e = e.nextSibling;
19586 //        }
19587 //        
19588 //        e.data = this.html || '';
19589
19590         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19591     }
19592    
19593 });
19594
19595  
19596
19597      /*
19598  * - LGPL
19599  *
19600  * Graph
19601  * 
19602  */
19603
19604
19605 /**
19606  * @class Roo.bootstrap.Graph
19607  * @extends Roo.bootstrap.Component
19608  * Bootstrap Graph class
19609 > Prameters
19610  -sm {number} sm 4
19611  -md {number} md 5
19612  @cfg {String} graphtype  bar | vbar | pie
19613  @cfg {number} g_x coodinator | centre x (pie)
19614  @cfg {number} g_y coodinator | centre y (pie)
19615  @cfg {number} g_r radius (pie)
19616  @cfg {number} g_height height of the chart (respected by all elements in the set)
19617  @cfg {number} g_width width of the chart (respected by all elements in the set)
19618  @cfg {Object} title The title of the chart
19619     
19620  -{Array}  values
19621  -opts (object) options for the chart 
19622      o {
19623      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19624      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19625      o vgutter (number)
19626      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.
19627      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19628      o to
19629      o stretch (boolean)
19630      o }
19631  -opts (object) options for the pie
19632      o{
19633      o cut
19634      o startAngle (number)
19635      o endAngle (number)
19636      } 
19637  *
19638  * @constructor
19639  * Create a new Input
19640  * @param {Object} config The config object
19641  */
19642
19643 Roo.bootstrap.Graph = function(config){
19644     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19645     
19646     this.addEvents({
19647         // img events
19648         /**
19649          * @event click
19650          * The img click event for the img.
19651          * @param {Roo.EventObject} e
19652          */
19653         "click" : true
19654     });
19655 };
19656
19657 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19658     
19659     sm: 4,
19660     md: 5,
19661     graphtype: 'bar',
19662     g_height: 250,
19663     g_width: 400,
19664     g_x: 50,
19665     g_y: 50,
19666     g_r: 30,
19667     opts:{
19668         //g_colors: this.colors,
19669         g_type: 'soft',
19670         g_gutter: '20%'
19671
19672     },
19673     title : false,
19674
19675     getAutoCreate : function(){
19676         
19677         var cfg = {
19678             tag: 'div',
19679             html : null
19680         }
19681         
19682         
19683         return  cfg;
19684     },
19685
19686     onRender : function(ct,position){
19687         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19688         this.raphael = Raphael(this.el.dom);
19689         
19690                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19691                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19692                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19693                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19694                 /*
19695                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19696                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19697                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19698                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19699                 
19700                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19701                 r.barchart(330, 10, 300, 220, data1);
19702                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19703                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19704                 */
19705                 
19706                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19707                 // r.barchart(30, 30, 560, 250,  xdata, {
19708                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19709                 //     axis : "0 0 1 1",
19710                 //     axisxlabels :  xdata
19711                 //     //yvalues : cols,
19712                    
19713                 // });
19714 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19715 //        
19716 //        this.load(null,xdata,{
19717 //                axis : "0 0 1 1",
19718 //                axisxlabels :  xdata
19719 //                });
19720
19721     },
19722
19723     load : function(graphtype,xdata,opts){
19724         this.raphael.clear();
19725         if(!graphtype) {
19726             graphtype = this.graphtype;
19727         }
19728         if(!opts){
19729             opts = this.opts;
19730         }
19731         var r = this.raphael,
19732             fin = function () {
19733                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19734             },
19735             fout = function () {
19736                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19737             },
19738             pfin = function() {
19739                 this.sector.stop();
19740                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19741
19742                 if (this.label) {
19743                     this.label[0].stop();
19744                     this.label[0].attr({ r: 7.5 });
19745                     this.label[1].attr({ "font-weight": 800 });
19746                 }
19747             },
19748             pfout = function() {
19749                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19750
19751                 if (this.label) {
19752                     this.label[0].animate({ r: 5 }, 500, "bounce");
19753                     this.label[1].attr({ "font-weight": 400 });
19754                 }
19755             };
19756
19757         switch(graphtype){
19758             case 'bar':
19759                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19760                 break;
19761             case 'hbar':
19762                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19763                 break;
19764             case 'pie':
19765 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19766 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19767 //            
19768                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19769                 
19770                 break;
19771
19772         }
19773         
19774         if(this.title){
19775             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19776         }
19777         
19778     },
19779     
19780     setTitle: function(o)
19781     {
19782         this.title = o;
19783     },
19784     
19785     initEvents: function() {
19786         
19787         if(!this.href){
19788             this.el.on('click', this.onClick, this);
19789         }
19790     },
19791     
19792     onClick : function(e)
19793     {
19794         Roo.log('img onclick');
19795         this.fireEvent('click', this, e);
19796     }
19797    
19798 });
19799
19800  
19801 /*
19802  * - LGPL
19803  *
19804  * numberBox
19805  * 
19806  */
19807 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19808
19809 /**
19810  * @class Roo.bootstrap.dash.NumberBox
19811  * @extends Roo.bootstrap.Component
19812  * Bootstrap NumberBox class
19813  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19814  * @cfg {String} headline Box headline
19815  * @cfg {String} content Box content
19816  * @cfg {String} icon Box icon
19817  * @cfg {String} footer Footer text
19818  * @cfg {String} fhref Footer href
19819  * 
19820  * @constructor
19821  * Create a new NumberBox
19822  * @param {Object} config The config object
19823  */
19824
19825
19826 Roo.bootstrap.dash.NumberBox = function(config){
19827     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19828     
19829 };
19830
19831 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19832     
19833     bgcolor : 'aqua',
19834     headline : '',
19835     content : '',
19836     icon : '',
19837     footer : '',
19838     fhref : '',
19839     ficon : '',
19840     
19841     getAutoCreate : function(){
19842         
19843         var cfg = {
19844             tag : 'div',
19845             cls : 'small-box bg-' + this.bgcolor,
19846             cn : [
19847                 {
19848                     tag : 'div',
19849                     cls : 'inner',
19850                     cn :[
19851                         {
19852                             tag : 'h3',
19853                             cls : 'roo-headline',
19854                             html : this.headline
19855                         },
19856                         {
19857                             tag : 'p',
19858                             cls : 'roo-content',
19859                             html : this.content
19860                         }
19861                     ]
19862                 }
19863             ]
19864         }
19865         
19866         if(this.icon){
19867             cfg.cn.push({
19868                 tag : 'div',
19869                 cls : 'icon',
19870                 cn :[
19871                     {
19872                         tag : 'i',
19873                         cls : 'ion ' + this.icon
19874                     }
19875                 ]
19876             });
19877         }
19878         
19879         if(this.footer){
19880             var footer = {
19881                 tag : 'a',
19882                 cls : 'small-box-footer',
19883                 href : this.fhref || '#',
19884                 html : this.footer
19885             };
19886             
19887             cfg.cn.push(footer);
19888             
19889         }
19890         
19891         return  cfg;
19892     },
19893
19894     onRender : function(ct,position){
19895         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19896
19897
19898        
19899                 
19900     },
19901
19902     setHeadline: function (value)
19903     {
19904         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19905     },
19906     
19907     setFooter: function (value, href)
19908     {
19909         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19910         
19911         if(href){
19912             this.el.select('a.small-box-footer',true).first().attr('href', href);
19913         }
19914         
19915     },
19916
19917     setContent: function (value)
19918     {
19919         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19920     },
19921
19922     initEvents: function() 
19923     {   
19924         
19925     }
19926     
19927 });
19928
19929  
19930 /*
19931  * - LGPL
19932  *
19933  * TabBox
19934  * 
19935  */
19936 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19937
19938 /**
19939  * @class Roo.bootstrap.dash.TabBox
19940  * @extends Roo.bootstrap.Component
19941  * Bootstrap TabBox class
19942  * @cfg {String} title Title of the TabBox
19943  * @cfg {String} icon Icon of the TabBox
19944  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19945  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19946  * 
19947  * @constructor
19948  * Create a new TabBox
19949  * @param {Object} config The config object
19950  */
19951
19952
19953 Roo.bootstrap.dash.TabBox = function(config){
19954     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19955     this.addEvents({
19956         // raw events
19957         /**
19958          * @event addpane
19959          * When a pane is added
19960          * @param {Roo.bootstrap.dash.TabPane} pane
19961          */
19962         "addpane" : true,
19963         /**
19964          * @event activatepane
19965          * When a pane is activated
19966          * @param {Roo.bootstrap.dash.TabPane} pane
19967          */
19968         "activatepane" : true
19969         
19970          
19971     });
19972     
19973     this.panes = [];
19974 };
19975
19976 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19977
19978     title : '',
19979     icon : false,
19980     showtabs : true,
19981     tabScrollable : false,
19982     
19983     getChildContainer : function()
19984     {
19985         return this.el.select('.tab-content', true).first();
19986     },
19987     
19988     getAutoCreate : function(){
19989         
19990         var header = {
19991             tag: 'li',
19992             cls: 'pull-left header',
19993             html: this.title,
19994             cn : []
19995         };
19996         
19997         if(this.icon){
19998             header.cn.push({
19999                 tag: 'i',
20000                 cls: 'fa ' + this.icon
20001             });
20002         }
20003         
20004         var h = {
20005             tag: 'ul',
20006             cls: 'nav nav-tabs pull-right',
20007             cn: [
20008                 header
20009             ]
20010         };
20011         
20012         if(this.tabScrollable){
20013             h = {
20014                 tag: 'div',
20015                 cls: 'tab-header',
20016                 cn: [
20017                     {
20018                         tag: 'ul',
20019                         cls: 'nav nav-tabs pull-right',
20020                         cn: [
20021                             header
20022                         ]
20023                     }
20024                 ]
20025             }
20026         }
20027         
20028         var cfg = {
20029             tag: 'div',
20030             cls: 'nav-tabs-custom',
20031             cn: [
20032                 h,
20033                 {
20034                     tag: 'div',
20035                     cls: 'tab-content no-padding',
20036                     cn: []
20037                 }
20038             ]
20039         }
20040
20041         return  cfg;
20042     },
20043     initEvents : function()
20044     {
20045         //Roo.log('add add pane handler');
20046         this.on('addpane', this.onAddPane, this);
20047     },
20048      /**
20049      * Updates the box title
20050      * @param {String} html to set the title to.
20051      */
20052     setTitle : function(value)
20053     {
20054         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20055     },
20056     onAddPane : function(pane)
20057     {
20058         this.panes.push(pane);
20059         //Roo.log('addpane');
20060         //Roo.log(pane);
20061         // tabs are rendere left to right..
20062         if(!this.showtabs){
20063             return;
20064         }
20065         
20066         var ctr = this.el.select('.nav-tabs', true).first();
20067          
20068          
20069         var existing = ctr.select('.nav-tab',true);
20070         var qty = existing.getCount();;
20071         
20072         
20073         var tab = ctr.createChild({
20074             tag : 'li',
20075             cls : 'nav-tab' + (qty ? '' : ' active'),
20076             cn : [
20077                 {
20078                     tag : 'a',
20079                     href:'#',
20080                     html : pane.title
20081                 }
20082             ]
20083         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20084         pane.tab = tab;
20085         
20086         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20087         if (!qty) {
20088             pane.el.addClass('active');
20089         }
20090         
20091                 
20092     },
20093     onTabClick : function(ev,un,ob,pane)
20094     {
20095         //Roo.log('tab - prev default');
20096         ev.preventDefault();
20097         
20098         
20099         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20100         pane.tab.addClass('active');
20101         //Roo.log(pane.title);
20102         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20103         // technically we should have a deactivate event.. but maybe add later.
20104         // and it should not de-activate the selected tab...
20105         this.fireEvent('activatepane', pane);
20106         pane.el.addClass('active');
20107         pane.fireEvent('activate');
20108         
20109         
20110     },
20111     
20112     getActivePane : function()
20113     {
20114         var r = false;
20115         Roo.each(this.panes, function(p) {
20116             if(p.el.hasClass('active')){
20117                 r = p;
20118                 return false;
20119             }
20120             
20121             return;
20122         });
20123         
20124         return r;
20125     }
20126     
20127     
20128 });
20129
20130  
20131 /*
20132  * - LGPL
20133  *
20134  * Tab pane
20135  * 
20136  */
20137 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20138 /**
20139  * @class Roo.bootstrap.TabPane
20140  * @extends Roo.bootstrap.Component
20141  * Bootstrap TabPane class
20142  * @cfg {Boolean} active (false | true) Default false
20143  * @cfg {String} title title of panel
20144
20145  * 
20146  * @constructor
20147  * Create a new TabPane
20148  * @param {Object} config The config object
20149  */
20150
20151 Roo.bootstrap.dash.TabPane = function(config){
20152     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20153     
20154     this.addEvents({
20155         // raw events
20156         /**
20157          * @event activate
20158          * When a pane is activated
20159          * @param {Roo.bootstrap.dash.TabPane} pane
20160          */
20161         "activate" : true
20162          
20163     });
20164 };
20165
20166 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20167     
20168     active : false,
20169     title : '',
20170     
20171     // the tabBox that this is attached to.
20172     tab : false,
20173      
20174     getAutoCreate : function() 
20175     {
20176         var cfg = {
20177             tag: 'div',
20178             cls: 'tab-pane'
20179         }
20180         
20181         if(this.active){
20182             cfg.cls += ' active';
20183         }
20184         
20185         return cfg;
20186     },
20187     initEvents  : function()
20188     {
20189         //Roo.log('trigger add pane handler');
20190         this.parent().fireEvent('addpane', this)
20191     },
20192     
20193      /**
20194      * Updates the tab title 
20195      * @param {String} html to set the title to.
20196      */
20197     setTitle: function(str)
20198     {
20199         if (!this.tab) {
20200             return;
20201         }
20202         this.title = str;
20203         this.tab.select('a', true).first().dom.innerHTML = str;
20204         
20205     }
20206     
20207     
20208     
20209 });
20210
20211  
20212
20213
20214  /*
20215  * - LGPL
20216  *
20217  * menu
20218  * 
20219  */
20220 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20221
20222 /**
20223  * @class Roo.bootstrap.menu.Menu
20224  * @extends Roo.bootstrap.Component
20225  * Bootstrap Menu class - container for Menu
20226  * @cfg {String} html Text of the menu
20227  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20228  * @cfg {String} icon Font awesome icon
20229  * @cfg {String} pos Menu align to (top | bottom) default bottom
20230  * 
20231  * 
20232  * @constructor
20233  * Create a new Menu
20234  * @param {Object} config The config object
20235  */
20236
20237
20238 Roo.bootstrap.menu.Menu = function(config){
20239     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20240     
20241     this.addEvents({
20242         /**
20243          * @event beforeshow
20244          * Fires before this menu is displayed
20245          * @param {Roo.bootstrap.menu.Menu} this
20246          */
20247         beforeshow : true,
20248         /**
20249          * @event beforehide
20250          * Fires before this menu is hidden
20251          * @param {Roo.bootstrap.menu.Menu} this
20252          */
20253         beforehide : true,
20254         /**
20255          * @event show
20256          * Fires after this menu is displayed
20257          * @param {Roo.bootstrap.menu.Menu} this
20258          */
20259         show : true,
20260         /**
20261          * @event hide
20262          * Fires after this menu is hidden
20263          * @param {Roo.bootstrap.menu.Menu} this
20264          */
20265         hide : true,
20266         /**
20267          * @event click
20268          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20269          * @param {Roo.bootstrap.menu.Menu} this
20270          * @param {Roo.EventObject} e
20271          */
20272         click : true
20273     });
20274     
20275 };
20276
20277 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20278     
20279     submenu : false,
20280     html : '',
20281     weight : 'default',
20282     icon : false,
20283     pos : 'bottom',
20284     
20285     
20286     getChildContainer : function() {
20287         if(this.isSubMenu){
20288             return this.el;
20289         }
20290         
20291         return this.el.select('ul.dropdown-menu', true).first();  
20292     },
20293     
20294     getAutoCreate : function()
20295     {
20296         var text = [
20297             {
20298                 tag : 'span',
20299                 cls : 'roo-menu-text',
20300                 html : this.html
20301             }
20302         ];
20303         
20304         if(this.icon){
20305             text.unshift({
20306                 tag : 'i',
20307                 cls : 'fa ' + this.icon
20308             })
20309         }
20310         
20311         
20312         var cfg = {
20313             tag : 'div',
20314             cls : 'btn-group',
20315             cn : [
20316                 {
20317                     tag : 'button',
20318                     cls : 'dropdown-button btn btn-' + this.weight,
20319                     cn : text
20320                 },
20321                 {
20322                     tag : 'button',
20323                     cls : 'dropdown-toggle btn btn-' + this.weight,
20324                     cn : [
20325                         {
20326                             tag : 'span',
20327                             cls : 'caret'
20328                         }
20329                     ]
20330                 },
20331                 {
20332                     tag : 'ul',
20333                     cls : 'dropdown-menu'
20334                 }
20335             ]
20336             
20337         };
20338         
20339         if(this.pos == 'top'){
20340             cfg.cls += ' dropup';
20341         }
20342         
20343         if(this.isSubMenu){
20344             cfg = {
20345                 tag : 'ul',
20346                 cls : 'dropdown-menu'
20347             }
20348         }
20349         
20350         return cfg;
20351     },
20352     
20353     onRender : function(ct, position)
20354     {
20355         this.isSubMenu = ct.hasClass('dropdown-submenu');
20356         
20357         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20358     },
20359     
20360     initEvents : function() 
20361     {
20362         if(this.isSubMenu){
20363             return;
20364         }
20365         
20366         this.hidden = true;
20367         
20368         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20369         this.triggerEl.on('click', this.onTriggerPress, this);
20370         
20371         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20372         this.buttonEl.on('click', this.onClick, this);
20373         
20374     },
20375     
20376     list : function()
20377     {
20378         if(this.isSubMenu){
20379             return this.el;
20380         }
20381         
20382         return this.el.select('ul.dropdown-menu', true).first();
20383     },
20384     
20385     onClick : function(e)
20386     {
20387         this.fireEvent("click", this, e);
20388     },
20389     
20390     onTriggerPress  : function(e)
20391     {   
20392         if (this.isVisible()) {
20393             this.hide();
20394         } else {
20395             this.show();
20396         }
20397     },
20398     
20399     isVisible : function(){
20400         return !this.hidden;
20401     },
20402     
20403     show : function()
20404     {
20405         this.fireEvent("beforeshow", this);
20406         
20407         this.hidden = false;
20408         this.el.addClass('open');
20409         
20410         Roo.get(document).on("mouseup", this.onMouseUp, this);
20411         
20412         this.fireEvent("show", this);
20413         
20414         
20415     },
20416     
20417     hide : function()
20418     {
20419         this.fireEvent("beforehide", this);
20420         
20421         this.hidden = true;
20422         this.el.removeClass('open');
20423         
20424         Roo.get(document).un("mouseup", this.onMouseUp);
20425         
20426         this.fireEvent("hide", this);
20427     },
20428     
20429     onMouseUp : function()
20430     {
20431         this.hide();
20432     }
20433     
20434 });
20435
20436  
20437  /*
20438  * - LGPL
20439  *
20440  * menu item
20441  * 
20442  */
20443 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20444
20445 /**
20446  * @class Roo.bootstrap.menu.Item
20447  * @extends Roo.bootstrap.Component
20448  * Bootstrap MenuItem class
20449  * @cfg {Boolean} submenu (true | false) default false
20450  * @cfg {String} html text of the item
20451  * @cfg {String} href the link
20452  * @cfg {Boolean} disable (true | false) default false
20453  * @cfg {Boolean} preventDefault (true | false) default true
20454  * @cfg {String} icon Font awesome icon
20455  * @cfg {String} pos Submenu align to (left | right) default right 
20456  * 
20457  * 
20458  * @constructor
20459  * Create a new Item
20460  * @param {Object} config The config object
20461  */
20462
20463
20464 Roo.bootstrap.menu.Item = function(config){
20465     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20466     this.addEvents({
20467         /**
20468          * @event mouseover
20469          * Fires when the mouse is hovering over this menu
20470          * @param {Roo.bootstrap.menu.Item} this
20471          * @param {Roo.EventObject} e
20472          */
20473         mouseover : true,
20474         /**
20475          * @event mouseout
20476          * Fires when the mouse exits this menu
20477          * @param {Roo.bootstrap.menu.Item} this
20478          * @param {Roo.EventObject} e
20479          */
20480         mouseout : true,
20481         // raw events
20482         /**
20483          * @event click
20484          * The raw click event for the entire grid.
20485          * @param {Roo.EventObject} e
20486          */
20487         click : true
20488     });
20489 };
20490
20491 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20492     
20493     submenu : false,
20494     href : '',
20495     html : '',
20496     preventDefault: true,
20497     disable : false,
20498     icon : false,
20499     pos : 'right',
20500     
20501     getAutoCreate : function()
20502     {
20503         var text = [
20504             {
20505                 tag : 'span',
20506                 cls : 'roo-menu-item-text',
20507                 html : this.html
20508             }
20509         ];
20510         
20511         if(this.icon){
20512             text.unshift({
20513                 tag : 'i',
20514                 cls : 'fa ' + this.icon
20515             })
20516         }
20517         
20518         var cfg = {
20519             tag : 'li',
20520             cn : [
20521                 {
20522                     tag : 'a',
20523                     href : this.href || '#',
20524                     cn : text
20525                 }
20526             ]
20527         };
20528         
20529         if(this.disable){
20530             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20531         }
20532         
20533         if(this.submenu){
20534             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20535             
20536             if(this.pos == 'left'){
20537                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20538             }
20539         }
20540         
20541         return cfg;
20542     },
20543     
20544     initEvents : function() 
20545     {
20546         this.el.on('mouseover', this.onMouseOver, this);
20547         this.el.on('mouseout', this.onMouseOut, this);
20548         
20549         this.el.select('a', true).first().on('click', this.onClick, this);
20550         
20551     },
20552     
20553     onClick : function(e)
20554     {
20555         if(this.preventDefault){
20556             e.preventDefault();
20557         }
20558         
20559         this.fireEvent("click", this, e);
20560     },
20561     
20562     onMouseOver : function(e)
20563     {
20564         if(this.submenu && this.pos == 'left'){
20565             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20566         }
20567         
20568         this.fireEvent("mouseover", this, e);
20569     },
20570     
20571     onMouseOut : function(e)
20572     {
20573         this.fireEvent("mouseout", this, e);
20574     }
20575 });
20576
20577  
20578
20579  /*
20580  * - LGPL
20581  *
20582  * menu separator
20583  * 
20584  */
20585 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20586
20587 /**
20588  * @class Roo.bootstrap.menu.Separator
20589  * @extends Roo.bootstrap.Component
20590  * Bootstrap Separator class
20591  * 
20592  * @constructor
20593  * Create a new Separator
20594  * @param {Object} config The config object
20595  */
20596
20597
20598 Roo.bootstrap.menu.Separator = function(config){
20599     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20600 };
20601
20602 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20603     
20604     getAutoCreate : function(){
20605         var cfg = {
20606             tag : 'li',
20607             cls: 'divider'
20608         };
20609         
20610         return cfg;
20611     }
20612    
20613 });
20614
20615  
20616
20617  /*
20618  * - LGPL
20619  *
20620  * Tooltip
20621  * 
20622  */
20623
20624 /**
20625  * @class Roo.bootstrap.Tooltip
20626  * Bootstrap Tooltip class
20627  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20628  * to determine which dom element triggers the tooltip.
20629  * 
20630  * It needs to add support for additional attributes like tooltip-position
20631  * 
20632  * @constructor
20633  * Create a new Toolti
20634  * @param {Object} config The config object
20635  */
20636
20637 Roo.bootstrap.Tooltip = function(config){
20638     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20639 };
20640
20641 Roo.apply(Roo.bootstrap.Tooltip, {
20642     /**
20643      * @function init initialize tooltip monitoring.
20644      * @static
20645      */
20646     currentEl : false,
20647     currentTip : false,
20648     currentRegion : false,
20649     
20650     //  init : delay?
20651     
20652     init : function()
20653     {
20654         Roo.get(document).on('mouseover', this.enter ,this);
20655         Roo.get(document).on('mouseout', this.leave, this);
20656          
20657         
20658         this.currentTip = new Roo.bootstrap.Tooltip();
20659     },
20660     
20661     enter : function(ev)
20662     {
20663         var dom = ev.getTarget();
20664         //Roo.log(['enter',dom]);
20665         var el = Roo.fly(dom);
20666         if (this.currentEl) {
20667             //Roo.log(dom);
20668             //Roo.log(this.currentEl);
20669             //Roo.log(this.currentEl.contains(dom));
20670             if (this.currentEl == el) {
20671                 return;
20672             }
20673             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20674                 return;
20675             }
20676
20677         }
20678         
20679         
20680         
20681         if (this.currentTip.el) {
20682             this.currentTip.el.hide(); // force hiding...
20683         }    
20684         //Roo.log(el);
20685         if (!el.attr('tooltip')) { // parents who have tip?
20686             return;
20687         }
20688         this.currentEl = el;
20689         this.currentTip.bind(el);
20690         this.currentRegion = Roo.lib.Region.getRegion(dom);
20691         this.currentTip.enter();
20692         
20693     },
20694     leave : function(ev)
20695     {
20696         var dom = ev.getTarget();
20697         //Roo.log(['leave',dom]);
20698         if (!this.currentEl) {
20699             return;
20700         }
20701         
20702         
20703         if (dom != this.currentEl.dom) {
20704             return;
20705         }
20706         var xy = ev.getXY();
20707         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
20708             return;
20709         }
20710         // only activate leave if mouse cursor is outside... bounding box..
20711         
20712         
20713         
20714         
20715         if (this.currentTip) {
20716             this.currentTip.leave();
20717         }
20718         //Roo.log('clear currentEl');
20719         this.currentEl = false;
20720         
20721         
20722     },
20723     alignment : {
20724         'left' : ['r-l', [-2,0], 'right'],
20725         'right' : ['l-r', [2,0], 'left'],
20726         'bottom' : ['t-b', [0,2], 'top'],
20727         'top' : [ 'b-t', [0,-2], 'bottom']
20728     }
20729     
20730 });
20731
20732
20733 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
20734     
20735     
20736     bindEl : false,
20737     
20738     delay : null, // can be { show : 300 , hide: 500}
20739     
20740     timeout : null,
20741     
20742     hoverState : null, //???
20743     
20744     placement : 'bottom', 
20745     
20746     getAutoCreate : function(){
20747     
20748         var cfg = {
20749            cls : 'tooltip',
20750            role : 'tooltip',
20751            cn : [
20752                 {
20753                     cls : 'tooltip-arrow'
20754                 },
20755                 {
20756                     cls : 'tooltip-inner'
20757                 }
20758            ]
20759         };
20760         
20761         return cfg;
20762     },
20763     bind : function(el)
20764     {
20765         this.bindEl = el;
20766     },
20767       
20768     
20769     enter : function () {
20770        
20771         if (this.timeout != null) {
20772             clearTimeout(this.timeout);
20773         }
20774         
20775         this.hoverState = 'in'
20776          //Roo.log("enter - show");
20777         if (!this.delay || !this.delay.show) {
20778             this.show();
20779             return;
20780         }
20781         var _t = this;
20782         this.timeout = setTimeout(function () {
20783             if (_t.hoverState == 'in') {
20784                 _t.show();
20785             }
20786         }, this.delay.show);
20787     },
20788     leave : function()
20789     {
20790         clearTimeout(this.timeout);
20791     
20792         this.hoverState = 'out'
20793          if (!this.delay || !this.delay.hide) {
20794             this.hide();
20795             return 
20796         }
20797        
20798         var _t = this;
20799         this.timeout = setTimeout(function () {
20800             //Roo.log("leave - timeout");
20801             
20802             if (_t.hoverState == 'out') {
20803                 _t.hide();
20804                 Roo.bootstrap.Tooltip.currentEl = false;
20805             }
20806         }, delay)
20807     },
20808     
20809     show : function ()
20810     {
20811         if (!this.el) {
20812             this.render(document.body);
20813         }
20814         // set content.
20815         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
20816         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
20817         
20818         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
20819         
20820         var placement = typeof this.placement == 'function' ?
20821             this.placement.call(this, this.el, on_el) :
20822             this.placement;
20823             
20824         var autoToken = /\s?auto?\s?/i;
20825         var autoPlace = autoToken.test(placement);
20826         if (autoPlace) {
20827             placement = placement.replace(autoToken, '') || 'top';
20828         }
20829         
20830         //this.el.detach()
20831         //this.el.setXY([0,0]);
20832         this.el.show();
20833         //this.el.dom.style.display='block';
20834         this.el.addClass(placement);
20835         
20836         //this.el.appendTo(on_el);
20837         
20838         var p = this.getPosition();
20839         var box = this.el.getBox();
20840         
20841         if (autoPlace) {
20842             // fixme..
20843         }
20844         var align = Roo.bootstrap.Tooltip.alignment[placement]
20845         this.el.alignTo(this.bindEl, align[0],align[1]);
20846         //var arrow = this.el.select('.arrow',true).first();
20847         //arrow.set(align[2], 
20848         
20849         this.el.addClass('in fade');
20850         this.hoverState = null;
20851         
20852         if (this.el.hasClass('fade')) {
20853             // fade it?
20854         }
20855         
20856     },
20857     hide : function()
20858     {
20859          
20860         if (!this.el) {
20861             return;
20862         }
20863         //this.el.setXY([0,0]);
20864         this.el.removeClass('in');
20865         //this.el.hide();
20866         
20867     }
20868     
20869 });
20870  
20871
20872