e5f98aa6215e91363962062b11ff3841e24e06d9
[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 (  primary | success | info | warning | danger | link ) default 
448  * @cfg {String} size ( lg | sm | xs)
449  * @cfg {String} tag ( a | input | submit)
450  * @cfg {String} href empty or href
451  * @cfg {Boolean} disabled default false;
452  * @cfg {Boolean} isClose default false;
453  * @cfg {String} glyphicon (| 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 
456  * @cfg {Boolean} inverse 
457  * @cfg {Boolean} toggle 
458  * @cfg {String} ontext text for on toggle state
459  * @cfg {String} offtext text for off toggle state
460  * @cfg {Boolean} defaulton 
461  * @cfg {Boolean} preventDefault  default true
462  * @cfg {Boolean} removeClass remove the standard class..
463  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
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 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 default true
2065  * @cfg {Boolean} allow_close default true
2066  * 
2067  * @constructor
2068  * Create a new Modal Dialog
2069  * @param {Object} config The config object
2070  */
2071
2072 Roo.bootstrap.Modal = function(config){
2073     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2074     this.addEvents({
2075         // raw events
2076         /**
2077          * @event btnclick
2078          * The raw btnclick event for the button
2079          * @param {Roo.EventObject} e
2080          */
2081         "btnclick" : true
2082     });
2083     this.buttons = this.buttons || [];
2084 };
2085
2086 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2087     
2088     title : 'test dialog',
2089    
2090     buttons : false,
2091     
2092     // set on load...
2093     body:  false,
2094     
2095     specificTitle: false,
2096     
2097     buttonPosition: 'right',
2098     
2099     allow_close : true,
2100     
2101     animate : true,
2102     
2103     onRender : function(ct, position)
2104     {
2105         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2106      
2107         if(!this.el){
2108             var cfg = Roo.apply({},  this.getAutoCreate());
2109             cfg.id = Roo.id();
2110             //if(!cfg.name){
2111             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2112             //}
2113             //if (!cfg.name.length) {
2114             //    delete cfg.name;
2115            // }
2116             if (this.cls) {
2117                 cfg.cls += ' ' + this.cls;
2118             }
2119             if (this.style) {
2120                 cfg.style = this.style;
2121             }
2122             this.el = Roo.get(document.body).createChild(cfg, position);
2123         }
2124         //var type = this.el.dom.type;
2125         
2126         if(this.tabIndex !== undefined){
2127             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2128         }
2129         
2130         
2131         
2132         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2133         this.maskEl.enableDisplayMode("block");
2134         this.maskEl.hide();
2135         //this.el.addClass("x-dlg-modal");
2136     
2137         if (this.buttons.length) {
2138             Roo.each(this.buttons, function(bb) {
2139                 b = Roo.apply({}, bb);
2140                 b.xns = b.xns || Roo.bootstrap;
2141                 b.xtype = b.xtype || 'Button';
2142                 if (typeof(b.listeners) == 'undefined') {
2143                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2144                 }
2145                 
2146                 var btn = Roo.factory(b);
2147                 
2148                 btn.onRender(this.el.select('.modal-footer div').first());
2149                 
2150             },this);
2151         }
2152         // render the children.
2153         var nitems = [];
2154         
2155         if(typeof(this.items) != 'undefined'){
2156             var items = this.items;
2157             delete this.items;
2158
2159             for(var i =0;i < items.length;i++) {
2160                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2161             }
2162         }
2163         
2164         this.items = nitems;
2165         
2166         this.body = this.el.select('.modal-body',true).first();
2167         this.close = this.el.select('.modal-header .close', true).first();
2168         this.footer = this.el.select('.modal-footer',true).first();
2169         this.initEvents();
2170         //this.el.addClass([this.fieldClass, this.cls]);
2171         
2172     },
2173     getAutoCreate : function(){
2174         
2175         
2176         var bdy = {
2177                 cls : 'modal-body',
2178                 html : this.html || ''
2179         };
2180         
2181         var title = {
2182             tag: 'h4',
2183             cls : 'modal-title',
2184             html : this.title
2185         };
2186         
2187         if(this.specificTitle){
2188             title = this.title;
2189             
2190         };
2191         
2192         var header = [];
2193         if (this.allow_close) {
2194             header.push({
2195                 tag: 'button',
2196                 cls : 'close',
2197                 html : '&times'
2198             });
2199         }
2200         header.push(title);
2201         
2202         var modal = {
2203             cls: "modal",
2204             style : 'display: none',
2205             cn : [
2206                 {
2207                     cls: "modal-dialog",
2208                     cn : [
2209                         {
2210                             cls : "modal-content",
2211                             cn : [
2212                                 {
2213                                     cls : 'modal-header',
2214                                     cn : header
2215                                 },
2216                                 bdy,
2217                                 {
2218                                     cls : 'modal-footer',
2219                                     cn : [
2220                                         {
2221                                             tag: 'div',
2222                                             cls: 'btn-' + this.buttonPosition
2223                                         }
2224                                     ]
2225                                     
2226                                 }
2227                                 
2228                                 
2229                             ]
2230                             
2231                         }
2232                     ]
2233                         
2234                 }
2235             ]
2236         };
2237         
2238         if(this.animate){
2239             modal.cls += ' fade';
2240         }
2241         
2242         return modal;
2243           
2244     },
2245     getChildContainer : function() {
2246          
2247          return this.el.select('.modal-body',true).first();
2248         
2249     },
2250     getButtonContainer : function() {
2251          return this.el.select('.modal-footer div',true).first();
2252         
2253     },
2254     initEvents : function()
2255     {
2256         this.el.select('.modal-header .close').on('click', this.hide, this);
2257 //        
2258 //        this.addxtype(this);
2259     },
2260     show : function() {
2261         
2262         if (!this.rendered) {
2263             this.render();
2264         }
2265         
2266         this.el.setStyle('display', 'block');
2267         
2268         if(this.animate){
2269             var _this = this;
2270             (function(){ _this.el.addClass('in'); }).defer(50);
2271         }else{
2272             this.el.addClass('in');
2273         }
2274         
2275         Roo.get(document.body).addClass("x-body-masked");
2276         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2277         this.maskEl.show();
2278         this.el.setStyle('zIndex', '10001');
2279         this.fireEvent('show', this);
2280         
2281         
2282     },
2283     hide : function()
2284     {
2285         this.maskEl.hide();
2286         Roo.get(document.body).removeClass("x-body-masked");
2287         this.el.removeClass('in');
2288         
2289         if(this.animate){
2290             var _this = this;
2291             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2292         }else{
2293             this.el.setStyle('display', 'none');
2294         }
2295         
2296         this.fireEvent('hide', this);
2297     },
2298     
2299     addButton : function(str, cb)
2300     {
2301          
2302         
2303         var b = Roo.apply({}, { html : str } );
2304         b.xns = b.xns || Roo.bootstrap;
2305         b.xtype = b.xtype || 'Button';
2306         if (typeof(b.listeners) == 'undefined') {
2307             b.listeners = { click : cb.createDelegate(this)  };
2308         }
2309         
2310         var btn = Roo.factory(b);
2311            
2312         btn.onRender(this.el.select('.modal-footer div').first());
2313         
2314         return btn;   
2315        
2316     },
2317     
2318     setDefaultButton : function(btn)
2319     {
2320         //this.el.select('.modal-footer').()
2321     },
2322     resizeTo: function(w,h)
2323     {
2324         // skip..
2325     },
2326     setContentSize  : function(w, h)
2327     {
2328         
2329     },
2330     onButtonClick: function(btn,e)
2331     {
2332         //Roo.log([a,b,c]);
2333         this.fireEvent('btnclick', btn.name, e);
2334     },
2335     setTitle: function(str) {
2336         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2337         
2338     }
2339 });
2340
2341
2342 Roo.apply(Roo.bootstrap.Modal,  {
2343     /**
2344          * Button config that displays a single OK button
2345          * @type Object
2346          */
2347         OK :  [{
2348             name : 'ok',
2349             weight : 'primary',
2350             html : 'OK'
2351         }], 
2352         /**
2353          * Button config that displays Yes and No buttons
2354          * @type Object
2355          */
2356         YESNO : [
2357             {
2358                 name  : 'no',
2359                 html : 'No'
2360             },
2361             {
2362                 name  :'yes',
2363                 weight : 'primary',
2364                 html : 'Yes'
2365             }
2366         ],
2367         
2368         /**
2369          * Button config that displays OK and Cancel buttons
2370          * @type Object
2371          */
2372         OKCANCEL : [
2373             {
2374                name : 'cancel',
2375                 html : 'Cancel'
2376             },
2377             {
2378                 name : 'ok',
2379                 weight : 'primary',
2380                 html : 'OK'
2381             }
2382         ],
2383         /**
2384          * Button config that displays Yes, No and Cancel buttons
2385          * @type Object
2386          */
2387         YESNOCANCEL : [
2388             {
2389                 name : 'yes',
2390                 weight : 'primary',
2391                 html : 'Yes'
2392             },
2393             {
2394                 name : 'no',
2395                 html : 'No'
2396             },
2397             {
2398                 name : 'cancel',
2399                 html : 'Cancel'
2400             }
2401         ]
2402 });
2403  /*
2404  * - LGPL
2405  *
2406  * messagebox - can be used as a replace
2407  * 
2408  */
2409 /**
2410  * @class Roo.MessageBox
2411  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2412  * Example usage:
2413  *<pre><code>
2414 // Basic alert:
2415 Roo.Msg.alert('Status', 'Changes saved successfully.');
2416
2417 // Prompt for user data:
2418 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2419     if (btn == 'ok'){
2420         // process text value...
2421     }
2422 });
2423
2424 // Show a dialog using config options:
2425 Roo.Msg.show({
2426    title:'Save Changes?',
2427    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2428    buttons: Roo.Msg.YESNOCANCEL,
2429    fn: processResult,
2430    animEl: 'elId'
2431 });
2432 </code></pre>
2433  * @singleton
2434  */
2435 Roo.bootstrap.MessageBox = function(){
2436     var dlg, opt, mask, waitTimer;
2437     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2438     var buttons, activeTextEl, bwidth;
2439
2440     
2441     // private
2442     var handleButton = function(button){
2443         dlg.hide();
2444         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2445     };
2446
2447     // private
2448     var handleHide = function(){
2449         if(opt && opt.cls){
2450             dlg.el.removeClass(opt.cls);
2451         }
2452         //if(waitTimer){
2453         //    Roo.TaskMgr.stop(waitTimer);
2454         //    waitTimer = null;
2455         //}
2456     };
2457
2458     // private
2459     var updateButtons = function(b){
2460         var width = 0;
2461         if(!b){
2462             buttons["ok"].hide();
2463             buttons["cancel"].hide();
2464             buttons["yes"].hide();
2465             buttons["no"].hide();
2466             //dlg.footer.dom.style.display = 'none';
2467             return width;
2468         }
2469         dlg.footer.dom.style.display = '';
2470         for(var k in buttons){
2471             if(typeof buttons[k] != "function"){
2472                 if(b[k]){
2473                     buttons[k].show();
2474                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2475                     width += buttons[k].el.getWidth()+15;
2476                 }else{
2477                     buttons[k].hide();
2478                 }
2479             }
2480         }
2481         return width;
2482     };
2483
2484     // private
2485     var handleEsc = function(d, k, e){
2486         if(opt && opt.closable !== false){
2487             dlg.hide();
2488         }
2489         if(e){
2490             e.stopEvent();
2491         }
2492     };
2493
2494     return {
2495         /**
2496          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2497          * @return {Roo.BasicDialog} The BasicDialog element
2498          */
2499         getDialog : function(){
2500            if(!dlg){
2501                 dlg = new Roo.bootstrap.Modal( {
2502                     //draggable: true,
2503                     //resizable:false,
2504                     //constraintoviewport:false,
2505                     //fixedcenter:true,
2506                     //collapsible : false,
2507                     //shim:true,
2508                     //modal: true,
2509                   //  width:400,
2510                   //  height:100,
2511                     //buttonAlign:"center",
2512                     closeClick : function(){
2513                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2514                             handleButton("no");
2515                         }else{
2516                             handleButton("cancel");
2517                         }
2518                     }
2519                 });
2520                 dlg.render();
2521                 dlg.on("hide", handleHide);
2522                 mask = dlg.mask;
2523                 //dlg.addKeyListener(27, handleEsc);
2524                 buttons = {};
2525                 this.buttons = buttons;
2526                 var bt = this.buttonText;
2527                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2528                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2529                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2530                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2531                 Roo.log(buttons)
2532                 bodyEl = dlg.body.createChild({
2533
2534                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2535                         '<textarea class="roo-mb-textarea"></textarea>' +
2536                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2537                 });
2538                 msgEl = bodyEl.dom.firstChild;
2539                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2540                 textboxEl.enableDisplayMode();
2541                 textboxEl.addKeyListener([10,13], function(){
2542                     if(dlg.isVisible() && opt && opt.buttons){
2543                         if(opt.buttons.ok){
2544                             handleButton("ok");
2545                         }else if(opt.buttons.yes){
2546                             handleButton("yes");
2547                         }
2548                     }
2549                 });
2550                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2551                 textareaEl.enableDisplayMode();
2552                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2553                 progressEl.enableDisplayMode();
2554                 var pf = progressEl.dom.firstChild;
2555                 if (pf) {
2556                     pp = Roo.get(pf.firstChild);
2557                     pp.setHeight(pf.offsetHeight);
2558                 }
2559                 
2560             }
2561             return dlg;
2562         },
2563
2564         /**
2565          * Updates the message box body text
2566          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2567          * the XHTML-compliant non-breaking space character '&amp;#160;')
2568          * @return {Roo.MessageBox} This message box
2569          */
2570         updateText : function(text){
2571             if(!dlg.isVisible() && !opt.width){
2572                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2573             }
2574             msgEl.innerHTML = text || '&#160;';
2575       
2576             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2577             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2578             var w = Math.max(
2579                     Math.min(opt.width || cw , this.maxWidth), 
2580                     Math.max(opt.minWidth || this.minWidth, bwidth)
2581             );
2582             if(opt.prompt){
2583                 activeTextEl.setWidth(w);
2584             }
2585             if(dlg.isVisible()){
2586                 dlg.fixedcenter = false;
2587             }
2588             // to big, make it scroll. = But as usual stupid IE does not support
2589             // !important..
2590             
2591             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2592                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2593                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2594             } else {
2595                 bodyEl.dom.style.height = '';
2596                 bodyEl.dom.style.overflowY = '';
2597             }
2598             if (cw > w) {
2599                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2600             } else {
2601                 bodyEl.dom.style.overflowX = '';
2602             }
2603             
2604             dlg.setContentSize(w, bodyEl.getHeight());
2605             if(dlg.isVisible()){
2606                 dlg.fixedcenter = true;
2607             }
2608             return this;
2609         },
2610
2611         /**
2612          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2613          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2614          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2615          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2616          * @return {Roo.MessageBox} This message box
2617          */
2618         updateProgress : function(value, text){
2619             if(text){
2620                 this.updateText(text);
2621             }
2622             if (pp) { // weird bug on my firefox - for some reason this is not defined
2623                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2624             }
2625             return this;
2626         },        
2627
2628         /**
2629          * Returns true if the message box is currently displayed
2630          * @return {Boolean} True if the message box is visible, else false
2631          */
2632         isVisible : function(){
2633             return dlg && dlg.isVisible();  
2634         },
2635
2636         /**
2637          * Hides the message box if it is displayed
2638          */
2639         hide : function(){
2640             if(this.isVisible()){
2641                 dlg.hide();
2642             }  
2643         },
2644
2645         /**
2646          * Displays a new message box, or reinitializes an existing message box, based on the config options
2647          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2648          * The following config object properties are supported:
2649          * <pre>
2650 Property    Type             Description
2651 ----------  ---------------  ------------------------------------------------------------------------------------
2652 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2653                                    closes (defaults to undefined)
2654 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2655                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2656 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2657                                    progress and wait dialogs will ignore this property and always hide the
2658                                    close button as they can only be closed programmatically.
2659 cls               String           A custom CSS class to apply to the message box element
2660 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2661                                    displayed (defaults to 75)
2662 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2663                                    function will be btn (the name of the button that was clicked, if applicable,
2664                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2665                                    Progress and wait dialogs will ignore this option since they do not respond to
2666                                    user actions and can only be closed programmatically, so any required function
2667                                    should be called by the same code after it closes the dialog.
2668 icon              String           A CSS class that provides a background image to be used as an icon for
2669                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2670 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2671 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2672 modal             Boolean          False to allow user interaction with the page while the message box is
2673                                    displayed (defaults to true)
2674 msg               String           A string that will replace the existing message box body text (defaults
2675                                    to the XHTML-compliant non-breaking space character '&#160;')
2676 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2677 progress          Boolean          True to display a progress bar (defaults to false)
2678 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2679 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2680 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2681 title             String           The title text
2682 value             String           The string value to set into the active textbox element if displayed
2683 wait              Boolean          True to display a progress bar (defaults to false)
2684 width             Number           The width of the dialog in pixels
2685 </pre>
2686          *
2687          * Example usage:
2688          * <pre><code>
2689 Roo.Msg.show({
2690    title: 'Address',
2691    msg: 'Please enter your address:',
2692    width: 300,
2693    buttons: Roo.MessageBox.OKCANCEL,
2694    multiline: true,
2695    fn: saveAddress,
2696    animEl: 'addAddressBtn'
2697 });
2698 </code></pre>
2699          * @param {Object} config Configuration options
2700          * @return {Roo.MessageBox} This message box
2701          */
2702         show : function(options)
2703         {
2704             
2705             // this causes nightmares if you show one dialog after another
2706             // especially on callbacks..
2707              
2708             if(this.isVisible()){
2709                 
2710                 this.hide();
2711                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2712                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2713                 Roo.log("New Dialog Message:" +  options.msg )
2714                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2715                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2716                 
2717             }
2718             var d = this.getDialog();
2719             opt = options;
2720             d.setTitle(opt.title || "&#160;");
2721             d.close.setDisplayed(opt.closable !== false);
2722             activeTextEl = textboxEl;
2723             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2724             if(opt.prompt){
2725                 if(opt.multiline){
2726                     textboxEl.hide();
2727                     textareaEl.show();
2728                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2729                         opt.multiline : this.defaultTextHeight);
2730                     activeTextEl = textareaEl;
2731                 }else{
2732                     textboxEl.show();
2733                     textareaEl.hide();
2734                 }
2735             }else{
2736                 textboxEl.hide();
2737                 textareaEl.hide();
2738             }
2739             progressEl.setDisplayed(opt.progress === true);
2740             this.updateProgress(0);
2741             activeTextEl.dom.value = opt.value || "";
2742             if(opt.prompt){
2743                 dlg.setDefaultButton(activeTextEl);
2744             }else{
2745                 var bs = opt.buttons;
2746                 var db = null;
2747                 if(bs && bs.ok){
2748                     db = buttons["ok"];
2749                 }else if(bs && bs.yes){
2750                     db = buttons["yes"];
2751                 }
2752                 dlg.setDefaultButton(db);
2753             }
2754             bwidth = updateButtons(opt.buttons);
2755             this.updateText(opt.msg);
2756             if(opt.cls){
2757                 d.el.addClass(opt.cls);
2758             }
2759             d.proxyDrag = opt.proxyDrag === true;
2760             d.modal = opt.modal !== false;
2761             d.mask = opt.modal !== false ? mask : false;
2762             if(!d.isVisible()){
2763                 // force it to the end of the z-index stack so it gets a cursor in FF
2764                 document.body.appendChild(dlg.el.dom);
2765                 d.animateTarget = null;
2766                 d.show(options.animEl);
2767             }
2768             return this;
2769         },
2770
2771         /**
2772          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2773          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2774          * and closing the message box when the process is complete.
2775          * @param {String} title The title bar text
2776          * @param {String} msg The message box body text
2777          * @return {Roo.MessageBox} This message box
2778          */
2779         progress : function(title, msg){
2780             this.show({
2781                 title : title,
2782                 msg : msg,
2783                 buttons: false,
2784                 progress:true,
2785                 closable:false,
2786                 minWidth: this.minProgressWidth,
2787                 modal : true
2788             });
2789             return this;
2790         },
2791
2792         /**
2793          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2794          * If a callback function is passed it will be called after the user clicks the button, and the
2795          * id of the button that was clicked will be passed as the only parameter to the callback
2796          * (could also be the top-right close button).
2797          * @param {String} title The title bar text
2798          * @param {String} msg The message box body text
2799          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2800          * @param {Object} scope (optional) The scope of the callback function
2801          * @return {Roo.MessageBox} This message box
2802          */
2803         alert : function(title, msg, fn, scope){
2804             this.show({
2805                 title : title,
2806                 msg : msg,
2807                 buttons: this.OK,
2808                 fn: fn,
2809                 scope : scope,
2810                 modal : true
2811             });
2812             return this;
2813         },
2814
2815         /**
2816          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2817          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2818          * You are responsible for closing the message box when the process is complete.
2819          * @param {String} msg The message box body text
2820          * @param {String} title (optional) The title bar text
2821          * @return {Roo.MessageBox} This message box
2822          */
2823         wait : function(msg, title){
2824             this.show({
2825                 title : title,
2826                 msg : msg,
2827                 buttons: false,
2828                 closable:false,
2829                 progress:true,
2830                 modal:true,
2831                 width:300,
2832                 wait:true
2833             });
2834             waitTimer = Roo.TaskMgr.start({
2835                 run: function(i){
2836                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2837                 },
2838                 interval: 1000
2839             });
2840             return this;
2841         },
2842
2843         /**
2844          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2845          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2846          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2847          * @param {String} title The title bar text
2848          * @param {String} msg The message box body text
2849          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2850          * @param {Object} scope (optional) The scope of the callback function
2851          * @return {Roo.MessageBox} This message box
2852          */
2853         confirm : function(title, msg, fn, scope){
2854             this.show({
2855                 title : title,
2856                 msg : msg,
2857                 buttons: this.YESNO,
2858                 fn: fn,
2859                 scope : scope,
2860                 modal : true
2861             });
2862             return this;
2863         },
2864
2865         /**
2866          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2867          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2868          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2869          * (could also be the top-right close button) and the text that was entered will be passed as the two
2870          * parameters to the callback.
2871          * @param {String} title The title bar text
2872          * @param {String} msg The message box body text
2873          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2874          * @param {Object} scope (optional) The scope of the callback function
2875          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2876          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2877          * @return {Roo.MessageBox} This message box
2878          */
2879         prompt : function(title, msg, fn, scope, multiline){
2880             this.show({
2881                 title : title,
2882                 msg : msg,
2883                 buttons: this.OKCANCEL,
2884                 fn: fn,
2885                 minWidth:250,
2886                 scope : scope,
2887                 prompt:true,
2888                 multiline: multiline,
2889                 modal : true
2890             });
2891             return this;
2892         },
2893
2894         /**
2895          * Button config that displays a single OK button
2896          * @type Object
2897          */
2898         OK : {ok:true},
2899         /**
2900          * Button config that displays Yes and No buttons
2901          * @type Object
2902          */
2903         YESNO : {yes:true, no:true},
2904         /**
2905          * Button config that displays OK and Cancel buttons
2906          * @type Object
2907          */
2908         OKCANCEL : {ok:true, cancel:true},
2909         /**
2910          * Button config that displays Yes, No and Cancel buttons
2911          * @type Object
2912          */
2913         YESNOCANCEL : {yes:true, no:true, cancel:true},
2914
2915         /**
2916          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2917          * @type Number
2918          */
2919         defaultTextHeight : 75,
2920         /**
2921          * The maximum width in pixels of the message box (defaults to 600)
2922          * @type Number
2923          */
2924         maxWidth : 600,
2925         /**
2926          * The minimum width in pixels of the message box (defaults to 100)
2927          * @type Number
2928          */
2929         minWidth : 100,
2930         /**
2931          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2932          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2933          * @type Number
2934          */
2935         minProgressWidth : 250,
2936         /**
2937          * An object containing the default button text strings that can be overriden for localized language support.
2938          * Supported properties are: ok, cancel, yes and no.
2939          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2940          * @type Object
2941          */
2942         buttonText : {
2943             ok : "OK",
2944             cancel : "Cancel",
2945             yes : "Yes",
2946             no : "No"
2947         }
2948     };
2949 }();
2950
2951 /**
2952  * Shorthand for {@link Roo.MessageBox}
2953  */
2954 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2955 Roo.Msg = Roo.Msg || Roo.MessageBox;
2956 /*
2957  * - LGPL
2958  *
2959  * navbar
2960  * 
2961  */
2962
2963 /**
2964  * @class Roo.bootstrap.Navbar
2965  * @extends Roo.bootstrap.Component
2966  * Bootstrap Navbar class
2967
2968  * @constructor
2969  * Create a new Navbar
2970  * @param {Object} config The config object
2971  */
2972
2973
2974 Roo.bootstrap.Navbar = function(config){
2975     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2976     
2977 };
2978
2979 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2980     
2981     
2982    
2983     // private
2984     navItems : false,
2985     loadMask : false,
2986     
2987     
2988     getAutoCreate : function(){
2989         
2990         
2991         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2992         
2993     },
2994     
2995     initEvents :function ()
2996     {
2997         //Roo.log(this.el.select('.navbar-toggle',true));
2998         this.el.select('.navbar-toggle',true).on('click', function() {
2999            // Roo.log('click');
3000             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3001         }, this);
3002         
3003         var mark = {
3004             tag: "div",
3005             cls:"x-dlg-mask"
3006         }
3007         
3008         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3009         
3010         var size = this.el.getSize();
3011         this.maskEl.setSize(size.width, size.height);
3012         this.maskEl.enableDisplayMode("block");
3013         this.maskEl.hide();
3014         
3015         if(this.loadMask){
3016             this.maskEl.show();
3017         }
3018     },
3019     
3020     
3021     getChildContainer : function()
3022     {
3023         if (this.el.select('.collapse').getCount()) {
3024             return this.el.select('.collapse',true).first();
3025         }
3026         
3027         return this.el;
3028     },
3029     
3030     mask : function()
3031     {
3032         this.maskEl.show();
3033     },
3034     
3035     unmask : function()
3036     {
3037         this.maskEl.hide();
3038     } 
3039     
3040     
3041     
3042     
3043 });
3044
3045
3046
3047  
3048
3049  /*
3050  * - LGPL
3051  *
3052  * navbar
3053  * 
3054  */
3055
3056 /**
3057  * @class Roo.bootstrap.NavSimplebar
3058  * @extends Roo.bootstrap.Navbar
3059  * Bootstrap Sidebar class
3060  *
3061  * @cfg {Boolean} inverse is inverted color
3062  * 
3063  * @cfg {String} type (nav | pills | tabs)
3064  * @cfg {Boolean} arrangement stacked | justified
3065  * @cfg {String} align (left | right) alignment
3066  * 
3067  * @cfg {Boolean} main (true|false) main nav bar? default false
3068  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3069  * 
3070  * @cfg {String} tag (header|footer|nav|div) default is nav 
3071
3072  * 
3073  * 
3074  * 
3075  * @constructor
3076  * Create a new Sidebar
3077  * @param {Object} config The config object
3078  */
3079
3080
3081 Roo.bootstrap.NavSimplebar = function(config){
3082     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3083 };
3084
3085 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3086     
3087     inverse: false,
3088     
3089     type: false,
3090     arrangement: '',
3091     align : false,
3092     
3093     
3094     
3095     main : false,
3096     
3097     
3098     tag : false,
3099     
3100     
3101     getAutoCreate : function(){
3102         
3103         
3104         var cfg = {
3105             tag : this.tag || 'div',
3106             cls : 'navbar'
3107         };
3108           
3109         
3110         cfg.cn = [
3111             {
3112                 cls: 'nav',
3113                 tag : 'ul'
3114             }
3115         ];
3116         
3117          
3118         this.type = this.type || 'nav';
3119         if (['tabs','pills'].indexOf(this.type)!==-1) {
3120             cfg.cn[0].cls += ' nav-' + this.type
3121         
3122         
3123         } else {
3124             if (this.type!=='nav') {
3125                 Roo.log('nav type must be nav/tabs/pills')
3126             }
3127             cfg.cn[0].cls += ' navbar-nav'
3128         }
3129         
3130         
3131         
3132         
3133         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3134             cfg.cn[0].cls += ' nav-' + this.arrangement;
3135         }
3136         
3137         
3138         if (this.align === 'right') {
3139             cfg.cn[0].cls += ' navbar-right';
3140         }
3141         
3142         if (this.inverse) {
3143             cfg.cls += ' navbar-inverse';
3144             
3145         }
3146         
3147         
3148         return cfg;
3149     
3150         
3151     }
3152     
3153     
3154     
3155 });
3156
3157
3158
3159  
3160
3161  
3162        /*
3163  * - LGPL
3164  *
3165  * navbar
3166  * 
3167  */
3168
3169 /**
3170  * @class Roo.bootstrap.NavHeaderbar
3171  * @extends Roo.bootstrap.NavSimplebar
3172  * Bootstrap Sidebar class
3173  *
3174  * @cfg {String} brand what is brand
3175  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3176  * @cfg {String} brand_href href of the brand
3177  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3178  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3179  * 
3180  * @constructor
3181  * Create a new Sidebar
3182  * @param {Object} config The config object
3183  */
3184
3185
3186 Roo.bootstrap.NavHeaderbar = function(config){
3187     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3188 };
3189
3190 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3191     
3192     position: '',
3193     brand: '',
3194     brand_href: false,
3195     srButton : true,
3196     autohide : false,
3197     
3198     getAutoCreate : function(){
3199         
3200         var   cfg = {
3201             tag: this.nav || 'nav',
3202             cls: 'navbar',
3203             role: 'navigation',
3204             cn: []
3205         };
3206         
3207         if(this.srButton){
3208             cfg.cn.push({
3209                 tag: 'div',
3210                 cls: 'navbar-header',
3211                 cn: [
3212                     {
3213                         tag: 'button',
3214                         type: 'button',
3215                         cls: 'navbar-toggle',
3216                         'data-toggle': 'collapse',
3217                         cn: [
3218                             {
3219                                 tag: 'span',
3220                                 cls: 'sr-only',
3221                                 html: 'Toggle navigation'
3222                             },
3223                             {
3224                                 tag: 'span',
3225                                 cls: 'icon-bar'
3226                             },
3227                             {
3228                                 tag: 'span',
3229                                 cls: 'icon-bar'
3230                             },
3231                             {
3232                                 tag: 'span',
3233                                 cls: 'icon-bar'
3234                             }
3235                         ]
3236                     }
3237                 ]
3238             });
3239         }
3240         
3241         cfg.cn.push({
3242             tag: 'div',
3243             cls: 'collapse navbar-collapse',
3244             cn : []
3245         });
3246         
3247         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3248         
3249         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3250             cfg.cls += ' navbar-' + this.position;
3251             
3252             // tag can override this..
3253             
3254             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3255         }
3256         
3257         if (this.brand !== '') {
3258             cfg.cn[0].cn.push({
3259                 tag: 'a',
3260                 href: this.brand_href ? this.brand_href : '#',
3261                 cls: 'navbar-brand',
3262                 cn: [
3263                 this.brand
3264                 ]
3265             });
3266         }
3267         
3268         if(this.main){
3269             cfg.cls += ' main-nav';
3270         }
3271         
3272         
3273         return cfg;
3274
3275         
3276     },
3277     initEvents : function()
3278     {
3279         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3280         
3281         if (this.autohide) {
3282             
3283             var prevScroll = 0;
3284             var ft = this.el;
3285             
3286             Roo.get(document).on('scroll',function(e) {
3287                 var ns = Roo.get(document).getScroll().top;
3288                 var os = prevScroll;
3289                 prevScroll = ns;
3290                 
3291                 if(ns > os){
3292                     ft.removeClass('slideDown');
3293                     ft.addClass('slideUp');
3294                     return;
3295                 }
3296                 ft.removeClass('slideUp');
3297                 ft.addClass('slideDown');
3298                  
3299               
3300           },this);
3301         }
3302     }    
3303           
3304       
3305     
3306     
3307 });
3308
3309
3310
3311  
3312
3313  /*
3314  * - LGPL
3315  *
3316  * navbar
3317  * 
3318  */
3319
3320 /**
3321  * @class Roo.bootstrap.NavSidebar
3322  * @extends Roo.bootstrap.Navbar
3323  * Bootstrap Sidebar class
3324  * 
3325  * @constructor
3326  * Create a new Sidebar
3327  * @param {Object} config The config object
3328  */
3329
3330
3331 Roo.bootstrap.NavSidebar = function(config){
3332     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3333 };
3334
3335 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3336     
3337     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3338     
3339     getAutoCreate : function(){
3340         
3341         
3342         return  {
3343             tag: 'div',
3344             cls: 'sidebar sidebar-nav'
3345         };
3346     
3347         
3348     }
3349     
3350     
3351     
3352 });
3353
3354
3355
3356  
3357
3358  /*
3359  * - LGPL
3360  *
3361  * nav group
3362  * 
3363  */
3364
3365 /**
3366  * @class Roo.bootstrap.NavGroup
3367  * @extends Roo.bootstrap.Component
3368  * Bootstrap NavGroup class
3369  * @cfg {String} align left | right
3370  * @cfg {Boolean} inverse false | true
3371  * @cfg {String} type (nav|pills|tab) default nav
3372  * @cfg {String} navId - reference Id for navbar.
3373
3374  * 
3375  * @constructor
3376  * Create a new nav group
3377  * @param {Object} config The config object
3378  */
3379
3380 Roo.bootstrap.NavGroup = function(config){
3381     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3382     this.navItems = [];
3383    
3384     Roo.bootstrap.NavGroup.register(this);
3385      this.addEvents({
3386         /**
3387              * @event changed
3388              * Fires when the active item changes
3389              * @param {Roo.bootstrap.NavGroup} this
3390              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3391              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3392          */
3393         'changed': true
3394      });
3395     
3396 };
3397
3398 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3399     
3400     align: '',
3401     inverse: false,
3402     form: false,
3403     type: 'nav',
3404     navId : '',
3405     // private
3406     
3407     navItems : false, 
3408     
3409     getAutoCreate : function()
3410     {
3411         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3412         
3413         cfg = {
3414             tag : 'ul',
3415             cls: 'nav' 
3416         }
3417         
3418         if (['tabs','pills'].indexOf(this.type)!==-1) {
3419             cfg.cls += ' nav-' + this.type
3420         } else {
3421             if (this.type!=='nav') {
3422                 Roo.log('nav type must be nav/tabs/pills')
3423             }
3424             cfg.cls += ' navbar-nav'
3425         }
3426         
3427         if (this.parent().sidebar) {
3428             cfg = {
3429                 tag: 'ul',
3430                 cls: 'dashboard-menu sidebar-menu'
3431             }
3432             
3433             return cfg;
3434         }
3435         
3436         if (this.form === true) {
3437             cfg = {
3438                 tag: 'form',
3439                 cls: 'navbar-form'
3440             }
3441             
3442             if (this.align === 'right') {
3443                 cfg.cls += ' navbar-right';
3444             } else {
3445                 cfg.cls += ' navbar-left';
3446             }
3447         }
3448         
3449         if (this.align === 'right') {
3450             cfg.cls += ' navbar-right';
3451         }
3452         
3453         if (this.inverse) {
3454             cfg.cls += ' navbar-inverse';
3455             
3456         }
3457         
3458         
3459         return cfg;
3460     },
3461     /**
3462     * sets the active Navigation item
3463     * @param {Roo.bootstrap.NavItem} the new current navitem
3464     */
3465     setActiveItem : function(item)
3466     {
3467         var prev = false;
3468         Roo.each(this.navItems, function(v){
3469             if (v == item) {
3470                 return ;
3471             }
3472             if (v.isActive()) {
3473                 v.setActive(false, true);
3474                 prev = v;
3475                 
3476             }
3477             
3478         });
3479
3480         item.setActive(true, true);
3481         this.fireEvent('changed', this, item, prev);
3482         
3483         
3484     },
3485     /**
3486     * gets the active Navigation item
3487     * @return {Roo.bootstrap.NavItem} the current navitem
3488     */
3489     getActive : function()
3490     {
3491         
3492         var prev = false;
3493         Roo.each(this.navItems, function(v){
3494             
3495             if (v.isActive()) {
3496                 prev = v;
3497                 
3498             }
3499             
3500         });
3501         return prev;
3502     },
3503     
3504     indexOfNav : function()
3505     {
3506         
3507         var prev = false;
3508         Roo.each(this.navItems, function(v,i){
3509             
3510             if (v.isActive()) {
3511                 prev = i;
3512                 
3513             }
3514             
3515         });
3516         return prev;
3517     },
3518     /**
3519     * adds a Navigation item
3520     * @param {Roo.bootstrap.NavItem} the navitem to add
3521     */
3522     addItem : function(cfg)
3523     {
3524         var cn = new Roo.bootstrap.NavItem(cfg);
3525         this.register(cn);
3526         cn.parentId = this.id;
3527         cn.onRender(this.el, null);
3528         return cn;
3529     },
3530     /**
3531     * register a Navigation item
3532     * @param {Roo.bootstrap.NavItem} the navitem to add
3533     */
3534     register : function(item)
3535     {
3536         this.navItems.push( item);
3537         item.navId = this.navId;
3538     
3539     },
3540     
3541     /**
3542     * clear all the Navigation item
3543     */
3544    
3545     clearAll : function()
3546     {
3547         this.navItems = [];
3548         this.el.dom.innerHTML = '';
3549     },
3550     
3551     getNavItem: function(tabId)
3552     {
3553         var ret = false;
3554         Roo.each(this.navItems, function(e) {
3555             if (e.tabId == tabId) {
3556                ret =  e;
3557                return false;
3558             }
3559             return true;
3560             
3561         });
3562         return ret;
3563     },
3564     
3565     setActiveNext : function()
3566     {
3567         var i = this.indexOfNav(this.getActive());
3568         if (i > this.navItems.length) {
3569             return;
3570         }
3571         this.setActiveItem(this.navItems[i+1]);
3572     },
3573     setActivePrev : function()
3574     {
3575         var i = this.indexOfNav(this.getActive());
3576         if (i  < 1) {
3577             return;
3578         }
3579         this.setActiveItem(this.navItems[i-1]);
3580     },
3581     clearWasActive : function(except) {
3582         Roo.each(this.navItems, function(e) {
3583             if (e.tabId != except.tabId && e.was_active) {
3584                e.was_active = false;
3585                return false;
3586             }
3587             return true;
3588             
3589         });
3590     },
3591     getWasActive : function ()
3592     {
3593         var r = false;
3594         Roo.each(this.navItems, function(e) {
3595             if (e.was_active) {
3596                r = e;
3597                return false;
3598             }
3599             return true;
3600             
3601         });
3602         return r;
3603     }
3604     
3605     
3606 });
3607
3608  
3609 Roo.apply(Roo.bootstrap.NavGroup, {
3610     
3611     groups: {},
3612      /**
3613     * register a Navigation Group
3614     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3615     */
3616     register : function(navgrp)
3617     {
3618         this.groups[navgrp.navId] = navgrp;
3619         
3620     },
3621     /**
3622     * fetch a Navigation Group based on the navigation ID
3623     * @param {string} the navgroup to add
3624     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3625     */
3626     get: function(navId) {
3627         if (typeof(this.groups[navId]) == 'undefined') {
3628             return false;
3629             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3630         }
3631         return this.groups[navId] ;
3632     }
3633     
3634     
3635     
3636 });
3637
3638  /*
3639  * - LGPL
3640  *
3641  * row
3642  * 
3643  */
3644
3645 /**
3646  * @class Roo.bootstrap.NavItem
3647  * @extends Roo.bootstrap.Component
3648  * Bootstrap Navbar.NavItem class
3649  * @cfg {String} href  link to
3650  * @cfg {String} html content of button
3651  * @cfg {String} badge text inside badge
3652  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3653  * @cfg {String} glyphicon name of glyphicon
3654  * @cfg {String} icon name of font awesome icon
3655  * @cfg {Boolean} active Is item active
3656  * @cfg {Boolean} disabled Is item disabled
3657  
3658  * @cfg {Boolean} preventDefault (true | false) default false
3659  * @cfg {String} tabId the tab that this item activates.
3660  * @cfg {String} tagtype (a|span) render as a href or span?
3661   
3662  * @constructor
3663  * Create a new Navbar Item
3664  * @param {Object} config The config object
3665  */
3666 Roo.bootstrap.NavItem = function(config){
3667     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3668     this.addEvents({
3669         // raw events
3670         /**
3671          * @event click
3672          * The raw click event for the entire grid.
3673          * @param {Roo.EventObject} e
3674          */
3675         "click" : true,
3676          /**
3677             * @event changed
3678             * Fires when the active item active state changes
3679             * @param {Roo.bootstrap.NavItem} this
3680             * @param {boolean} state the new state
3681              
3682          */
3683         'changed': true
3684     });
3685    
3686 };
3687
3688 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3689     
3690     href: false,
3691     html: '',
3692     badge: '',
3693     icon: false,
3694     glyphicon: false,
3695     active: false,
3696     preventDefault : false,
3697     tabId : false,
3698     tagtype : 'a',
3699     disabled : false,
3700     
3701     was_active : false,
3702     
3703     getAutoCreate : function(){
3704          
3705         var cfg = {
3706             tag: 'li',
3707             cls: 'nav-item'
3708             
3709         }
3710         if (this.active) {
3711             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3712         }
3713         if (this.disabled) {
3714             cfg.cls += ' disabled';
3715         }
3716         
3717         if (this.href || this.html || this.glyphicon || this.icon) {
3718             cfg.cn = [
3719                 {
3720                     tag: this.tagtype,
3721                     href : this.href || "#",
3722                     html: this.html || ''
3723                 }
3724             ];
3725             
3726             if (this.icon) {
3727                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3728             }
3729
3730             if(this.glyphicon) {
3731                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3732             }
3733             
3734             if (this.menu) {
3735                 
3736                 cfg.cn[0].html += " <span class='caret'></span>";
3737              
3738             }
3739             
3740             if (this.badge !== '') {
3741                  
3742                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3743             }
3744         }
3745         
3746         
3747         
3748         return cfg;
3749     },
3750     initEvents: function() 
3751     {
3752         if (typeof (this.menu) != 'undefined') {
3753             this.menu.parentType = this.xtype;
3754             this.menu.triggerEl = this.el;
3755             this.addxtype(Roo.apply({}, this.menu));
3756         }
3757         
3758         this.el.select('a',true).on('click', this.onClick, this);
3759         
3760         if(this.tagtype == 'span'){
3761             this.el.select('span',true).on('click', this.onClick, this);
3762         }
3763        
3764         // at this point parent should be available..
3765         this.parent().register(this);
3766     },
3767     
3768     onClick : function(e)
3769     {
3770          
3771         if(this.preventDefault){
3772             e.preventDefault();
3773         }
3774         if (this.disabled) {
3775             return;
3776         }
3777         
3778         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3779         if (tg && tg.transition) {
3780             Roo.log("waiting for the transitionend");
3781             return;
3782         }
3783         
3784         Roo.log("fire event clicked");
3785         if(this.fireEvent('click', this, e) === false){
3786             return;
3787         };
3788         
3789         if(this.tagtype == 'span'){
3790             return;
3791         }
3792         
3793         var p = this.parent();
3794         if (['tabs','pills'].indexOf(p.type)!==-1) {
3795             if (typeof(p.setActiveItem) !== 'undefined') {
3796                 p.setActiveItem(this);
3797             }
3798         }
3799         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3800         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3801             // remove the collapsed menu expand...
3802             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3803         }
3804         
3805     },
3806     
3807     isActive: function () {
3808         return this.active
3809     },
3810     setActive : function(state, fire, is_was_active)
3811     {
3812         if (this.active && !state & this.navId) {
3813             this.was_active = true;
3814             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3815             if (nv) {
3816                 nv.clearWasActive(this);
3817             }
3818             
3819         }
3820         this.active = state;
3821         
3822         if (!state ) {
3823             this.el.removeClass('active');
3824         } else if (!this.el.hasClass('active')) {
3825             this.el.addClass('active');
3826         }
3827         if (fire) {
3828             this.fireEvent('changed', this, state);
3829         }
3830         
3831         // show a panel if it's registered and related..
3832         
3833         if (!this.navId || !this.tabId || !state || is_was_active) {
3834             return;
3835         }
3836         
3837         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3838         if (!tg) {
3839             return;
3840         }
3841         var pan = tg.getPanelByName(this.tabId);
3842         if (!pan) {
3843             return;
3844         }
3845         // if we can not flip to new panel - go back to old nav highlight..
3846         if (false == tg.showPanel(pan)) {
3847             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3848             if (nv) {
3849                 var onav = nv.getWasActive();
3850                 if (onav) {
3851                     onav.setActive(true, false, true);
3852                 }
3853             }
3854             
3855         }
3856         
3857         
3858         
3859     },
3860      // this should not be here...
3861     setDisabled : function(state)
3862     {
3863         this.disabled = state;
3864         if (!state ) {
3865             this.el.removeClass('disabled');
3866         } else if (!this.el.hasClass('disabled')) {
3867             this.el.addClass('disabled');
3868         }
3869         
3870     },
3871     
3872     /**
3873      * Fetch the element to display the tooltip on.
3874      * @return {Roo.Element} defaults to this.el
3875      */
3876     tooltipEl : function()
3877     {
3878         return this.el.select('' + this.tagtype + '', true).first();
3879     }
3880 });
3881  
3882
3883  /*
3884  * - LGPL
3885  *
3886  * sidebar item
3887  *
3888  *  li
3889  *    <span> icon </span>
3890  *    <span> text </span>
3891  *    <span>badge </span>
3892  */
3893
3894 /**
3895  * @class Roo.bootstrap.NavSidebarItem
3896  * @extends Roo.bootstrap.NavItem
3897  * Bootstrap Navbar.NavSidebarItem class
3898  * @constructor
3899  * Create a new Navbar Button
3900  * @param {Object} config The config object
3901  */
3902 Roo.bootstrap.NavSidebarItem = function(config){
3903     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3904     this.addEvents({
3905         // raw events
3906         /**
3907          * @event click
3908          * The raw click event for the entire grid.
3909          * @param {Roo.EventObject} e
3910          */
3911         "click" : true,
3912          /**
3913             * @event changed
3914             * Fires when the active item active state changes
3915             * @param {Roo.bootstrap.NavSidebarItem} this
3916             * @param {boolean} state the new state
3917              
3918          */
3919         'changed': true
3920     });
3921    
3922 };
3923
3924 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3925     
3926     
3927     getAutoCreate : function(){
3928         
3929         
3930         var a = {
3931                 tag: 'a',
3932                 href : this.href || '#',
3933                 cls: '',
3934                 html : '',
3935                 cn : []
3936         };
3937         var cfg = {
3938             tag: 'li',
3939             cls: '',
3940             cn: [ a ]
3941         }
3942         var span = {
3943             tag: 'span',
3944             html : this.html || ''
3945         }
3946         
3947         
3948         if (this.active) {
3949             cfg.cls += ' active';
3950         }
3951         
3952         // left icon..
3953         if (this.glyphicon || this.icon) {
3954             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3955             a.cn.push({ tag : 'i', cls : c }) ;
3956         }
3957         // html..
3958         a.cn.push(span);
3959         // then badge..
3960         if (this.badge !== '') {
3961             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3962         }
3963         // fi
3964         if (this.menu) {
3965             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3966             a.cls += 'dropdown-toggle treeview' ;
3967             
3968         }
3969         
3970         
3971         
3972         return cfg;
3973          
3974            
3975     }
3976    
3977      
3978  
3979 });
3980  
3981
3982  /*
3983  * - LGPL
3984  *
3985  * row
3986  * 
3987  */
3988
3989 /**
3990  * @class Roo.bootstrap.Row
3991  * @extends Roo.bootstrap.Component
3992  * Bootstrap Row class (contains columns...)
3993  * 
3994  * @constructor
3995  * Create a new Row
3996  * @param {Object} config The config object
3997  */
3998
3999 Roo.bootstrap.Row = function(config){
4000     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4001 };
4002
4003 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4004     
4005     getAutoCreate : function(){
4006        return {
4007             cls: 'row clearfix'
4008        };
4009     }
4010     
4011     
4012 });
4013
4014  
4015
4016  /*
4017  * - LGPL
4018  *
4019  * element
4020  * 
4021  */
4022
4023 /**
4024  * @class Roo.bootstrap.Element
4025  * @extends Roo.bootstrap.Component
4026  * Bootstrap Element class
4027  * @cfg {String} html contents of the element
4028  * @cfg {String} tag tag of the element
4029  * @cfg {String} cls class of the element
4030  * 
4031  * @constructor
4032  * Create a new Element
4033  * @param {Object} config The config object
4034  */
4035
4036 Roo.bootstrap.Element = function(config){
4037     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4038 };
4039
4040 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4041     
4042     tag: 'div',
4043     cls: '',
4044     html: '',
4045      
4046     
4047     getAutoCreate : function(){
4048         
4049         var cfg = {
4050             tag: this.tag,
4051             cls: this.cls,
4052             html: this.html
4053         }
4054         
4055         
4056         
4057         return cfg;
4058     }
4059    
4060 });
4061
4062  
4063
4064  /*
4065  * - LGPL
4066  *
4067  * pagination
4068  * 
4069  */
4070
4071 /**
4072  * @class Roo.bootstrap.Pagination
4073  * @extends Roo.bootstrap.Component
4074  * Bootstrap Pagination class
4075  * @cfg {String} size xs | sm | md | lg
4076  * @cfg {Boolean} inverse false | true
4077  * 
4078  * @constructor
4079  * Create a new Pagination
4080  * @param {Object} config The config object
4081  */
4082
4083 Roo.bootstrap.Pagination = function(config){
4084     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4085 };
4086
4087 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4088     
4089     cls: false,
4090     size: false,
4091     inverse: false,
4092     
4093     getAutoCreate : function(){
4094         var cfg = {
4095             tag: 'ul',
4096                 cls: 'pagination'
4097         };
4098         if (this.inverse) {
4099             cfg.cls += ' inverse';
4100         }
4101         if (this.html) {
4102             cfg.html=this.html;
4103         }
4104         if (this.cls) {
4105             cfg.cls += " " + this.cls;
4106         }
4107         return cfg;
4108     }
4109    
4110 });
4111
4112  
4113
4114  /*
4115  * - LGPL
4116  *
4117  * Pagination item
4118  * 
4119  */
4120
4121
4122 /**
4123  * @class Roo.bootstrap.PaginationItem
4124  * @extends Roo.bootstrap.Component
4125  * Bootstrap PaginationItem class
4126  * @cfg {String} html text
4127  * @cfg {String} href the link
4128  * @cfg {Boolean} preventDefault (true | false) default true
4129  * @cfg {Boolean} active (true | false) default false
4130  * 
4131  * 
4132  * @constructor
4133  * Create a new PaginationItem
4134  * @param {Object} config The config object
4135  */
4136
4137
4138 Roo.bootstrap.PaginationItem = function(config){
4139     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4140     this.addEvents({
4141         // raw events
4142         /**
4143          * @event click
4144          * The raw click event for the entire grid.
4145          * @param {Roo.EventObject} e
4146          */
4147         "click" : true
4148     });
4149 };
4150
4151 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4152     
4153     href : false,
4154     html : false,
4155     preventDefault: true,
4156     active : false,
4157     cls : false,
4158     
4159     getAutoCreate : function(){
4160         var cfg= {
4161             tag: 'li',
4162             cn: [
4163                 {
4164                     tag : 'a',
4165                     href : this.href ? this.href : '#',
4166                     html : this.html ? this.html : ''
4167                 }
4168             ]
4169         };
4170         
4171         if(this.cls){
4172             cfg.cls = this.cls;
4173         }
4174         
4175         if(this.active){
4176             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4177         }
4178         
4179         return cfg;
4180     },
4181     
4182     initEvents: function() {
4183         
4184         this.el.on('click', this.onClick, this);
4185         
4186     },
4187     onClick : function(e)
4188     {
4189         Roo.log('PaginationItem on click ');
4190         if(this.preventDefault){
4191             e.preventDefault();
4192         }
4193         
4194         this.fireEvent('click', this, e);
4195     }
4196    
4197 });
4198
4199  
4200
4201  /*
4202  * - LGPL
4203  *
4204  * slider
4205  * 
4206  */
4207
4208
4209 /**
4210  * @class Roo.bootstrap.Slider
4211  * @extends Roo.bootstrap.Component
4212  * Bootstrap Slider class
4213  *    
4214  * @constructor
4215  * Create a new Slider
4216  * @param {Object} config The config object
4217  */
4218
4219 Roo.bootstrap.Slider = function(config){
4220     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4221 };
4222
4223 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4224     
4225     getAutoCreate : function(){
4226         
4227         var cfg = {
4228             tag: 'div',
4229             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4230             cn: [
4231                 {
4232                     tag: 'a',
4233                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4234                 }
4235             ]
4236         }
4237         
4238         return cfg;
4239     }
4240    
4241 });
4242
4243  /*
4244  * Based on:
4245  * Ext JS Library 1.1.1
4246  * Copyright(c) 2006-2007, Ext JS, LLC.
4247  *
4248  * Originally Released Under LGPL - original licence link has changed is not relivant.
4249  *
4250  * Fork - LGPL
4251  * <script type="text/javascript">
4252  */
4253  
4254
4255 /**
4256  * @class Roo.grid.ColumnModel
4257  * @extends Roo.util.Observable
4258  * This is the default implementation of a ColumnModel used by the Grid. It defines
4259  * the columns in the grid.
4260  * <br>Usage:<br>
4261  <pre><code>
4262  var colModel = new Roo.grid.ColumnModel([
4263         {header: "Ticker", width: 60, sortable: true, locked: true},
4264         {header: "Company Name", width: 150, sortable: true},
4265         {header: "Market Cap.", width: 100, sortable: true},
4266         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4267         {header: "Employees", width: 100, sortable: true, resizable: false}
4268  ]);
4269  </code></pre>
4270  * <p>
4271  
4272  * The config options listed for this class are options which may appear in each
4273  * individual column definition.
4274  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4275  * @constructor
4276  * @param {Object} config An Array of column config objects. See this class's
4277  * config objects for details.
4278 */
4279 Roo.grid.ColumnModel = function(config){
4280         /**
4281      * The config passed into the constructor
4282      */
4283     this.config = config;
4284     this.lookup = {};
4285
4286     // if no id, create one
4287     // if the column does not have a dataIndex mapping,
4288     // map it to the order it is in the config
4289     for(var i = 0, len = config.length; i < len; i++){
4290         var c = config[i];
4291         if(typeof c.dataIndex == "undefined"){
4292             c.dataIndex = i;
4293         }
4294         if(typeof c.renderer == "string"){
4295             c.renderer = Roo.util.Format[c.renderer];
4296         }
4297         if(typeof c.id == "undefined"){
4298             c.id = Roo.id();
4299         }
4300         if(c.editor && c.editor.xtype){
4301             c.editor  = Roo.factory(c.editor, Roo.grid);
4302         }
4303         if(c.editor && c.editor.isFormField){
4304             c.editor = new Roo.grid.GridEditor(c.editor);
4305         }
4306         this.lookup[c.id] = c;
4307     }
4308
4309     /**
4310      * The width of columns which have no width specified (defaults to 100)
4311      * @type Number
4312      */
4313     this.defaultWidth = 100;
4314
4315     /**
4316      * Default sortable of columns which have no sortable specified (defaults to false)
4317      * @type Boolean
4318      */
4319     this.defaultSortable = false;
4320
4321     this.addEvents({
4322         /**
4323              * @event widthchange
4324              * Fires when the width of a column changes.
4325              * @param {ColumnModel} this
4326              * @param {Number} columnIndex The column index
4327              * @param {Number} newWidth The new width
4328              */
4329             "widthchange": true,
4330         /**
4331              * @event headerchange
4332              * Fires when the text of a header changes.
4333              * @param {ColumnModel} this
4334              * @param {Number} columnIndex The column index
4335              * @param {Number} newText The new header text
4336              */
4337             "headerchange": true,
4338         /**
4339              * @event hiddenchange
4340              * Fires when a column is hidden or "unhidden".
4341              * @param {ColumnModel} this
4342              * @param {Number} columnIndex The column index
4343              * @param {Boolean} hidden true if hidden, false otherwise
4344              */
4345             "hiddenchange": true,
4346             /**
4347          * @event columnmoved
4348          * Fires when a column is moved.
4349          * @param {ColumnModel} this
4350          * @param {Number} oldIndex
4351          * @param {Number} newIndex
4352          */
4353         "columnmoved" : true,
4354         /**
4355          * @event columlockchange
4356          * Fires when a column's locked state is changed
4357          * @param {ColumnModel} this
4358          * @param {Number} colIndex
4359          * @param {Boolean} locked true if locked
4360          */
4361         "columnlockchange" : true
4362     });
4363     Roo.grid.ColumnModel.superclass.constructor.call(this);
4364 };
4365 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4366     /**
4367      * @cfg {String} header The header text to display in the Grid view.
4368      */
4369     /**
4370      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4371      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4372      * specified, the column's index is used as an index into the Record's data Array.
4373      */
4374     /**
4375      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4376      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4377      */
4378     /**
4379      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4380      * Defaults to the value of the {@link #defaultSortable} property.
4381      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4382      */
4383     /**
4384      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4385      */
4386     /**
4387      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4388      */
4389     /**
4390      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4391      */
4392     /**
4393      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4394      */
4395     /**
4396      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4397      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4398      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4399      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4400      */
4401        /**
4402      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4403      */
4404     /**
4405      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4406      */
4407     /**
4408      * @cfg {String} cursor (Optional)
4409      */
4410     /**
4411      * Returns the id of the column at the specified index.
4412      * @param {Number} index The column index
4413      * @return {String} the id
4414      */
4415     getColumnId : function(index){
4416         return this.config[index].id;
4417     },
4418
4419     /**
4420      * Returns the column for a specified id.
4421      * @param {String} id The column id
4422      * @return {Object} the column
4423      */
4424     getColumnById : function(id){
4425         return this.lookup[id];
4426     },
4427
4428     
4429     /**
4430      * Returns the column for a specified dataIndex.
4431      * @param {String} dataIndex The column dataIndex
4432      * @return {Object|Boolean} the column or false if not found
4433      */
4434     getColumnByDataIndex: function(dataIndex){
4435         var index = this.findColumnIndex(dataIndex);
4436         return index > -1 ? this.config[index] : false;
4437     },
4438     
4439     /**
4440      * Returns the index for a specified column id.
4441      * @param {String} id The column id
4442      * @return {Number} the index, or -1 if not found
4443      */
4444     getIndexById : function(id){
4445         for(var i = 0, len = this.config.length; i < len; i++){
4446             if(this.config[i].id == id){
4447                 return i;
4448             }
4449         }
4450         return -1;
4451     },
4452     
4453     /**
4454      * Returns the index for a specified column dataIndex.
4455      * @param {String} dataIndex The column dataIndex
4456      * @return {Number} the index, or -1 if not found
4457      */
4458     
4459     findColumnIndex : function(dataIndex){
4460         for(var i = 0, len = this.config.length; i < len; i++){
4461             if(this.config[i].dataIndex == dataIndex){
4462                 return i;
4463             }
4464         }
4465         return -1;
4466     },
4467     
4468     
4469     moveColumn : function(oldIndex, newIndex){
4470         var c = this.config[oldIndex];
4471         this.config.splice(oldIndex, 1);
4472         this.config.splice(newIndex, 0, c);
4473         this.dataMap = null;
4474         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4475     },
4476
4477     isLocked : function(colIndex){
4478         return this.config[colIndex].locked === true;
4479     },
4480
4481     setLocked : function(colIndex, value, suppressEvent){
4482         if(this.isLocked(colIndex) == value){
4483             return;
4484         }
4485         this.config[colIndex].locked = value;
4486         if(!suppressEvent){
4487             this.fireEvent("columnlockchange", this, colIndex, value);
4488         }
4489     },
4490
4491     getTotalLockedWidth : function(){
4492         var totalWidth = 0;
4493         for(var i = 0; i < this.config.length; i++){
4494             if(this.isLocked(i) && !this.isHidden(i)){
4495                 this.totalWidth += this.getColumnWidth(i);
4496             }
4497         }
4498         return totalWidth;
4499     },
4500
4501     getLockedCount : function(){
4502         for(var i = 0, len = this.config.length; i < len; i++){
4503             if(!this.isLocked(i)){
4504                 return i;
4505             }
4506         }
4507     },
4508
4509     /**
4510      * Returns the number of columns.
4511      * @return {Number}
4512      */
4513     getColumnCount : function(visibleOnly){
4514         if(visibleOnly === true){
4515             var c = 0;
4516             for(var i = 0, len = this.config.length; i < len; i++){
4517                 if(!this.isHidden(i)){
4518                     c++;
4519                 }
4520             }
4521             return c;
4522         }
4523         return this.config.length;
4524     },
4525
4526     /**
4527      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4528      * @param {Function} fn
4529      * @param {Object} scope (optional)
4530      * @return {Array} result
4531      */
4532     getColumnsBy : function(fn, scope){
4533         var r = [];
4534         for(var i = 0, len = this.config.length; i < len; i++){
4535             var c = this.config[i];
4536             if(fn.call(scope||this, c, i) === true){
4537                 r[r.length] = c;
4538             }
4539         }
4540         return r;
4541     },
4542
4543     /**
4544      * Returns true if the specified column is sortable.
4545      * @param {Number} col The column index
4546      * @return {Boolean}
4547      */
4548     isSortable : function(col){
4549         if(typeof this.config[col].sortable == "undefined"){
4550             return this.defaultSortable;
4551         }
4552         return this.config[col].sortable;
4553     },
4554
4555     /**
4556      * Returns the rendering (formatting) function defined for the column.
4557      * @param {Number} col The column index.
4558      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4559      */
4560     getRenderer : function(col){
4561         if(!this.config[col].renderer){
4562             return Roo.grid.ColumnModel.defaultRenderer;
4563         }
4564         return this.config[col].renderer;
4565     },
4566
4567     /**
4568      * Sets the rendering (formatting) function for a column.
4569      * @param {Number} col The column index
4570      * @param {Function} fn The function to use to process the cell's raw data
4571      * to return HTML markup for the grid view. The render function is called with
4572      * the following parameters:<ul>
4573      * <li>Data value.</li>
4574      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4575      * <li>css A CSS style string to apply to the table cell.</li>
4576      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4577      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4578      * <li>Row index</li>
4579      * <li>Column index</li>
4580      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4581      */
4582     setRenderer : function(col, fn){
4583         this.config[col].renderer = fn;
4584     },
4585
4586     /**
4587      * Returns the width for the specified column.
4588      * @param {Number} col The column index
4589      * @return {Number}
4590      */
4591     getColumnWidth : function(col){
4592         return this.config[col].width * 1 || this.defaultWidth;
4593     },
4594
4595     /**
4596      * Sets the width for a column.
4597      * @param {Number} col The column index
4598      * @param {Number} width The new width
4599      */
4600     setColumnWidth : function(col, width, suppressEvent){
4601         this.config[col].width = width;
4602         this.totalWidth = null;
4603         if(!suppressEvent){
4604              this.fireEvent("widthchange", this, col, width);
4605         }
4606     },
4607
4608     /**
4609      * Returns the total width of all columns.
4610      * @param {Boolean} includeHidden True to include hidden column widths
4611      * @return {Number}
4612      */
4613     getTotalWidth : function(includeHidden){
4614         if(!this.totalWidth){
4615             this.totalWidth = 0;
4616             for(var i = 0, len = this.config.length; i < len; i++){
4617                 if(includeHidden || !this.isHidden(i)){
4618                     this.totalWidth += this.getColumnWidth(i);
4619                 }
4620             }
4621         }
4622         return this.totalWidth;
4623     },
4624
4625     /**
4626      * Returns the header for the specified column.
4627      * @param {Number} col The column index
4628      * @return {String}
4629      */
4630     getColumnHeader : function(col){
4631         return this.config[col].header;
4632     },
4633
4634     /**
4635      * Sets the header for a column.
4636      * @param {Number} col The column index
4637      * @param {String} header The new header
4638      */
4639     setColumnHeader : function(col, header){
4640         this.config[col].header = header;
4641         this.fireEvent("headerchange", this, col, header);
4642     },
4643
4644     /**
4645      * Returns the tooltip for the specified column.
4646      * @param {Number} col The column index
4647      * @return {String}
4648      */
4649     getColumnTooltip : function(col){
4650             return this.config[col].tooltip;
4651     },
4652     /**
4653      * Sets the tooltip for a column.
4654      * @param {Number} col The column index
4655      * @param {String} tooltip The new tooltip
4656      */
4657     setColumnTooltip : function(col, tooltip){
4658             this.config[col].tooltip = tooltip;
4659     },
4660
4661     /**
4662      * Returns the dataIndex for the specified column.
4663      * @param {Number} col The column index
4664      * @return {Number}
4665      */
4666     getDataIndex : function(col){
4667         return this.config[col].dataIndex;
4668     },
4669
4670     /**
4671      * Sets the dataIndex for a column.
4672      * @param {Number} col The column index
4673      * @param {Number} dataIndex The new dataIndex
4674      */
4675     setDataIndex : function(col, dataIndex){
4676         this.config[col].dataIndex = dataIndex;
4677     },
4678
4679     
4680     
4681     /**
4682      * Returns true if the cell is editable.
4683      * @param {Number} colIndex The column index
4684      * @param {Number} rowIndex The row index
4685      * @return {Boolean}
4686      */
4687     isCellEditable : function(colIndex, rowIndex){
4688         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4689     },
4690
4691     /**
4692      * Returns the editor defined for the cell/column.
4693      * return false or null to disable editing.
4694      * @param {Number} colIndex The column index
4695      * @param {Number} rowIndex The row index
4696      * @return {Object}
4697      */
4698     getCellEditor : function(colIndex, rowIndex){
4699         return this.config[colIndex].editor;
4700     },
4701
4702     /**
4703      * Sets if a column is editable.
4704      * @param {Number} col The column index
4705      * @param {Boolean} editable True if the column is editable
4706      */
4707     setEditable : function(col, editable){
4708         this.config[col].editable = editable;
4709     },
4710
4711
4712     /**
4713      * Returns true if the column is hidden.
4714      * @param {Number} colIndex The column index
4715      * @return {Boolean}
4716      */
4717     isHidden : function(colIndex){
4718         return this.config[colIndex].hidden;
4719     },
4720
4721
4722     /**
4723      * Returns true if the column width cannot be changed
4724      */
4725     isFixed : function(colIndex){
4726         return this.config[colIndex].fixed;
4727     },
4728
4729     /**
4730      * Returns true if the column can be resized
4731      * @return {Boolean}
4732      */
4733     isResizable : function(colIndex){
4734         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4735     },
4736     /**
4737      * Sets if a column is hidden.
4738      * @param {Number} colIndex The column index
4739      * @param {Boolean} hidden True if the column is hidden
4740      */
4741     setHidden : function(colIndex, hidden){
4742         this.config[colIndex].hidden = hidden;
4743         this.totalWidth = null;
4744         this.fireEvent("hiddenchange", this, colIndex, hidden);
4745     },
4746
4747     /**
4748      * Sets the editor for a column.
4749      * @param {Number} col The column index
4750      * @param {Object} editor The editor object
4751      */
4752     setEditor : function(col, editor){
4753         this.config[col].editor = editor;
4754     }
4755 });
4756
4757 Roo.grid.ColumnModel.defaultRenderer = function(value){
4758         if(typeof value == "string" && value.length < 1){
4759             return "&#160;";
4760         }
4761         return value;
4762 };
4763
4764 // Alias for backwards compatibility
4765 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4766 /*
4767  * Based on:
4768  * Ext JS Library 1.1.1
4769  * Copyright(c) 2006-2007, Ext JS, LLC.
4770  *
4771  * Originally Released Under LGPL - original licence link has changed is not relivant.
4772  *
4773  * Fork - LGPL
4774  * <script type="text/javascript">
4775  */
4776  
4777 /**
4778  * @class Roo.LoadMask
4779  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4780  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4781  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4782  * element's UpdateManager load indicator and will be destroyed after the initial load.
4783  * @constructor
4784  * Create a new LoadMask
4785  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4786  * @param {Object} config The config object
4787  */
4788 Roo.LoadMask = function(el, config){
4789     this.el = Roo.get(el);
4790     Roo.apply(this, config);
4791     if(this.store){
4792         this.store.on('beforeload', this.onBeforeLoad, this);
4793         this.store.on('load', this.onLoad, this);
4794         this.store.on('loadexception', this.onLoadException, this);
4795         this.removeMask = false;
4796     }else{
4797         var um = this.el.getUpdateManager();
4798         um.showLoadIndicator = false; // disable the default indicator
4799         um.on('beforeupdate', this.onBeforeLoad, this);
4800         um.on('update', this.onLoad, this);
4801         um.on('failure', this.onLoad, this);
4802         this.removeMask = true;
4803     }
4804 };
4805
4806 Roo.LoadMask.prototype = {
4807     /**
4808      * @cfg {Boolean} removeMask
4809      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4810      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4811      */
4812     /**
4813      * @cfg {String} msg
4814      * The text to display in a centered loading message box (defaults to 'Loading...')
4815      */
4816     msg : 'Loading...',
4817     /**
4818      * @cfg {String} msgCls
4819      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4820      */
4821     msgCls : 'x-mask-loading',
4822
4823     /**
4824      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4825      * @type Boolean
4826      */
4827     disabled: false,
4828
4829     /**
4830      * Disables the mask to prevent it from being displayed
4831      */
4832     disable : function(){
4833        this.disabled = true;
4834     },
4835
4836     /**
4837      * Enables the mask so that it can be displayed
4838      */
4839     enable : function(){
4840         this.disabled = false;
4841     },
4842     
4843     onLoadException : function()
4844     {
4845         Roo.log(arguments);
4846         
4847         if (typeof(arguments[3]) != 'undefined') {
4848             Roo.MessageBox.alert("Error loading",arguments[3]);
4849         } 
4850         /*
4851         try {
4852             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4853                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4854             }   
4855         } catch(e) {
4856             
4857         }
4858         */
4859     
4860         
4861         
4862         this.el.unmask(this.removeMask);
4863     },
4864     // private
4865     onLoad : function()
4866     {
4867         this.el.unmask(this.removeMask);
4868     },
4869
4870     // private
4871     onBeforeLoad : function(){
4872         if(!this.disabled){
4873             this.el.mask(this.msg, this.msgCls);
4874         }
4875     },
4876
4877     // private
4878     destroy : function(){
4879         if(this.store){
4880             this.store.un('beforeload', this.onBeforeLoad, this);
4881             this.store.un('load', this.onLoad, this);
4882             this.store.un('loadexception', this.onLoadException, this);
4883         }else{
4884             var um = this.el.getUpdateManager();
4885             um.un('beforeupdate', this.onBeforeLoad, this);
4886             um.un('update', this.onLoad, this);
4887             um.un('failure', this.onLoad, this);
4888         }
4889     }
4890 };/*
4891  * - LGPL
4892  *
4893  * table
4894  * 
4895  */
4896
4897 /**
4898  * @class Roo.bootstrap.Table
4899  * @extends Roo.bootstrap.Component
4900  * Bootstrap Table class
4901  * @cfg {String} cls table class
4902  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4903  * @cfg {String} bgcolor Specifies the background color for a table
4904  * @cfg {Number} border Specifies whether the table cells should have borders or not
4905  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4906  * @cfg {Number} cellspacing Specifies the space between cells
4907  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4908  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4909  * @cfg {String} sortable Specifies that the table should be sortable
4910  * @cfg {String} summary Specifies a summary of the content of a table
4911  * @cfg {Number} width Specifies the width of a table
4912  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4913  * 
4914  * @cfg {boolean} striped Should the rows be alternative striped
4915  * @cfg {boolean} bordered Add borders to the table
4916  * @cfg {boolean} hover Add hover highlighting
4917  * @cfg {boolean} condensed Format condensed
4918  * @cfg {boolean} responsive Format condensed
4919  * @cfg {Boolean} loadMask (true|false) default false
4920  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4921  * @cfg {Boolean} thead (true|false) generate thead, default true
4922  * @cfg {Boolean} RowSelection (true|false) default false
4923  * @cfg {Boolean} CellSelection (true|false) default false
4924  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4925  
4926  * 
4927  * @constructor
4928  * Create a new Table
4929  * @param {Object} config The config object
4930  */
4931
4932 Roo.bootstrap.Table = function(config){
4933     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4934     
4935     if (this.sm) {
4936         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4937         this.sm = this.selModel;
4938         this.sm.xmodule = this.xmodule || false;
4939     }
4940     if (this.cm && typeof(this.cm.config) == 'undefined') {
4941         this.colModel = new Roo.grid.ColumnModel(this.cm);
4942         this.cm = this.colModel;
4943         this.cm.xmodule = this.xmodule || false;
4944     }
4945     if (this.store) {
4946         this.store= Roo.factory(this.store, Roo.data);
4947         this.ds = this.store;
4948         this.ds.xmodule = this.xmodule || false;
4949          
4950     }
4951     if (this.footer && this.store) {
4952         this.footer.dataSource = this.ds;
4953         this.footer = Roo.factory(this.footer);
4954     }
4955     
4956     /** @private */
4957     this.addEvents({
4958         /**
4959          * @event cellclick
4960          * Fires when a cell is clicked
4961          * @param {Roo.bootstrap.Table} this
4962          * @param {Roo.Element} el
4963          * @param {Number} rowIndex
4964          * @param {Number} columnIndex
4965          * @param {Roo.EventObject} e
4966          */
4967         "cellclick" : true,
4968         /**
4969          * @event celldblclick
4970          * Fires when a cell is double clicked
4971          * @param {Roo.bootstrap.Table} this
4972          * @param {Roo.Element} el
4973          * @param {Number} rowIndex
4974          * @param {Number} columnIndex
4975          * @param {Roo.EventObject} e
4976          */
4977         "celldblclick" : true,
4978         /**
4979          * @event rowclick
4980          * Fires when a row is clicked
4981          * @param {Roo.bootstrap.Table} this
4982          * @param {Roo.Element} el
4983          * @param {Number} rowIndex
4984          * @param {Roo.EventObject} e
4985          */
4986         "rowclick" : true,
4987         /**
4988          * @event rowdblclick
4989          * Fires when a row is double clicked
4990          * @param {Roo.bootstrap.Table} this
4991          * @param {Roo.Element} el
4992          * @param {Number} rowIndex
4993          * @param {Roo.EventObject} e
4994          */
4995         "rowdblclick" : true,
4996         /**
4997          * @event mouseover
4998          * Fires when a mouseover occur
4999          * @param {Roo.bootstrap.Table} this
5000          * @param {Roo.Element} el
5001          * @param {Number} rowIndex
5002          * @param {Number} columnIndex
5003          * @param {Roo.EventObject} e
5004          */
5005         "mouseover" : true,
5006         /**
5007          * @event mouseout
5008          * Fires when a mouseout occur
5009          * @param {Roo.bootstrap.Table} this
5010          * @param {Roo.Element} el
5011          * @param {Number} rowIndex
5012          * @param {Number} columnIndex
5013          * @param {Roo.EventObject} e
5014          */
5015         "mouseout" : true,
5016         /**
5017          * @event rowclass
5018          * Fires when a row is rendered, so you can change add a style to it.
5019          * @param {Roo.bootstrap.Table} this
5020          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5021          */
5022         'rowclass' : true
5023         
5024     });
5025 };
5026
5027 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5028     
5029     cls: false,
5030     align: false,
5031     bgcolor: false,
5032     border: false,
5033     cellpadding: false,
5034     cellspacing: false,
5035     frame: false,
5036     rules: false,
5037     sortable: false,
5038     summary: false,
5039     width: false,
5040     striped : false,
5041     bordered: false,
5042     hover:  false,
5043     condensed : false,
5044     responsive : false,
5045     sm : false,
5046     cm : false,
5047     store : false,
5048     loadMask : false,
5049     tfoot : true,
5050     thead : true,
5051     RowSelection : false,
5052     CellSelection : false,
5053     layout : false,
5054     
5055     // Roo.Element - the tbody
5056     mainBody: false, 
5057     
5058     getAutoCreate : function(){
5059         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5060         
5061         cfg = {
5062             tag: 'table',
5063             cls : 'table',
5064             cn : []
5065         }
5066             
5067         if (this.striped) {
5068             cfg.cls += ' table-striped';
5069         }
5070         
5071         if (this.hover) {
5072             cfg.cls += ' table-hover';
5073         }
5074         if (this.bordered) {
5075             cfg.cls += ' table-bordered';
5076         }
5077         if (this.condensed) {
5078             cfg.cls += ' table-condensed';
5079         }
5080         if (this.responsive) {
5081             cfg.cls += ' table-responsive';
5082         }
5083         
5084         if (this.cls) {
5085             cfg.cls+=  ' ' +this.cls;
5086         }
5087         
5088         // this lot should be simplifed...
5089         
5090         if (this.align) {
5091             cfg.align=this.align;
5092         }
5093         if (this.bgcolor) {
5094             cfg.bgcolor=this.bgcolor;
5095         }
5096         if (this.border) {
5097             cfg.border=this.border;
5098         }
5099         if (this.cellpadding) {
5100             cfg.cellpadding=this.cellpadding;
5101         }
5102         if (this.cellspacing) {
5103             cfg.cellspacing=this.cellspacing;
5104         }
5105         if (this.frame) {
5106             cfg.frame=this.frame;
5107         }
5108         if (this.rules) {
5109             cfg.rules=this.rules;
5110         }
5111         if (this.sortable) {
5112             cfg.sortable=this.sortable;
5113         }
5114         if (this.summary) {
5115             cfg.summary=this.summary;
5116         }
5117         if (this.width) {
5118             cfg.width=this.width;
5119         }
5120         if (this.layout) {
5121             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5122         }
5123         
5124         if(this.store || this.cm){
5125             if(this.thead){
5126                 cfg.cn.push(this.renderHeader());
5127             }
5128             
5129             cfg.cn.push(this.renderBody());
5130             
5131             if(this.tfoot){
5132                 cfg.cn.push(this.renderFooter());
5133             }
5134             
5135             cfg.cls+=  ' TableGrid';
5136         }
5137         
5138         return { cn : [ cfg ] };
5139     },
5140     
5141     initEvents : function()
5142     {   
5143         if(!this.store || !this.cm){
5144             return;
5145         }
5146         
5147         //Roo.log('initEvents with ds!!!!');
5148         
5149         this.mainBody = this.el.select('tbody', true).first();
5150         
5151         
5152         var _this = this;
5153         
5154         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5155             e.on('click', _this.sort, _this);
5156         });
5157         
5158         this.el.on("click", this.onClick, this);
5159         this.el.on("dblclick", this.onDblClick, this);
5160         
5161         this.parent().el.setStyle('position', 'relative');
5162         if (this.footer) {
5163             this.footer.parentId = this.id;
5164             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5165         }
5166         
5167         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5168         
5169         this.store.on('load', this.onLoad, this);
5170         this.store.on('beforeload', this.onBeforeLoad, this);
5171         this.store.on('update', this.onUpdate, this);
5172         
5173     },
5174     
5175     onMouseover : function(e, el)
5176     {
5177         var cell = Roo.get(el);
5178         
5179         if(!cell){
5180             return;
5181         }
5182         
5183         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5184             cell = cell.findParent('td', false, true);
5185         }
5186         
5187         var row = cell.findParent('tr', false, true);
5188         var cellIndex = cell.dom.cellIndex;
5189         var rowIndex = row.dom.rowIndex - 1; // start from 0
5190         
5191         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5192         
5193     },
5194     
5195     onMouseout : function(e, el)
5196     {
5197         var cell = Roo.get(el);
5198         
5199         if(!cell){
5200             return;
5201         }
5202         
5203         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5204             cell = cell.findParent('td', false, true);
5205         }
5206         
5207         var row = cell.findParent('tr', false, true);
5208         var cellIndex = cell.dom.cellIndex;
5209         var rowIndex = row.dom.rowIndex - 1; // start from 0
5210         
5211         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5212         
5213     },
5214     
5215     onClick : function(e, el)
5216     {
5217         var cell = Roo.get(el);
5218         
5219         if(!cell || (!this.CellSelection && !this.RowSelection)){
5220             return;
5221         }
5222         
5223         
5224         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5225             cell = cell.findParent('td', false, true);
5226         }
5227         
5228         var row = cell.findParent('tr', false, true);
5229         var cellIndex = cell.dom.cellIndex;
5230         var rowIndex = row.dom.rowIndex - 1;
5231         
5232         if(this.CellSelection){
5233             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5234         }
5235         
5236         if(this.RowSelection){
5237             this.fireEvent('rowclick', this, row, rowIndex, e);
5238         }
5239         
5240         
5241     },
5242     
5243     onDblClick : function(e,el)
5244     {
5245         var cell = Roo.get(el);
5246         
5247         if(!cell || (!this.CellSelection && !this.RowSelection)){
5248             return;
5249         }
5250         
5251         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5252             cell = cell.findParent('td', false, true);
5253         }
5254         
5255         var row = cell.findParent('tr', false, true);
5256         var cellIndex = cell.dom.cellIndex;
5257         var rowIndex = row.dom.rowIndex - 1;
5258         
5259         if(this.CellSelection){
5260             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5261         }
5262         
5263         if(this.RowSelection){
5264             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5265         }
5266     },
5267     
5268     sort : function(e,el)
5269     {
5270         var col = Roo.get(el)
5271         
5272         if(!col.hasClass('sortable')){
5273             return;
5274         }
5275         
5276         var sort = col.attr('sort');
5277         var dir = 'ASC';
5278         
5279         if(col.hasClass('glyphicon-arrow-up')){
5280             dir = 'DESC';
5281         }
5282         
5283         this.store.sortInfo = {field : sort, direction : dir};
5284         
5285         if (this.footer) {
5286             Roo.log("calling footer first");
5287             this.footer.onClick('first');
5288         } else {
5289         
5290             this.store.load({ params : { start : 0 } });
5291         }
5292     },
5293     
5294     renderHeader : function()
5295     {
5296         var header = {
5297             tag: 'thead',
5298             cn : []
5299         };
5300         
5301         var cm = this.cm;
5302         
5303         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5304             
5305             var config = cm.config[i];
5306                     
5307             var c = {
5308                 tag: 'th',
5309                 style : '',
5310                 html: cm.getColumnHeader(i)
5311             };
5312             
5313             if(typeof(config.hidden) != 'undefined' && config.hidden){
5314                 c.style += ' display:none;';
5315             }
5316             
5317             if(typeof(config.dataIndex) != 'undefined'){
5318                 c.sort = config.dataIndex;
5319             }
5320             
5321             if(typeof(config.sortable) != 'undefined' && config.sortable){
5322                 c.cls = 'sortable';
5323             }
5324             
5325             if(typeof(config.align) != 'undefined' && config.align.length){
5326                 c.style += ' text-align:' + config.align + ';';
5327             }
5328             
5329             if(typeof(config.width) != 'undefined'){
5330                 c.style += ' width:' + config.width + 'px;';
5331             }
5332             
5333             header.cn.push(c)
5334         }
5335         
5336         return header;
5337     },
5338     
5339     renderBody : function()
5340     {
5341         var body = {
5342             tag: 'tbody',
5343             cn : [
5344                 {
5345                     tag: 'tr',
5346                     cn : [
5347                         {
5348                             tag : 'td',
5349                             colspan :  this.cm.getColumnCount()
5350                         }
5351                     ]
5352                 }
5353             ]
5354         };
5355         
5356         return body;
5357     },
5358     
5359     renderFooter : function()
5360     {
5361         var footer = {
5362             tag: 'tfoot',
5363             cn : [
5364                 {
5365                     tag: 'tr',
5366                     cn : [
5367                         {
5368                             tag : 'td',
5369                             colspan :  this.cm.getColumnCount()
5370                         }
5371                     ]
5372                 }
5373             ]
5374         };
5375         
5376         return footer;
5377     },
5378     
5379     
5380     
5381     onLoad : function()
5382     {
5383         Roo.log('ds onload');
5384         this.clear();
5385         
5386         var _this = this;
5387         var cm = this.cm;
5388         var ds = this.store;
5389         
5390         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5391             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5392             
5393             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5394                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5395             }
5396             
5397             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5398                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5399             }
5400         });
5401         
5402         var tbody =  this.mainBody;
5403               
5404         if(ds.getCount() > 0){
5405             ds.data.each(function(d,rowIndex){
5406                 var row =  this.renderRow(cm, ds, rowIndex);
5407                 
5408                 tbody.createChild(row);
5409                 
5410                 var _this = this;
5411                 
5412                 if(row.cellObjects.length){
5413                     Roo.each(row.cellObjects, function(r){
5414                         _this.renderCellObject(r);
5415                     })
5416                 }
5417                 
5418             }, this);
5419         }
5420         
5421         Roo.each(this.el.select('tbody td', true).elements, function(e){
5422             e.on('mouseover', _this.onMouseover, _this);
5423         });
5424         
5425         Roo.each(this.el.select('tbody td', true).elements, function(e){
5426             e.on('mouseout', _this.onMouseout, _this);
5427         });
5428
5429         //if(this.loadMask){
5430         //    this.maskEl.hide();
5431         //}
5432     },
5433     
5434     
5435     onUpdate : function(ds,record)
5436     {
5437         this.refreshRow(record);
5438     },
5439     onRemove : function(ds, record, index, isUpdate){
5440         if(isUpdate !== true){
5441             this.fireEvent("beforerowremoved", this, index, record);
5442         }
5443         var bt = this.mainBody.dom;
5444         if(bt.rows[index]){
5445             bt.removeChild(bt.rows[index]);
5446         }
5447         
5448         if(isUpdate !== true){
5449             //this.stripeRows(index);
5450             //this.syncRowHeights(index, index);
5451             //this.layout();
5452             this.fireEvent("rowremoved", this, index, record);
5453         }
5454     },
5455     
5456     
5457     refreshRow : function(record){
5458         var ds = this.store, index;
5459         if(typeof record == 'number'){
5460             index = record;
5461             record = ds.getAt(index);
5462         }else{
5463             index = ds.indexOf(record);
5464         }
5465         this.insertRow(ds, index, true);
5466         this.onRemove(ds, record, index+1, true);
5467         //this.syncRowHeights(index, index);
5468         //this.layout();
5469         this.fireEvent("rowupdated", this, index, record);
5470     },
5471     
5472     insertRow : function(dm, rowIndex, isUpdate){
5473         
5474         if(!isUpdate){
5475             this.fireEvent("beforerowsinserted", this, rowIndex);
5476         }
5477             //var s = this.getScrollState();
5478         var row = this.renderRow(this.cm, this.store, rowIndex);
5479         // insert before rowIndex..
5480         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5481         
5482         var _this = this;
5483                 
5484         if(row.cellObjects.length){
5485             Roo.each(row.cellObjects, function(r){
5486                 _this.renderCellObject(r);
5487             })
5488         }
5489             
5490         if(!isUpdate){
5491             this.fireEvent("rowsinserted", this, rowIndex);
5492             //this.syncRowHeights(firstRow, lastRow);
5493             //this.stripeRows(firstRow);
5494             //this.layout();
5495         }
5496         
5497     },
5498     
5499     
5500     getRowDom : function(rowIndex)
5501     {
5502         // not sure if I need to check this.. but let's do it anyway..
5503         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5504                 this.mainBody.dom.rows[rowIndex] : false
5505     },
5506     // returns the object tree for a tr..
5507   
5508     
5509     renderRow : function(cm, ds, rowIndex) {
5510         
5511         var d = ds.getAt(rowIndex);
5512         
5513         var row = {
5514             tag : 'tr',
5515             cn : []
5516         };
5517             
5518         var cellObjects = [];
5519         
5520         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5521             var config = cm.config[i];
5522             
5523             var renderer = cm.getRenderer(i);
5524             var value = '';
5525             var id = false;
5526             
5527             if(typeof(renderer) !== 'undefined'){
5528                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5529             }
5530             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5531             // and are rendered into the cells after the row is rendered - using the id for the element.
5532             
5533             if(typeof(value) === 'object'){
5534                 id = Roo.id();
5535                 cellObjects.push({
5536                     container : id,
5537                     cfg : value 
5538                 })
5539             }
5540             
5541             var rowcfg = {
5542                 record: d,
5543                 rowIndex : rowIndex,
5544                 colIndex : i,
5545                 rowClass : ''
5546             }
5547
5548             this.fireEvent('rowclass', this, rowcfg);
5549             
5550             var td = {
5551                 tag: 'td',
5552                 cls : rowcfg.rowClass,
5553                 style: '',
5554                 html: (typeof(value) === 'object') ? '' : value
5555             };
5556             
5557             if (id) {
5558                 td.id = id;
5559             }
5560             
5561             if(typeof(config.hidden) != 'undefined' && config.hidden){
5562                 td.style += ' display:none;';
5563             }
5564             
5565             if(typeof(config.align) != 'undefined' && config.align.length){
5566                 td.style += ' text-align:' + config.align + ';';
5567             }
5568             
5569             if(typeof(config.width) != 'undefined'){
5570                 td.style += ' width:' +  config.width + 'px;';
5571             }
5572             
5573             if(typeof(config.cursor) != 'undefined'){
5574                 td.style += ' cursor:' +  config.cursor + ';';
5575             }
5576              
5577             row.cn.push(td);
5578            
5579         }
5580         
5581         row.cellObjects = cellObjects;
5582         
5583         return row;
5584           
5585     },
5586     
5587     
5588     
5589     onBeforeLoad : function()
5590     {
5591         //Roo.log('ds onBeforeLoad');
5592         
5593         //this.clear();
5594         
5595         //if(this.loadMask){
5596         //    this.maskEl.show();
5597         //}
5598     },
5599     
5600     clear : function()
5601     {
5602         this.el.select('tbody', true).first().dom.innerHTML = '';
5603     },
5604     
5605     getSelectionModel : function(){
5606         if(!this.selModel){
5607             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5608         }
5609         return this.selModel;
5610     },
5611     /*
5612      * Render the Roo.bootstrap object from renderder
5613      */
5614     renderCellObject : function(r)
5615     {
5616         var _this = this;
5617         
5618         var t = r.cfg.render(r.container);
5619         
5620         if(r.cfg.cn){
5621             Roo.each(r.cfg.cn, function(c){
5622                 var child = {
5623                     container: t.getChildContainer(),
5624                     cfg: c
5625                 }
5626                 _this.renderCellObject(child);
5627             })
5628         }
5629     }
5630    
5631 });
5632
5633  
5634
5635  /*
5636  * - LGPL
5637  *
5638  * table cell
5639  * 
5640  */
5641
5642 /**
5643  * @class Roo.bootstrap.TableCell
5644  * @extends Roo.bootstrap.Component
5645  * Bootstrap TableCell class
5646  * @cfg {String} html cell contain text
5647  * @cfg {String} cls cell class
5648  * @cfg {String} tag cell tag (td|th) default td
5649  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5650  * @cfg {String} align Aligns the content in a cell
5651  * @cfg {String} axis Categorizes cells
5652  * @cfg {String} bgcolor Specifies the background color of a cell
5653  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5654  * @cfg {Number} colspan Specifies the number of columns a cell should span
5655  * @cfg {String} headers Specifies one or more header cells a cell is related to
5656  * @cfg {Number} height Sets the height of a cell
5657  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5658  * @cfg {Number} rowspan Sets the number of rows a cell should span
5659  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5660  * @cfg {String} valign Vertical aligns the content in a cell
5661  * @cfg {Number} width Specifies the width of a cell
5662  * 
5663  * @constructor
5664  * Create a new TableCell
5665  * @param {Object} config The config object
5666  */
5667
5668 Roo.bootstrap.TableCell = function(config){
5669     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5670 };
5671
5672 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5673     
5674     html: false,
5675     cls: false,
5676     tag: false,
5677     abbr: false,
5678     align: false,
5679     axis: false,
5680     bgcolor: false,
5681     charoff: false,
5682     colspan: false,
5683     headers: false,
5684     height: false,
5685     nowrap: false,
5686     rowspan: false,
5687     scope: false,
5688     valign: false,
5689     width: false,
5690     
5691     
5692     getAutoCreate : function(){
5693         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5694         
5695         cfg = {
5696             tag: 'td'
5697         }
5698         
5699         if(this.tag){
5700             cfg.tag = this.tag;
5701         }
5702         
5703         if (this.html) {
5704             cfg.html=this.html
5705         }
5706         if (this.cls) {
5707             cfg.cls=this.cls
5708         }
5709         if (this.abbr) {
5710             cfg.abbr=this.abbr
5711         }
5712         if (this.align) {
5713             cfg.align=this.align
5714         }
5715         if (this.axis) {
5716             cfg.axis=this.axis
5717         }
5718         if (this.bgcolor) {
5719             cfg.bgcolor=this.bgcolor
5720         }
5721         if (this.charoff) {
5722             cfg.charoff=this.charoff
5723         }
5724         if (this.colspan) {
5725             cfg.colspan=this.colspan
5726         }
5727         if (this.headers) {
5728             cfg.headers=this.headers
5729         }
5730         if (this.height) {
5731             cfg.height=this.height
5732         }
5733         if (this.nowrap) {
5734             cfg.nowrap=this.nowrap
5735         }
5736         if (this.rowspan) {
5737             cfg.rowspan=this.rowspan
5738         }
5739         if (this.scope) {
5740             cfg.scope=this.scope
5741         }
5742         if (this.valign) {
5743             cfg.valign=this.valign
5744         }
5745         if (this.width) {
5746             cfg.width=this.width
5747         }
5748         
5749         
5750         return cfg;
5751     }
5752    
5753 });
5754
5755  
5756
5757  /*
5758  * - LGPL
5759  *
5760  * table row
5761  * 
5762  */
5763
5764 /**
5765  * @class Roo.bootstrap.TableRow
5766  * @extends Roo.bootstrap.Component
5767  * Bootstrap TableRow class
5768  * @cfg {String} cls row class
5769  * @cfg {String} align Aligns the content in a table row
5770  * @cfg {String} bgcolor Specifies a background color for a table row
5771  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5772  * @cfg {String} valign Vertical aligns the content in a table row
5773  * 
5774  * @constructor
5775  * Create a new TableRow
5776  * @param {Object} config The config object
5777  */
5778
5779 Roo.bootstrap.TableRow = function(config){
5780     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5781 };
5782
5783 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5784     
5785     cls: false,
5786     align: false,
5787     bgcolor: false,
5788     charoff: false,
5789     valign: false,
5790     
5791     getAutoCreate : function(){
5792         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5793         
5794         cfg = {
5795             tag: 'tr'
5796         }
5797             
5798         if(this.cls){
5799             cfg.cls = this.cls;
5800         }
5801         if(this.align){
5802             cfg.align = this.align;
5803         }
5804         if(this.bgcolor){
5805             cfg.bgcolor = this.bgcolor;
5806         }
5807         if(this.charoff){
5808             cfg.charoff = this.charoff;
5809         }
5810         if(this.valign){
5811             cfg.valign = this.valign;
5812         }
5813         
5814         return cfg;
5815     }
5816    
5817 });
5818
5819  
5820
5821  /*
5822  * - LGPL
5823  *
5824  * table body
5825  * 
5826  */
5827
5828 /**
5829  * @class Roo.bootstrap.TableBody
5830  * @extends Roo.bootstrap.Component
5831  * Bootstrap TableBody class
5832  * @cfg {String} cls element class
5833  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5834  * @cfg {String} align Aligns the content inside the element
5835  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5836  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5837  * 
5838  * @constructor
5839  * Create a new TableBody
5840  * @param {Object} config The config object
5841  */
5842
5843 Roo.bootstrap.TableBody = function(config){
5844     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5845 };
5846
5847 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5848     
5849     cls: false,
5850     tag: false,
5851     align: false,
5852     charoff: false,
5853     valign: false,
5854     
5855     getAutoCreate : function(){
5856         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5857         
5858         cfg = {
5859             tag: 'tbody'
5860         }
5861             
5862         if (this.cls) {
5863             cfg.cls=this.cls
5864         }
5865         if(this.tag){
5866             cfg.tag = this.tag;
5867         }
5868         
5869         if(this.align){
5870             cfg.align = this.align;
5871         }
5872         if(this.charoff){
5873             cfg.charoff = this.charoff;
5874         }
5875         if(this.valign){
5876             cfg.valign = this.valign;
5877         }
5878         
5879         return cfg;
5880     }
5881     
5882     
5883 //    initEvents : function()
5884 //    {
5885 //        
5886 //        if(!this.store){
5887 //            return;
5888 //        }
5889 //        
5890 //        this.store = Roo.factory(this.store, Roo.data);
5891 //        this.store.on('load', this.onLoad, this);
5892 //        
5893 //        this.store.load();
5894 //        
5895 //    },
5896 //    
5897 //    onLoad: function () 
5898 //    {   
5899 //        this.fireEvent('load', this);
5900 //    }
5901 //    
5902 //   
5903 });
5904
5905  
5906
5907  /*
5908  * Based on:
5909  * Ext JS Library 1.1.1
5910  * Copyright(c) 2006-2007, Ext JS, LLC.
5911  *
5912  * Originally Released Under LGPL - original licence link has changed is not relivant.
5913  *
5914  * Fork - LGPL
5915  * <script type="text/javascript">
5916  */
5917
5918 // as we use this in bootstrap.
5919 Roo.namespace('Roo.form');
5920  /**
5921  * @class Roo.form.Action
5922  * Internal Class used to handle form actions
5923  * @constructor
5924  * @param {Roo.form.BasicForm} el The form element or its id
5925  * @param {Object} config Configuration options
5926  */
5927
5928  
5929  
5930 // define the action interface
5931 Roo.form.Action = function(form, options){
5932     this.form = form;
5933     this.options = options || {};
5934 };
5935 /**
5936  * Client Validation Failed
5937  * @const 
5938  */
5939 Roo.form.Action.CLIENT_INVALID = 'client';
5940 /**
5941  * Server Validation Failed
5942  * @const 
5943  */
5944 Roo.form.Action.SERVER_INVALID = 'server';
5945  /**
5946  * Connect to Server Failed
5947  * @const 
5948  */
5949 Roo.form.Action.CONNECT_FAILURE = 'connect';
5950 /**
5951  * Reading Data from Server Failed
5952  * @const 
5953  */
5954 Roo.form.Action.LOAD_FAILURE = 'load';
5955
5956 Roo.form.Action.prototype = {
5957     type : 'default',
5958     failureType : undefined,
5959     response : undefined,
5960     result : undefined,
5961
5962     // interface method
5963     run : function(options){
5964
5965     },
5966
5967     // interface method
5968     success : function(response){
5969
5970     },
5971
5972     // interface method
5973     handleResponse : function(response){
5974
5975     },
5976
5977     // default connection failure
5978     failure : function(response){
5979         
5980         this.response = response;
5981         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5982         this.form.afterAction(this, false);
5983     },
5984
5985     processResponse : function(response){
5986         this.response = response;
5987         if(!response.responseText){
5988             return true;
5989         }
5990         this.result = this.handleResponse(response);
5991         return this.result;
5992     },
5993
5994     // utility functions used internally
5995     getUrl : function(appendParams){
5996         var url = this.options.url || this.form.url || this.form.el.dom.action;
5997         if(appendParams){
5998             var p = this.getParams();
5999             if(p){
6000                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6001             }
6002         }
6003         return url;
6004     },
6005
6006     getMethod : function(){
6007         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6008     },
6009
6010     getParams : function(){
6011         var bp = this.form.baseParams;
6012         var p = this.options.params;
6013         if(p){
6014             if(typeof p == "object"){
6015                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6016             }else if(typeof p == 'string' && bp){
6017                 p += '&' + Roo.urlEncode(bp);
6018             }
6019         }else if(bp){
6020             p = Roo.urlEncode(bp);
6021         }
6022         return p;
6023     },
6024
6025     createCallback : function(){
6026         return {
6027             success: this.success,
6028             failure: this.failure,
6029             scope: this,
6030             timeout: (this.form.timeout*1000),
6031             upload: this.form.fileUpload ? this.success : undefined
6032         };
6033     }
6034 };
6035
6036 Roo.form.Action.Submit = function(form, options){
6037     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6038 };
6039
6040 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6041     type : 'submit',
6042
6043     haveProgress : false,
6044     uploadComplete : false,
6045     
6046     // uploadProgress indicator.
6047     uploadProgress : function()
6048     {
6049         if (!this.form.progressUrl) {
6050             return;
6051         }
6052         
6053         if (!this.haveProgress) {
6054             Roo.MessageBox.progress("Uploading", "Uploading");
6055         }
6056         if (this.uploadComplete) {
6057            Roo.MessageBox.hide();
6058            return;
6059         }
6060         
6061         this.haveProgress = true;
6062    
6063         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6064         
6065         var c = new Roo.data.Connection();
6066         c.request({
6067             url : this.form.progressUrl,
6068             params: {
6069                 id : uid
6070             },
6071             method: 'GET',
6072             success : function(req){
6073                //console.log(data);
6074                 var rdata = false;
6075                 var edata;
6076                 try  {
6077                    rdata = Roo.decode(req.responseText)
6078                 } catch (e) {
6079                     Roo.log("Invalid data from server..");
6080                     Roo.log(edata);
6081                     return;
6082                 }
6083                 if (!rdata || !rdata.success) {
6084                     Roo.log(rdata);
6085                     Roo.MessageBox.alert(Roo.encode(rdata));
6086                     return;
6087                 }
6088                 var data = rdata.data;
6089                 
6090                 if (this.uploadComplete) {
6091                    Roo.MessageBox.hide();
6092                    return;
6093                 }
6094                    
6095                 if (data){
6096                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6097                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6098                     );
6099                 }
6100                 this.uploadProgress.defer(2000,this);
6101             },
6102        
6103             failure: function(data) {
6104                 Roo.log('progress url failed ');
6105                 Roo.log(data);
6106             },
6107             scope : this
6108         });
6109            
6110     },
6111     
6112     
6113     run : function()
6114     {
6115         // run get Values on the form, so it syncs any secondary forms.
6116         this.form.getValues();
6117         
6118         var o = this.options;
6119         var method = this.getMethod();
6120         var isPost = method == 'POST';
6121         if(o.clientValidation === false || this.form.isValid()){
6122             
6123             if (this.form.progressUrl) {
6124                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6125                     (new Date() * 1) + '' + Math.random());
6126                     
6127             } 
6128             
6129             
6130             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6131                 form:this.form.el.dom,
6132                 url:this.getUrl(!isPost),
6133                 method: method,
6134                 params:isPost ? this.getParams() : null,
6135                 isUpload: this.form.fileUpload
6136             }));
6137             
6138             this.uploadProgress();
6139
6140         }else if (o.clientValidation !== false){ // client validation failed
6141             this.failureType = Roo.form.Action.CLIENT_INVALID;
6142             this.form.afterAction(this, false);
6143         }
6144     },
6145
6146     success : function(response)
6147     {
6148         this.uploadComplete= true;
6149         if (this.haveProgress) {
6150             Roo.MessageBox.hide();
6151         }
6152         
6153         
6154         var result = this.processResponse(response);
6155         if(result === true || result.success){
6156             this.form.afterAction(this, true);
6157             return;
6158         }
6159         if(result.errors){
6160             this.form.markInvalid(result.errors);
6161             this.failureType = Roo.form.Action.SERVER_INVALID;
6162         }
6163         this.form.afterAction(this, false);
6164     },
6165     failure : function(response)
6166     {
6167         this.uploadComplete= true;
6168         if (this.haveProgress) {
6169             Roo.MessageBox.hide();
6170         }
6171         
6172         this.response = response;
6173         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6174         this.form.afterAction(this, false);
6175     },
6176     
6177     handleResponse : function(response){
6178         if(this.form.errorReader){
6179             var rs = this.form.errorReader.read(response);
6180             var errors = [];
6181             if(rs.records){
6182                 for(var i = 0, len = rs.records.length; i < len; i++) {
6183                     var r = rs.records[i];
6184                     errors[i] = r.data;
6185                 }
6186             }
6187             if(errors.length < 1){
6188                 errors = null;
6189             }
6190             return {
6191                 success : rs.success,
6192                 errors : errors
6193             };
6194         }
6195         var ret = false;
6196         try {
6197             ret = Roo.decode(response.responseText);
6198         } catch (e) {
6199             ret = {
6200                 success: false,
6201                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6202                 errors : []
6203             };
6204         }
6205         return ret;
6206         
6207     }
6208 });
6209
6210
6211 Roo.form.Action.Load = function(form, options){
6212     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6213     this.reader = this.form.reader;
6214 };
6215
6216 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6217     type : 'load',
6218
6219     run : function(){
6220         
6221         Roo.Ajax.request(Roo.apply(
6222                 this.createCallback(), {
6223                     method:this.getMethod(),
6224                     url:this.getUrl(false),
6225                     params:this.getParams()
6226         }));
6227     },
6228
6229     success : function(response){
6230         
6231         var result = this.processResponse(response);
6232         if(result === true || !result.success || !result.data){
6233             this.failureType = Roo.form.Action.LOAD_FAILURE;
6234             this.form.afterAction(this, false);
6235             return;
6236         }
6237         this.form.clearInvalid();
6238         this.form.setValues(result.data);
6239         this.form.afterAction(this, true);
6240     },
6241
6242     handleResponse : function(response){
6243         if(this.form.reader){
6244             var rs = this.form.reader.read(response);
6245             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6246             return {
6247                 success : rs.success,
6248                 data : data
6249             };
6250         }
6251         return Roo.decode(response.responseText);
6252     }
6253 });
6254
6255 Roo.form.Action.ACTION_TYPES = {
6256     'load' : Roo.form.Action.Load,
6257     'submit' : Roo.form.Action.Submit
6258 };/*
6259  * - LGPL
6260  *
6261  * form
6262  * 
6263  */
6264
6265 /**
6266  * @class Roo.bootstrap.Form
6267  * @extends Roo.bootstrap.Component
6268  * Bootstrap Form class
6269  * @cfg {String} method  GET | POST (default POST)
6270  * @cfg {String} labelAlign top | left (default top)
6271  * @cfg {String} align left  | right - for navbars
6272  * @cfg {Boolean} loadMask load mask when submit (default true)
6273
6274  * 
6275  * @constructor
6276  * Create a new Form
6277  * @param {Object} config The config object
6278  */
6279
6280
6281 Roo.bootstrap.Form = function(config){
6282     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6283     this.addEvents({
6284         /**
6285          * @event clientvalidation
6286          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6287          * @param {Form} this
6288          * @param {Boolean} valid true if the form has passed client-side validation
6289          */
6290         clientvalidation: true,
6291         /**
6292          * @event beforeaction
6293          * Fires before any action is performed. Return false to cancel the action.
6294          * @param {Form} this
6295          * @param {Action} action The action to be performed
6296          */
6297         beforeaction: true,
6298         /**
6299          * @event actionfailed
6300          * Fires when an action fails.
6301          * @param {Form} this
6302          * @param {Action} action The action that failed
6303          */
6304         actionfailed : true,
6305         /**
6306          * @event actioncomplete
6307          * Fires when an action is completed.
6308          * @param {Form} this
6309          * @param {Action} action The action that completed
6310          */
6311         actioncomplete : true
6312     });
6313     
6314 };
6315
6316 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6317       
6318      /**
6319      * @cfg {String} method
6320      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6321      */
6322     method : 'POST',
6323     /**
6324      * @cfg {String} url
6325      * The URL to use for form actions if one isn't supplied in the action options.
6326      */
6327     /**
6328      * @cfg {Boolean} fileUpload
6329      * Set to true if this form is a file upload.
6330      */
6331      
6332     /**
6333      * @cfg {Object} baseParams
6334      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6335      */
6336       
6337     /**
6338      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6339      */
6340     timeout: 30,
6341     /**
6342      * @cfg {Sting} align (left|right) for navbar forms
6343      */
6344     align : 'left',
6345
6346     // private
6347     activeAction : null,
6348  
6349     /**
6350      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6351      * element by passing it or its id or mask the form itself by passing in true.
6352      * @type Mixed
6353      */
6354     waitMsgTarget : false,
6355     
6356     loadMask : true,
6357     
6358     getAutoCreate : function(){
6359         
6360         var cfg = {
6361             tag: 'form',
6362             method : this.method || 'POST',
6363             id : this.id || Roo.id(),
6364             cls : ''
6365         }
6366         if (this.parent().xtype.match(/^Nav/)) {
6367             cfg.cls = 'navbar-form navbar-' + this.align;
6368             
6369         }
6370         
6371         if (this.labelAlign == 'left' ) {
6372             cfg.cls += ' form-horizontal';
6373         }
6374         
6375         
6376         return cfg;
6377     },
6378     initEvents : function()
6379     {
6380         this.el.on('submit', this.onSubmit, this);
6381         // this was added as random key presses on the form where triggering form submit.
6382         this.el.on('keypress', function(e) {
6383             if (e.getCharCode() != 13) {
6384                 return true;
6385             }
6386             // we might need to allow it for textareas.. and some other items.
6387             // check e.getTarget().
6388             
6389             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6390                 return true;
6391             }
6392         
6393             Roo.log("keypress blocked");
6394             
6395             e.preventDefault();
6396             return false;
6397         });
6398         
6399     },
6400     // private
6401     onSubmit : function(e){
6402         e.stopEvent();
6403     },
6404     
6405      /**
6406      * Returns true if client-side validation on the form is successful.
6407      * @return Boolean
6408      */
6409     isValid : function(){
6410         var items = this.getItems();
6411         var valid = true;
6412         items.each(function(f){
6413            if(!f.validate()){
6414                valid = false;
6415                
6416            }
6417         });
6418         return valid;
6419     },
6420     /**
6421      * Returns true if any fields in this form have changed since their original load.
6422      * @return Boolean
6423      */
6424     isDirty : function(){
6425         var dirty = false;
6426         var items = this.getItems();
6427         items.each(function(f){
6428            if(f.isDirty()){
6429                dirty = true;
6430                return false;
6431            }
6432            return true;
6433         });
6434         return dirty;
6435     },
6436      /**
6437      * Performs a predefined action (submit or load) or custom actions you define on this form.
6438      * @param {String} actionName The name of the action type
6439      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6440      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6441      * accept other config options):
6442      * <pre>
6443 Property          Type             Description
6444 ----------------  ---------------  ----------------------------------------------------------------------------------
6445 url               String           The url for the action (defaults to the form's url)
6446 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6447 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6448 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6449                                    validate the form on the client (defaults to false)
6450      * </pre>
6451      * @return {BasicForm} this
6452      */
6453     doAction : function(action, options){
6454         if(typeof action == 'string'){
6455             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6456         }
6457         if(this.fireEvent('beforeaction', this, action) !== false){
6458             this.beforeAction(action);
6459             action.run.defer(100, action);
6460         }
6461         return this;
6462     },
6463     
6464     // private
6465     beforeAction : function(action){
6466         var o = action.options;
6467         
6468         if(this.loadMask){
6469             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6470         }
6471         // not really supported yet.. ??
6472         
6473         //if(this.waitMsgTarget === true){
6474         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6475         //}else if(this.waitMsgTarget){
6476         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6477         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6478         //}else {
6479         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6480        // }
6481          
6482     },
6483
6484     // private
6485     afterAction : function(action, success){
6486         this.activeAction = null;
6487         var o = action.options;
6488         
6489         //if(this.waitMsgTarget === true){
6490             this.el.unmask();
6491         //}else if(this.waitMsgTarget){
6492         //    this.waitMsgTarget.unmask();
6493         //}else{
6494         //    Roo.MessageBox.updateProgress(1);
6495         //    Roo.MessageBox.hide();
6496        // }
6497         // 
6498         if(success){
6499             if(o.reset){
6500                 this.reset();
6501             }
6502             Roo.callback(o.success, o.scope, [this, action]);
6503             this.fireEvent('actioncomplete', this, action);
6504             
6505         }else{
6506             
6507             // failure condition..
6508             // we have a scenario where updates need confirming.
6509             // eg. if a locking scenario exists..
6510             // we look for { errors : { needs_confirm : true }} in the response.
6511             if (
6512                 (typeof(action.result) != 'undefined')  &&
6513                 (typeof(action.result.errors) != 'undefined')  &&
6514                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6515            ){
6516                 var _t = this;
6517                 Roo.log("not supported yet");
6518                  /*
6519                 
6520                 Roo.MessageBox.confirm(
6521                     "Change requires confirmation",
6522                     action.result.errorMsg,
6523                     function(r) {
6524                         if (r != 'yes') {
6525                             return;
6526                         }
6527                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6528                     }
6529                     
6530                 );
6531                 */
6532                 
6533                 
6534                 return;
6535             }
6536             
6537             Roo.callback(o.failure, o.scope, [this, action]);
6538             // show an error message if no failed handler is set..
6539             if (!this.hasListener('actionfailed')) {
6540                 Roo.log("need to add dialog support");
6541                 /*
6542                 Roo.MessageBox.alert("Error",
6543                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6544                         action.result.errorMsg :
6545                         "Saving Failed, please check your entries or try again"
6546                 );
6547                 */
6548             }
6549             
6550             this.fireEvent('actionfailed', this, action);
6551         }
6552         
6553     },
6554     /**
6555      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6556      * @param {String} id The value to search for
6557      * @return Field
6558      */
6559     findField : function(id){
6560         var items = this.getItems();
6561         var field = items.get(id);
6562         if(!field){
6563              items.each(function(f){
6564                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6565                     field = f;
6566                     return false;
6567                 }
6568                 return true;
6569             });
6570         }
6571         return field || null;
6572     },
6573      /**
6574      * Mark fields in this form invalid in bulk.
6575      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6576      * @return {BasicForm} this
6577      */
6578     markInvalid : function(errors){
6579         if(errors instanceof Array){
6580             for(var i = 0, len = errors.length; i < len; i++){
6581                 var fieldError = errors[i];
6582                 var f = this.findField(fieldError.id);
6583                 if(f){
6584                     f.markInvalid(fieldError.msg);
6585                 }
6586             }
6587         }else{
6588             var field, id;
6589             for(id in errors){
6590                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6591                     field.markInvalid(errors[id]);
6592                 }
6593             }
6594         }
6595         //Roo.each(this.childForms || [], function (f) {
6596         //    f.markInvalid(errors);
6597         //});
6598         
6599         return this;
6600     },
6601
6602     /**
6603      * Set values for fields in this form in bulk.
6604      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6605      * @return {BasicForm} this
6606      */
6607     setValues : function(values){
6608         if(values instanceof Array){ // array of objects
6609             for(var i = 0, len = values.length; i < len; i++){
6610                 var v = values[i];
6611                 var f = this.findField(v.id);
6612                 if(f){
6613                     f.setValue(v.value);
6614                     if(this.trackResetOnLoad){
6615                         f.originalValue = f.getValue();
6616                     }
6617                 }
6618             }
6619         }else{ // object hash
6620             var field, id;
6621             for(id in values){
6622                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6623                     
6624                     if (field.setFromData && 
6625                         field.valueField && 
6626                         field.displayField &&
6627                         // combos' with local stores can 
6628                         // be queried via setValue()
6629                         // to set their value..
6630                         (field.store && !field.store.isLocal)
6631                         ) {
6632                         // it's a combo
6633                         var sd = { };
6634                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6635                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6636                         field.setFromData(sd);
6637                         
6638                     } else {
6639                         field.setValue(values[id]);
6640                     }
6641                     
6642                     
6643                     if(this.trackResetOnLoad){
6644                         field.originalValue = field.getValue();
6645                     }
6646                 }
6647             }
6648         }
6649          
6650         //Roo.each(this.childForms || [], function (f) {
6651         //    f.setValues(values);
6652         //});
6653                 
6654         return this;
6655     },
6656
6657     /**
6658      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6659      * they are returned as an array.
6660      * @param {Boolean} asString
6661      * @return {Object}
6662      */
6663     getValues : function(asString){
6664         //if (this.childForms) {
6665             // copy values from the child forms
6666         //    Roo.each(this.childForms, function (f) {
6667         //        this.setValues(f.getValues());
6668         //    }, this);
6669         //}
6670         
6671         
6672         
6673         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6674         if(asString === true){
6675             return fs;
6676         }
6677         return Roo.urlDecode(fs);
6678     },
6679     
6680     /**
6681      * Returns the fields in this form as an object with key/value pairs. 
6682      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6683      * @return {Object}
6684      */
6685     getFieldValues : function(with_hidden)
6686     {
6687         var items = this.getItems();
6688         var ret = {};
6689         items.each(function(f){
6690             if (!f.getName()) {
6691                 return;
6692             }
6693             var v = f.getValue();
6694             if (f.inputType =='radio') {
6695                 if (typeof(ret[f.getName()]) == 'undefined') {
6696                     ret[f.getName()] = ''; // empty..
6697                 }
6698                 
6699                 if (!f.el.dom.checked) {
6700                     return;
6701                     
6702                 }
6703                 v = f.el.dom.value;
6704                 
6705             }
6706             
6707             // not sure if this supported any more..
6708             if ((typeof(v) == 'object') && f.getRawValue) {
6709                 v = f.getRawValue() ; // dates..
6710             }
6711             // combo boxes where name != hiddenName...
6712             if (f.name != f.getName()) {
6713                 ret[f.name] = f.getRawValue();
6714             }
6715             ret[f.getName()] = v;
6716         });
6717         
6718         return ret;
6719     },
6720
6721     /**
6722      * Clears all invalid messages in this form.
6723      * @return {BasicForm} this
6724      */
6725     clearInvalid : function(){
6726         var items = this.getItems();
6727         
6728         items.each(function(f){
6729            f.clearInvalid();
6730         });
6731         
6732         
6733         
6734         return this;
6735     },
6736
6737     /**
6738      * Resets this form.
6739      * @return {BasicForm} this
6740      */
6741     reset : function(){
6742         var items = this.getItems();
6743         items.each(function(f){
6744             f.reset();
6745         });
6746         
6747         Roo.each(this.childForms || [], function (f) {
6748             f.reset();
6749         });
6750        
6751         
6752         return this;
6753     },
6754     getItems : function()
6755     {
6756         var r=new Roo.util.MixedCollection(false, function(o){
6757             return o.id || (o.id = Roo.id());
6758         });
6759         var iter = function(el) {
6760             if (el.inputEl) {
6761                 r.add(el);
6762             }
6763             if (!el.items) {
6764                 return;
6765             }
6766             Roo.each(el.items,function(e) {
6767                 iter(e);
6768             });
6769             
6770             
6771         };
6772         iter(this);
6773         return r;
6774         
6775         
6776         
6777         
6778     }
6779     
6780 });
6781
6782  
6783 /*
6784  * Based on:
6785  * Ext JS Library 1.1.1
6786  * Copyright(c) 2006-2007, Ext JS, LLC.
6787  *
6788  * Originally Released Under LGPL - original licence link has changed is not relivant.
6789  *
6790  * Fork - LGPL
6791  * <script type="text/javascript">
6792  */
6793 /**
6794  * @class Roo.form.VTypes
6795  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6796  * @singleton
6797  */
6798 Roo.form.VTypes = function(){
6799     // closure these in so they are only created once.
6800     var alpha = /^[a-zA-Z_]+$/;
6801     var alphanum = /^[a-zA-Z0-9_]+$/;
6802     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6803     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6804
6805     // All these messages and functions are configurable
6806     return {
6807         /**
6808          * The function used to validate email addresses
6809          * @param {String} value The email address
6810          */
6811         'email' : function(v){
6812             return email.test(v);
6813         },
6814         /**
6815          * The error text to display when the email validation function returns false
6816          * @type String
6817          */
6818         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6819         /**
6820          * The keystroke filter mask to be applied on email input
6821          * @type RegExp
6822          */
6823         'emailMask' : /[a-z0-9_\.\-@]/i,
6824
6825         /**
6826          * The function used to validate URLs
6827          * @param {String} value The URL
6828          */
6829         'url' : function(v){
6830             return url.test(v);
6831         },
6832         /**
6833          * The error text to display when the url validation function returns false
6834          * @type String
6835          */
6836         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6837         
6838         /**
6839          * The function used to validate alpha values
6840          * @param {String} value The value
6841          */
6842         'alpha' : function(v){
6843             return alpha.test(v);
6844         },
6845         /**
6846          * The error text to display when the alpha validation function returns false
6847          * @type String
6848          */
6849         'alphaText' : 'This field should only contain letters and _',
6850         /**
6851          * The keystroke filter mask to be applied on alpha input
6852          * @type RegExp
6853          */
6854         'alphaMask' : /[a-z_]/i,
6855
6856         /**
6857          * The function used to validate alphanumeric values
6858          * @param {String} value The value
6859          */
6860         'alphanum' : function(v){
6861             return alphanum.test(v);
6862         },
6863         /**
6864          * The error text to display when the alphanumeric validation function returns false
6865          * @type String
6866          */
6867         'alphanumText' : 'This field should only contain letters, numbers and _',
6868         /**
6869          * The keystroke filter mask to be applied on alphanumeric input
6870          * @type RegExp
6871          */
6872         'alphanumMask' : /[a-z0-9_]/i
6873     };
6874 }();/*
6875  * - LGPL
6876  *
6877  * Input
6878  * 
6879  */
6880
6881 /**
6882  * @class Roo.bootstrap.Input
6883  * @extends Roo.bootstrap.Component
6884  * Bootstrap Input class
6885  * @cfg {Boolean} disabled is it disabled
6886  * @cfg {String} fieldLabel - the label associated
6887  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6888  * @cfg {String} name name of the input
6889  * @cfg {string} fieldLabel - the label associated
6890  * @cfg {string}  inputType - input / file submit ...
6891  * @cfg {string} placeholder - placeholder to put in text.
6892  * @cfg {string}  before - input group add on before
6893  * @cfg {string} after - input group add on after
6894  * @cfg {string} size - (lg|sm) or leave empty..
6895  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6896  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6897  * @cfg {Number} md colspan out of 12 for computer-sized screens
6898  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6899  * @cfg {string} value default value of the input
6900  * @cfg {Number} labelWidth set the width of label (0-12)
6901  * @cfg {String} labelAlign (top|left)
6902  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6903  * @cfg {String} align (left|center|right) Default left
6904  * 
6905  * 
6906  * @constructor
6907  * Create a new Input
6908  * @param {Object} config The config object
6909  */
6910
6911 Roo.bootstrap.Input = function(config){
6912     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6913    
6914         this.addEvents({
6915             /**
6916              * @event focus
6917              * Fires when this field receives input focus.
6918              * @param {Roo.form.Field} this
6919              */
6920             focus : true,
6921             /**
6922              * @event blur
6923              * Fires when this field loses input focus.
6924              * @param {Roo.form.Field} this
6925              */
6926             blur : true,
6927             /**
6928              * @event specialkey
6929              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6930              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6931              * @param {Roo.form.Field} this
6932              * @param {Roo.EventObject} e The event object
6933              */
6934             specialkey : true,
6935             /**
6936              * @event change
6937              * Fires just before the field blurs if the field value has changed.
6938              * @param {Roo.form.Field} this
6939              * @param {Mixed} newValue The new value
6940              * @param {Mixed} oldValue The original value
6941              */
6942             change : true,
6943             /**
6944              * @event invalid
6945              * Fires after the field has been marked as invalid.
6946              * @param {Roo.form.Field} this
6947              * @param {String} msg The validation message
6948              */
6949             invalid : true,
6950             /**
6951              * @event valid
6952              * Fires after the field has been validated with no errors.
6953              * @param {Roo.form.Field} this
6954              */
6955             valid : true,
6956              /**
6957              * @event keyup
6958              * Fires after the key up
6959              * @param {Roo.form.Field} this
6960              * @param {Roo.EventObject}  e The event Object
6961              */
6962             keyup : true
6963         });
6964 };
6965
6966 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6967      /**
6968      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6969       automatic validation (defaults to "keyup").
6970      */
6971     validationEvent : "keyup",
6972      /**
6973      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6974      */
6975     validateOnBlur : true,
6976     /**
6977      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6978      */
6979     validationDelay : 250,
6980      /**
6981      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6982      */
6983     focusClass : "x-form-focus",  // not needed???
6984     
6985        
6986     /**
6987      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6988      */
6989     invalidClass : "has-error",
6990     
6991     /**
6992      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6993      */
6994     selectOnFocus : false,
6995     
6996      /**
6997      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6998      */
6999     maskRe : null,
7000        /**
7001      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7002      */
7003     vtype : null,
7004     
7005       /**
7006      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7007      */
7008     disableKeyFilter : false,
7009     
7010        /**
7011      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7012      */
7013     disabled : false,
7014      /**
7015      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7016      */
7017     allowBlank : true,
7018     /**
7019      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7020      */
7021     blankText : "This field is required",
7022     
7023      /**
7024      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7025      */
7026     minLength : 0,
7027     /**
7028      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7029      */
7030     maxLength : Number.MAX_VALUE,
7031     /**
7032      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7033      */
7034     minLengthText : "The minimum length for this field is {0}",
7035     /**
7036      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7037      */
7038     maxLengthText : "The maximum length for this field is {0}",
7039   
7040     
7041     /**
7042      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7043      * If available, this function will be called only after the basic validators all return true, and will be passed the
7044      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7045      */
7046     validator : null,
7047     /**
7048      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7049      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7050      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7051      */
7052     regex : null,
7053     /**
7054      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7055      */
7056     regexText : "",
7057     
7058     
7059     
7060     fieldLabel : '',
7061     inputType : 'text',
7062     
7063     name : false,
7064     placeholder: false,
7065     before : false,
7066     after : false,
7067     size : false,
7068     // private
7069     hasFocus : false,
7070     preventMark: false,
7071     isFormField : true,
7072     value : '',
7073     labelWidth : 2,
7074     labelAlign : false,
7075     readOnly : false,
7076     align : false,
7077     formatedValue : false,
7078     
7079     parentLabelAlign : function()
7080     {
7081         var parent = this;
7082         while (parent.parent()) {
7083             parent = parent.parent();
7084             if (typeof(parent.labelAlign) !='undefined') {
7085                 return parent.labelAlign;
7086             }
7087         }
7088         return 'left';
7089         
7090     },
7091     
7092     getAutoCreate : function(){
7093         
7094         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7095         
7096         var id = Roo.id();
7097         
7098         var cfg = {};
7099         
7100         if(this.inputType != 'hidden'){
7101             cfg.cls = 'form-group' //input-group
7102         }
7103         
7104         var input =  {
7105             tag: 'input',
7106             id : id,
7107             type : this.inputType,
7108             value : this.value,
7109             cls : 'form-control',
7110             placeholder : this.placeholder || ''
7111             
7112         };
7113         
7114         if(this.align){
7115             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7116         }
7117         
7118         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7119             input.maxLength = this.maxLength;
7120         }
7121         
7122         if (this.disabled) {
7123             input.disabled=true;
7124         }
7125         
7126         if (this.readOnly) {
7127             input.readonly=true;
7128         }
7129         
7130         if (this.name) {
7131             input.name = this.name;
7132         }
7133         if (this.size) {
7134             input.cls += ' input-' + this.size;
7135         }
7136         var settings=this;
7137         ['xs','sm','md','lg'].map(function(size){
7138             if (settings[size]) {
7139                 cfg.cls += ' col-' + size + '-' + settings[size];
7140             }
7141         });
7142         
7143         var inputblock = input;
7144         
7145         if (this.before || this.after) {
7146             
7147             inputblock = {
7148                 cls : 'input-group',
7149                 cn :  [] 
7150             };
7151             if (this.before && typeof(this.before) == 'string') {
7152                 
7153                 inputblock.cn.push({
7154                     tag :'span',
7155                     cls : 'roo-input-before input-group-addon',
7156                     html : this.before
7157                 });
7158             }
7159             if (this.before && typeof(this.before) == 'object') {
7160                 this.before = Roo.factory(this.before);
7161                 Roo.log(this.before);
7162                 inputblock.cn.push({
7163                     tag :'span',
7164                     cls : 'roo-input-before input-group-' +
7165                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7166                 });
7167             }
7168             
7169             inputblock.cn.push(input);
7170             
7171             if (this.after && typeof(this.after) == 'string') {
7172                 inputblock.cn.push({
7173                     tag :'span',
7174                     cls : 'roo-input-after input-group-addon',
7175                     html : this.after
7176                 });
7177             }
7178             if (this.after && typeof(this.after) == 'object') {
7179                 this.after = Roo.factory(this.after);
7180                 Roo.log(this.after);
7181                 inputblock.cn.push({
7182                     tag :'span',
7183                     cls : 'roo-input-after input-group-' +
7184                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7185                 });
7186             }
7187         };
7188         
7189         if (align ==='left' && this.fieldLabel.length) {
7190                 Roo.log("left and has label");
7191                 cfg.cn = [
7192                     
7193                     {
7194                         tag: 'label',
7195                         'for' :  id,
7196                         cls : 'control-label col-sm-' + this.labelWidth,
7197                         html : this.fieldLabel
7198                         
7199                     },
7200                     {
7201                         cls : "col-sm-" + (12 - this.labelWidth), 
7202                         cn: [
7203                             inputblock
7204                         ]
7205                     }
7206                     
7207                 ];
7208         } else if ( this.fieldLabel.length) {
7209                 Roo.log(" label");
7210                  cfg.cn = [
7211                    
7212                     {
7213                         tag: 'label',
7214                         //cls : 'input-group-addon',
7215                         html : this.fieldLabel
7216                         
7217                     },
7218                     
7219                     inputblock
7220                     
7221                 ];
7222
7223         } else {
7224             
7225                 Roo.log(" no label && no align");
7226                 cfg.cn = [
7227                     
7228                         inputblock
7229                     
7230                 ];
7231                 
7232                 
7233         };
7234         Roo.log('input-parentType: ' + this.parentType);
7235         
7236         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7237            cfg.cls += ' navbar-form';
7238            Roo.log(cfg);
7239         }
7240         
7241         return cfg;
7242         
7243     },
7244     /**
7245      * return the real input element.
7246      */
7247     inputEl: function ()
7248     {
7249         return this.el.select('input.form-control',true).first();
7250     },
7251     
7252     tooltipEl : function()
7253     {
7254         return this.inputEl();
7255     },
7256     
7257     setDisabled : function(v)
7258     {
7259         var i  = this.inputEl().dom;
7260         if (!v) {
7261             i.removeAttribute('disabled');
7262             return;
7263             
7264         }
7265         i.setAttribute('disabled','true');
7266     },
7267     initEvents : function()
7268     {
7269           
7270         this.inputEl().on("keydown" , this.fireKey,  this);
7271         this.inputEl().on("focus", this.onFocus,  this);
7272         this.inputEl().on("blur", this.onBlur,  this);
7273         
7274         this.inputEl().relayEvent('keyup', this);
7275
7276         // reference to original value for reset
7277         this.originalValue = this.getValue();
7278         //Roo.form.TextField.superclass.initEvents.call(this);
7279         if(this.validationEvent == 'keyup'){
7280             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7281             this.inputEl().on('keyup', this.filterValidation, this);
7282         }
7283         else if(this.validationEvent !== false){
7284             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7285         }
7286         
7287         if(this.selectOnFocus){
7288             this.on("focus", this.preFocus, this);
7289             
7290         }
7291         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7292             this.inputEl().on("keypress", this.filterKeys, this);
7293         }
7294        /* if(this.grow){
7295             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7296             this.el.on("click", this.autoSize,  this);
7297         }
7298         */
7299         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7300             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7301         }
7302         
7303         if (typeof(this.before) == 'object') {
7304             this.before.render(this.el.select('.roo-input-before',true).first());
7305         }
7306         if (typeof(this.after) == 'object') {
7307             this.after.render(this.el.select('.roo-input-after',true).first());
7308         }
7309         
7310         
7311     },
7312     filterValidation : function(e){
7313         if(!e.isNavKeyPress()){
7314             this.validationTask.delay(this.validationDelay);
7315         }
7316     },
7317      /**
7318      * Validates the field value
7319      * @return {Boolean} True if the value is valid, else false
7320      */
7321     validate : function(){
7322         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7323         if(this.disabled || this.validateValue(this.getRawValue())){
7324             this.clearInvalid();
7325             return true;
7326         }
7327         return false;
7328     },
7329     
7330     
7331     /**
7332      * Validates a value according to the field's validation rules and marks the field as invalid
7333      * if the validation fails
7334      * @param {Mixed} value The value to validate
7335      * @return {Boolean} True if the value is valid, else false
7336      */
7337     validateValue : function(value){
7338         if(value.length < 1)  { // if it's blank
7339              if(this.allowBlank){
7340                 this.clearInvalid();
7341                 return true;
7342              }else{
7343                 this.markInvalid(this.blankText);
7344                 return false;
7345              }
7346         }
7347         if(value.length < this.minLength){
7348             this.markInvalid(String.format(this.minLengthText, this.minLength));
7349             return false;
7350         }
7351         if(value.length > this.maxLength){
7352             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7353             return false;
7354         }
7355         if(this.vtype){
7356             var vt = Roo.form.VTypes;
7357             if(!vt[this.vtype](value, this)){
7358                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7359                 return false;
7360             }
7361         }
7362         if(typeof this.validator == "function"){
7363             var msg = this.validator(value);
7364             if(msg !== true){
7365                 this.markInvalid(msg);
7366                 return false;
7367             }
7368         }
7369         if(this.regex && !this.regex.test(value)){
7370             this.markInvalid(this.regexText);
7371             return false;
7372         }
7373         return true;
7374     },
7375
7376     
7377     
7378      // private
7379     fireKey : function(e){
7380         //Roo.log('field ' + e.getKey());
7381         if(e.isNavKeyPress()){
7382             this.fireEvent("specialkey", this, e);
7383         }
7384     },
7385     focus : function (selectText){
7386         if(this.rendered){
7387             this.inputEl().focus();
7388             if(selectText === true){
7389                 this.inputEl().dom.select();
7390             }
7391         }
7392         return this;
7393     } ,
7394     
7395     onFocus : function(){
7396         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7397            // this.el.addClass(this.focusClass);
7398         }
7399         if(!this.hasFocus){
7400             this.hasFocus = true;
7401             this.startValue = this.getValue();
7402             this.fireEvent("focus", this);
7403         }
7404     },
7405     
7406     beforeBlur : Roo.emptyFn,
7407
7408     
7409     // private
7410     onBlur : function(){
7411         this.beforeBlur();
7412         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7413             //this.el.removeClass(this.focusClass);
7414         }
7415         this.hasFocus = false;
7416         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7417             this.validate();
7418         }
7419         var v = this.getValue();
7420         if(String(v) !== String(this.startValue)){
7421             this.fireEvent('change', this, v, this.startValue);
7422         }
7423         this.fireEvent("blur", this);
7424     },
7425     
7426     /**
7427      * Resets the current field value to the originally loaded value and clears any validation messages
7428      */
7429     reset : function(){
7430         this.setValue(this.originalValue);
7431         this.clearInvalid();
7432     },
7433      /**
7434      * Returns the name of the field
7435      * @return {Mixed} name The name field
7436      */
7437     getName: function(){
7438         return this.name;
7439     },
7440      /**
7441      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7442      * @return {Mixed} value The field value
7443      */
7444     getValue : function(){
7445         
7446         var v = this.inputEl().getValue();
7447         
7448         return v;
7449     },
7450     /**
7451      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7452      * @return {Mixed} value The field value
7453      */
7454     getRawValue : function(){
7455         var v = this.inputEl().getValue();
7456         
7457         return v;
7458     },
7459     
7460     /**
7461      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7462      * @param {Mixed} value The value to set
7463      */
7464     setRawValue : function(v){
7465         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7466     },
7467     
7468     selectText : function(start, end){
7469         var v = this.getRawValue();
7470         if(v.length > 0){
7471             start = start === undefined ? 0 : start;
7472             end = end === undefined ? v.length : end;
7473             var d = this.inputEl().dom;
7474             if(d.setSelectionRange){
7475                 d.setSelectionRange(start, end);
7476             }else if(d.createTextRange){
7477                 var range = d.createTextRange();
7478                 range.moveStart("character", start);
7479                 range.moveEnd("character", v.length-end);
7480                 range.select();
7481             }
7482         }
7483     },
7484     
7485     /**
7486      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7487      * @param {Mixed} value The value to set
7488      */
7489     setValue : function(v){
7490         this.value = v;
7491         if(this.rendered){
7492             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7493             this.validate();
7494         }
7495     },
7496     
7497     /*
7498     processValue : function(value){
7499         if(this.stripCharsRe){
7500             var newValue = value.replace(this.stripCharsRe, '');
7501             if(newValue !== value){
7502                 this.setRawValue(newValue);
7503                 return newValue;
7504             }
7505         }
7506         return value;
7507     },
7508   */
7509     preFocus : function(){
7510         
7511         if(this.selectOnFocus){
7512             this.inputEl().dom.select();
7513         }
7514     },
7515     filterKeys : function(e){
7516         var k = e.getKey();
7517         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7518             return;
7519         }
7520         var c = e.getCharCode(), cc = String.fromCharCode(c);
7521         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7522             return;
7523         }
7524         if(!this.maskRe.test(cc)){
7525             e.stopEvent();
7526         }
7527     },
7528      /**
7529      * Clear any invalid styles/messages for this field
7530      */
7531     clearInvalid : function(){
7532         
7533         if(!this.el || this.preventMark){ // not rendered
7534             return;
7535         }
7536         this.el.removeClass(this.invalidClass);
7537         /*
7538         switch(this.msgTarget){
7539             case 'qtip':
7540                 this.el.dom.qtip = '';
7541                 break;
7542             case 'title':
7543                 this.el.dom.title = '';
7544                 break;
7545             case 'under':
7546                 if(this.errorEl){
7547                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7548                 }
7549                 break;
7550             case 'side':
7551                 if(this.errorIcon){
7552                     this.errorIcon.dom.qtip = '';
7553                     this.errorIcon.hide();
7554                     this.un('resize', this.alignErrorIcon, this);
7555                 }
7556                 break;
7557             default:
7558                 var t = Roo.getDom(this.msgTarget);
7559                 t.innerHTML = '';
7560                 t.style.display = 'none';
7561                 break;
7562         }
7563         */
7564         this.fireEvent('valid', this);
7565     },
7566      /**
7567      * Mark this field as invalid
7568      * @param {String} msg The validation message
7569      */
7570     markInvalid : function(msg){
7571         if(!this.el  || this.preventMark){ // not rendered
7572             return;
7573         }
7574         this.el.addClass(this.invalidClass);
7575         /*
7576         msg = msg || this.invalidText;
7577         switch(this.msgTarget){
7578             case 'qtip':
7579                 this.el.dom.qtip = msg;
7580                 this.el.dom.qclass = 'x-form-invalid-tip';
7581                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7582                     Roo.QuickTips.enable();
7583                 }
7584                 break;
7585             case 'title':
7586                 this.el.dom.title = msg;
7587                 break;
7588             case 'under':
7589                 if(!this.errorEl){
7590                     var elp = this.el.findParent('.x-form-element', 5, true);
7591                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7592                     this.errorEl.setWidth(elp.getWidth(true)-20);
7593                 }
7594                 this.errorEl.update(msg);
7595                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7596                 break;
7597             case 'side':
7598                 if(!this.errorIcon){
7599                     var elp = this.el.findParent('.x-form-element', 5, true);
7600                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7601                 }
7602                 this.alignErrorIcon();
7603                 this.errorIcon.dom.qtip = msg;
7604                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7605                 this.errorIcon.show();
7606                 this.on('resize', this.alignErrorIcon, this);
7607                 break;
7608             default:
7609                 var t = Roo.getDom(this.msgTarget);
7610                 t.innerHTML = msg;
7611                 t.style.display = this.msgDisplay;
7612                 break;
7613         }
7614         */
7615         this.fireEvent('invalid', this, msg);
7616     },
7617     // private
7618     SafariOnKeyDown : function(event)
7619     {
7620         // this is a workaround for a password hang bug on chrome/ webkit.
7621         
7622         var isSelectAll = false;
7623         
7624         if(this.inputEl().dom.selectionEnd > 0){
7625             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7626         }
7627         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7628             event.preventDefault();
7629             this.setValue('');
7630             return;
7631         }
7632         
7633         if(isSelectAll){ // backspace and delete key
7634             
7635             event.preventDefault();
7636             // this is very hacky as keydown always get's upper case.
7637             //
7638             var cc = String.fromCharCode(event.getCharCode());
7639             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7640             
7641         }
7642     },
7643     adjustWidth : function(tag, w){
7644         tag = tag.toLowerCase();
7645         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7646             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7647                 if(tag == 'input'){
7648                     return w + 2;
7649                 }
7650                 if(tag == 'textarea'){
7651                     return w-2;
7652                 }
7653             }else if(Roo.isOpera){
7654                 if(tag == 'input'){
7655                     return w + 2;
7656                 }
7657                 if(tag == 'textarea'){
7658                     return w-2;
7659                 }
7660             }
7661         }
7662         return w;
7663     }
7664     
7665 });
7666
7667  
7668 /*
7669  * - LGPL
7670  *
7671  * Input
7672  * 
7673  */
7674
7675 /**
7676  * @class Roo.bootstrap.TextArea
7677  * @extends Roo.bootstrap.Input
7678  * Bootstrap TextArea class
7679  * @cfg {Number} cols Specifies the visible width of a text area
7680  * @cfg {Number} rows Specifies the visible number of lines in a text area
7681  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7682  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7683  * @cfg {string} html text
7684  * 
7685  * @constructor
7686  * Create a new TextArea
7687  * @param {Object} config The config object
7688  */
7689
7690 Roo.bootstrap.TextArea = function(config){
7691     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7692    
7693 };
7694
7695 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7696      
7697     cols : false,
7698     rows : 5,
7699     readOnly : false,
7700     warp : 'soft',
7701     resize : false,
7702     value: false,
7703     html: false,
7704     
7705     getAutoCreate : function(){
7706         
7707         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7708         
7709         var id = Roo.id();
7710         
7711         var cfg = {};
7712         
7713         var input =  {
7714             tag: 'textarea',
7715             id : id,
7716             warp : this.warp,
7717             rows : this.rows,
7718             value : this.value || '',
7719             html: this.html || '',
7720             cls : 'form-control',
7721             placeholder : this.placeholder || '' 
7722             
7723         };
7724         
7725         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7726             input.maxLength = this.maxLength;
7727         }
7728         
7729         if(this.resize){
7730             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7731         }
7732         
7733         if(this.cols){
7734             input.cols = this.cols;
7735         }
7736         
7737         if (this.readOnly) {
7738             input.readonly = true;
7739         }
7740         
7741         if (this.name) {
7742             input.name = this.name;
7743         }
7744         
7745         if (this.size) {
7746             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7747         }
7748         
7749         var settings=this;
7750         ['xs','sm','md','lg'].map(function(size){
7751             if (settings[size]) {
7752                 cfg.cls += ' col-' + size + '-' + settings[size];
7753             }
7754         });
7755         
7756         var inputblock = input;
7757         
7758         if (this.before || this.after) {
7759             
7760             inputblock = {
7761                 cls : 'input-group',
7762                 cn :  [] 
7763             };
7764             if (this.before) {
7765                 inputblock.cn.push({
7766                     tag :'span',
7767                     cls : 'input-group-addon',
7768                     html : this.before
7769                 });
7770             }
7771             inputblock.cn.push(input);
7772             if (this.after) {
7773                 inputblock.cn.push({
7774                     tag :'span',
7775                     cls : 'input-group-addon',
7776                     html : this.after
7777                 });
7778             }
7779             
7780         }
7781         
7782         if (align ==='left' && this.fieldLabel.length) {
7783                 Roo.log("left and has label");
7784                 cfg.cn = [
7785                     
7786                     {
7787                         tag: 'label',
7788                         'for' :  id,
7789                         cls : 'control-label col-sm-' + this.labelWidth,
7790                         html : this.fieldLabel
7791                         
7792                     },
7793                     {
7794                         cls : "col-sm-" + (12 - this.labelWidth), 
7795                         cn: [
7796                             inputblock
7797                         ]
7798                     }
7799                     
7800                 ];
7801         } else if ( this.fieldLabel.length) {
7802                 Roo.log(" label");
7803                  cfg.cn = [
7804                    
7805                     {
7806                         tag: 'label',
7807                         //cls : 'input-group-addon',
7808                         html : this.fieldLabel
7809                         
7810                     },
7811                     
7812                     inputblock
7813                     
7814                 ];
7815
7816         } else {
7817             
7818                    Roo.log(" no label && no align");
7819                 cfg.cn = [
7820                     
7821                         inputblock
7822                     
7823                 ];
7824                 
7825                 
7826         }
7827         
7828         if (this.disabled) {
7829             input.disabled=true;
7830         }
7831         
7832         return cfg;
7833         
7834     },
7835     /**
7836      * return the real textarea element.
7837      */
7838     inputEl: function ()
7839     {
7840         return this.el.select('textarea.form-control',true).first();
7841     }
7842 });
7843
7844  
7845 /*
7846  * - LGPL
7847  *
7848  * trigger field - base class for combo..
7849  * 
7850  */
7851  
7852 /**
7853  * @class Roo.bootstrap.TriggerField
7854  * @extends Roo.bootstrap.Input
7855  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7856  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7857  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7858  * for which you can provide a custom implementation.  For example:
7859  * <pre><code>
7860 var trigger = new Roo.bootstrap.TriggerField();
7861 trigger.onTriggerClick = myTriggerFn;
7862 trigger.applyTo('my-field');
7863 </code></pre>
7864  *
7865  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7866  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7867  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7868  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7869  * @constructor
7870  * Create a new TriggerField.
7871  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7872  * to the base TextField)
7873  */
7874 Roo.bootstrap.TriggerField = function(config){
7875     this.mimicing = false;
7876     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7877 };
7878
7879 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7880     /**
7881      * @cfg {String} triggerClass A CSS class to apply to the trigger
7882      */
7883      /**
7884      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7885      */
7886     hideTrigger:false,
7887
7888     /** @cfg {Boolean} grow @hide */
7889     /** @cfg {Number} growMin @hide */
7890     /** @cfg {Number} growMax @hide */
7891
7892     /**
7893      * @hide 
7894      * @method
7895      */
7896     autoSize: Roo.emptyFn,
7897     // private
7898     monitorTab : true,
7899     // private
7900     deferHeight : true,
7901
7902     
7903     actionMode : 'wrap',
7904     
7905     
7906     
7907     getAutoCreate : function(){
7908        
7909         var align = this.labelAlign || this.parentLabelAlign();
7910         
7911         var id = Roo.id();
7912         
7913         var cfg = {
7914             cls: 'form-group' //input-group
7915         };
7916         
7917         
7918         var input =  {
7919             tag: 'input',
7920             id : id,
7921             type : this.inputType,
7922             cls : 'form-control',
7923             autocomplete: 'false',
7924             placeholder : this.placeholder || '' 
7925             
7926         };
7927         if (this.name) {
7928             input.name = this.name;
7929         }
7930         if (this.size) {
7931             input.cls += ' input-' + this.size;
7932         }
7933         
7934         if (this.disabled) {
7935             input.disabled=true;
7936         }
7937         
7938         var inputblock = input;
7939         
7940         if (this.before || this.after) {
7941             
7942             inputblock = {
7943                 cls : 'input-group',
7944                 cn :  [] 
7945             };
7946             if (this.before) {
7947                 inputblock.cn.push({
7948                     tag :'span',
7949                     cls : 'input-group-addon',
7950                     html : this.before
7951                 });
7952             }
7953             inputblock.cn.push(input);
7954             if (this.after) {
7955                 inputblock.cn.push({
7956                     tag :'span',
7957                     cls : 'input-group-addon',
7958                     html : this.after
7959                 });
7960             }
7961             
7962         };
7963         
7964         var box = {
7965             tag: 'div',
7966             cn: [
7967                 {
7968                     tag: 'input',
7969                     type : 'hidden',
7970                     cls: 'form-hidden-field'
7971                 },
7972                 inputblock
7973             ]
7974             
7975         };
7976         
7977         if(this.multiple){
7978             Roo.log('multiple');
7979             
7980             box = {
7981                 tag: 'div',
7982                 cn: [
7983                     {
7984                         tag: 'input',
7985                         type : 'hidden',
7986                         cls: 'form-hidden-field'
7987                     },
7988                     {
7989                         tag: 'ul',
7990                         cls: 'select2-choices',
7991                         cn:[
7992                             {
7993                                 tag: 'li',
7994                                 cls: 'select2-search-field',
7995                                 cn: [
7996
7997                                     inputblock
7998                                 ]
7999                             }
8000                         ]
8001                     }
8002                 ]
8003             }
8004         };
8005         
8006         var combobox = {
8007             cls: 'select2-container input-group',
8008             cn: [
8009                 box
8010 //                {
8011 //                    tag: 'ul',
8012 //                    cls: 'typeahead typeahead-long dropdown-menu',
8013 //                    style: 'display:none'
8014 //                }
8015             ]
8016         };
8017         
8018         if(!this.multiple && this.showToggleBtn){
8019             combobox.cn.push({
8020                 tag :'span',
8021                 cls : 'input-group-addon btn dropdown-toggle',
8022                 cn : [
8023                     {
8024                         tag: 'span',
8025                         cls: 'caret'
8026                     },
8027                     {
8028                         tag: 'span',
8029                         cls: 'combobox-clear',
8030                         cn  : [
8031                             {
8032                                 tag : 'i',
8033                                 cls: 'icon-remove'
8034                             }
8035                         ]
8036                     }
8037                 ]
8038
8039             })
8040         }
8041         
8042         if(this.multiple){
8043             combobox.cls += ' select2-container-multi';
8044         }
8045         
8046         if (align ==='left' && this.fieldLabel.length) {
8047             
8048                 Roo.log("left and has label");
8049                 cfg.cn = [
8050                     
8051                     {
8052                         tag: 'label',
8053                         'for' :  id,
8054                         cls : 'control-label col-sm-' + this.labelWidth,
8055                         html : this.fieldLabel
8056                         
8057                     },
8058                     {
8059                         cls : "col-sm-" + (12 - this.labelWidth), 
8060                         cn: [
8061                             combobox
8062                         ]
8063                     }
8064                     
8065                 ];
8066         } else if ( this.fieldLabel.length) {
8067                 Roo.log(" label");
8068                  cfg.cn = [
8069                    
8070                     {
8071                         tag: 'label',
8072                         //cls : 'input-group-addon',
8073                         html : this.fieldLabel
8074                         
8075                     },
8076                     
8077                     combobox
8078                     
8079                 ];
8080
8081         } else {
8082             
8083                 Roo.log(" no label && no align");
8084                 cfg = combobox
8085                      
8086                 
8087         }
8088          
8089         var settings=this;
8090         ['xs','sm','md','lg'].map(function(size){
8091             if (settings[size]) {
8092                 cfg.cls += ' col-' + size + '-' + settings[size];
8093             }
8094         });
8095         
8096         return cfg;
8097         
8098     },
8099     
8100     
8101     
8102     // private
8103     onResize : function(w, h){
8104 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8105 //        if(typeof w == 'number'){
8106 //            var x = w - this.trigger.getWidth();
8107 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8108 //            this.trigger.setStyle('left', x+'px');
8109 //        }
8110     },
8111
8112     // private
8113     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8114
8115     // private
8116     getResizeEl : function(){
8117         return this.inputEl();
8118     },
8119
8120     // private
8121     getPositionEl : function(){
8122         return this.inputEl();
8123     },
8124
8125     // private
8126     alignErrorIcon : function(){
8127         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8128     },
8129
8130     // private
8131     initEvents : function(){
8132         
8133         this.createList();
8134         
8135         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8136         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8137         if(!this.multiple && this.showToggleBtn){
8138             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8139             if(this.hideTrigger){
8140                 this.trigger.setDisplayed(false);
8141             }
8142             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8143         }
8144         
8145         if(this.multiple){
8146             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8147         }
8148         
8149         //this.trigger.addClassOnOver('x-form-trigger-over');
8150         //this.trigger.addClassOnClick('x-form-trigger-click');
8151         
8152         //if(!this.width){
8153         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8154         //}
8155     },
8156     
8157     createList : function()
8158     {
8159         this.list = Roo.get(document.body).createChild({
8160             tag: 'ul',
8161             cls: 'typeahead typeahead-long dropdown-menu',
8162             style: 'display:none'
8163         });
8164         
8165         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8166         
8167     },
8168
8169     // private
8170     initTrigger : function(){
8171        
8172     },
8173
8174     // private
8175     onDestroy : function(){
8176         if(this.trigger){
8177             this.trigger.removeAllListeners();
8178           //  this.trigger.remove();
8179         }
8180         //if(this.wrap){
8181         //    this.wrap.remove();
8182         //}
8183         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8184     },
8185
8186     // private
8187     onFocus : function(){
8188         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8189         /*
8190         if(!this.mimicing){
8191             this.wrap.addClass('x-trigger-wrap-focus');
8192             this.mimicing = true;
8193             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8194             if(this.monitorTab){
8195                 this.el.on("keydown", this.checkTab, this);
8196             }
8197         }
8198         */
8199     },
8200
8201     // private
8202     checkTab : function(e){
8203         if(e.getKey() == e.TAB){
8204             this.triggerBlur();
8205         }
8206     },
8207
8208     // private
8209     onBlur : function(){
8210         // do nothing
8211     },
8212
8213     // private
8214     mimicBlur : function(e, t){
8215         /*
8216         if(!this.wrap.contains(t) && this.validateBlur()){
8217             this.triggerBlur();
8218         }
8219         */
8220     },
8221
8222     // private
8223     triggerBlur : function(){
8224         this.mimicing = false;
8225         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8226         if(this.monitorTab){
8227             this.el.un("keydown", this.checkTab, this);
8228         }
8229         //this.wrap.removeClass('x-trigger-wrap-focus');
8230         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8231     },
8232
8233     // private
8234     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8235     validateBlur : function(e, t){
8236         return true;
8237     },
8238
8239     // private
8240     onDisable : function(){
8241         this.inputEl().dom.disabled = true;
8242         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8243         //if(this.wrap){
8244         //    this.wrap.addClass('x-item-disabled');
8245         //}
8246     },
8247
8248     // private
8249     onEnable : function(){
8250         this.inputEl().dom.disabled = false;
8251         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8252         //if(this.wrap){
8253         //    this.el.removeClass('x-item-disabled');
8254         //}
8255     },
8256
8257     // private
8258     onShow : function(){
8259         var ae = this.getActionEl();
8260         
8261         if(ae){
8262             ae.dom.style.display = '';
8263             ae.dom.style.visibility = 'visible';
8264         }
8265     },
8266
8267     // private
8268     
8269     onHide : function(){
8270         var ae = this.getActionEl();
8271         ae.dom.style.display = 'none';
8272     },
8273
8274     /**
8275      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8276      * by an implementing function.
8277      * @method
8278      * @param {EventObject} e
8279      */
8280     onTriggerClick : Roo.emptyFn
8281 });
8282  /*
8283  * Based on:
8284  * Ext JS Library 1.1.1
8285  * Copyright(c) 2006-2007, Ext JS, LLC.
8286  *
8287  * Originally Released Under LGPL - original licence link has changed is not relivant.
8288  *
8289  * Fork - LGPL
8290  * <script type="text/javascript">
8291  */
8292
8293
8294 /**
8295  * @class Roo.data.SortTypes
8296  * @singleton
8297  * Defines the default sorting (casting?) comparison functions used when sorting data.
8298  */
8299 Roo.data.SortTypes = {
8300     /**
8301      * Default sort that does nothing
8302      * @param {Mixed} s The value being converted
8303      * @return {Mixed} The comparison value
8304      */
8305     none : function(s){
8306         return s;
8307     },
8308     
8309     /**
8310      * The regular expression used to strip tags
8311      * @type {RegExp}
8312      * @property
8313      */
8314     stripTagsRE : /<\/?[^>]+>/gi,
8315     
8316     /**
8317      * Strips all HTML tags to sort on text only
8318      * @param {Mixed} s The value being converted
8319      * @return {String} The comparison value
8320      */
8321     asText : function(s){
8322         return String(s).replace(this.stripTagsRE, "");
8323     },
8324     
8325     /**
8326      * Strips all HTML tags to sort on text only - Case insensitive
8327      * @param {Mixed} s The value being converted
8328      * @return {String} The comparison value
8329      */
8330     asUCText : function(s){
8331         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8332     },
8333     
8334     /**
8335      * Case insensitive string
8336      * @param {Mixed} s The value being converted
8337      * @return {String} The comparison value
8338      */
8339     asUCString : function(s) {
8340         return String(s).toUpperCase();
8341     },
8342     
8343     /**
8344      * Date sorting
8345      * @param {Mixed} s The value being converted
8346      * @return {Number} The comparison value
8347      */
8348     asDate : function(s) {
8349         if(!s){
8350             return 0;
8351         }
8352         if(s instanceof Date){
8353             return s.getTime();
8354         }
8355         return Date.parse(String(s));
8356     },
8357     
8358     /**
8359      * Float sorting
8360      * @param {Mixed} s The value being converted
8361      * @return {Float} The comparison value
8362      */
8363     asFloat : function(s) {
8364         var val = parseFloat(String(s).replace(/,/g, ""));
8365         if(isNaN(val)) val = 0;
8366         return val;
8367     },
8368     
8369     /**
8370      * Integer sorting
8371      * @param {Mixed} s The value being converted
8372      * @return {Number} The comparison value
8373      */
8374     asInt : function(s) {
8375         var val = parseInt(String(s).replace(/,/g, ""));
8376         if(isNaN(val)) val = 0;
8377         return val;
8378     }
8379 };/*
8380  * Based on:
8381  * Ext JS Library 1.1.1
8382  * Copyright(c) 2006-2007, Ext JS, LLC.
8383  *
8384  * Originally Released Under LGPL - original licence link has changed is not relivant.
8385  *
8386  * Fork - LGPL
8387  * <script type="text/javascript">
8388  */
8389
8390 /**
8391 * @class Roo.data.Record
8392  * Instances of this class encapsulate both record <em>definition</em> information, and record
8393  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8394  * to access Records cached in an {@link Roo.data.Store} object.<br>
8395  * <p>
8396  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8397  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8398  * objects.<br>
8399  * <p>
8400  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8401  * @constructor
8402  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8403  * {@link #create}. The parameters are the same.
8404  * @param {Array} data An associative Array of data values keyed by the field name.
8405  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8406  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8407  * not specified an integer id is generated.
8408  */
8409 Roo.data.Record = function(data, id){
8410     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8411     this.data = data;
8412 };
8413
8414 /**
8415  * Generate a constructor for a specific record layout.
8416  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8417  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8418  * Each field definition object may contain the following properties: <ul>
8419  * <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,
8420  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8421  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8422  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8423  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8424  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8425  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8426  * this may be omitted.</p></li>
8427  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8428  * <ul><li>auto (Default, implies no conversion)</li>
8429  * <li>string</li>
8430  * <li>int</li>
8431  * <li>float</li>
8432  * <li>boolean</li>
8433  * <li>date</li></ul></p></li>
8434  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8435  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8436  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8437  * by the Reader into an object that will be stored in the Record. It is passed the
8438  * following parameters:<ul>
8439  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8440  * </ul></p></li>
8441  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8442  * </ul>
8443  * <br>usage:<br><pre><code>
8444 var TopicRecord = Roo.data.Record.create(
8445     {name: 'title', mapping: 'topic_title'},
8446     {name: 'author', mapping: 'username'},
8447     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8448     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8449     {name: 'lastPoster', mapping: 'user2'},
8450     {name: 'excerpt', mapping: 'post_text'}
8451 );
8452
8453 var myNewRecord = new TopicRecord({
8454     title: 'Do my job please',
8455     author: 'noobie',
8456     totalPosts: 1,
8457     lastPost: new Date(),
8458     lastPoster: 'Animal',
8459     excerpt: 'No way dude!'
8460 });
8461 myStore.add(myNewRecord);
8462 </code></pre>
8463  * @method create
8464  * @static
8465  */
8466 Roo.data.Record.create = function(o){
8467     var f = function(){
8468         f.superclass.constructor.apply(this, arguments);
8469     };
8470     Roo.extend(f, Roo.data.Record);
8471     var p = f.prototype;
8472     p.fields = new Roo.util.MixedCollection(false, function(field){
8473         return field.name;
8474     });
8475     for(var i = 0, len = o.length; i < len; i++){
8476         p.fields.add(new Roo.data.Field(o[i]));
8477     }
8478     f.getField = function(name){
8479         return p.fields.get(name);  
8480     };
8481     return f;
8482 };
8483
8484 Roo.data.Record.AUTO_ID = 1000;
8485 Roo.data.Record.EDIT = 'edit';
8486 Roo.data.Record.REJECT = 'reject';
8487 Roo.data.Record.COMMIT = 'commit';
8488
8489 Roo.data.Record.prototype = {
8490     /**
8491      * Readonly flag - true if this record has been modified.
8492      * @type Boolean
8493      */
8494     dirty : false,
8495     editing : false,
8496     error: null,
8497     modified: null,
8498
8499     // private
8500     join : function(store){
8501         this.store = store;
8502     },
8503
8504     /**
8505      * Set the named field to the specified value.
8506      * @param {String} name The name of the field to set.
8507      * @param {Object} value The value to set the field to.
8508      */
8509     set : function(name, value){
8510         if(this.data[name] == value){
8511             return;
8512         }
8513         this.dirty = true;
8514         if(!this.modified){
8515             this.modified = {};
8516         }
8517         if(typeof this.modified[name] == 'undefined'){
8518             this.modified[name] = this.data[name];
8519         }
8520         this.data[name] = value;
8521         if(!this.editing && this.store){
8522             this.store.afterEdit(this);
8523         }       
8524     },
8525
8526     /**
8527      * Get the value of the named field.
8528      * @param {String} name The name of the field to get the value of.
8529      * @return {Object} The value of the field.
8530      */
8531     get : function(name){
8532         return this.data[name]; 
8533     },
8534
8535     // private
8536     beginEdit : function(){
8537         this.editing = true;
8538         this.modified = {}; 
8539     },
8540
8541     // private
8542     cancelEdit : function(){
8543         this.editing = false;
8544         delete this.modified;
8545     },
8546
8547     // private
8548     endEdit : function(){
8549         this.editing = false;
8550         if(this.dirty && this.store){
8551             this.store.afterEdit(this);
8552         }
8553     },
8554
8555     /**
8556      * Usually called by the {@link Roo.data.Store} which owns the Record.
8557      * Rejects all changes made to the Record since either creation, or the last commit operation.
8558      * Modified fields are reverted to their original values.
8559      * <p>
8560      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8561      * of reject operations.
8562      */
8563     reject : function(){
8564         var m = this.modified;
8565         for(var n in m){
8566             if(typeof m[n] != "function"){
8567                 this.data[n] = m[n];
8568             }
8569         }
8570         this.dirty = false;
8571         delete this.modified;
8572         this.editing = false;
8573         if(this.store){
8574             this.store.afterReject(this);
8575         }
8576     },
8577
8578     /**
8579      * Usually called by the {@link Roo.data.Store} which owns the Record.
8580      * Commits all changes made to the Record since either creation, or the last commit operation.
8581      * <p>
8582      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8583      * of commit operations.
8584      */
8585     commit : function(){
8586         this.dirty = false;
8587         delete this.modified;
8588         this.editing = false;
8589         if(this.store){
8590             this.store.afterCommit(this);
8591         }
8592     },
8593
8594     // private
8595     hasError : function(){
8596         return this.error != null;
8597     },
8598
8599     // private
8600     clearError : function(){
8601         this.error = null;
8602     },
8603
8604     /**
8605      * Creates a copy of this record.
8606      * @param {String} id (optional) A new record id if you don't want to use this record's id
8607      * @return {Record}
8608      */
8609     copy : function(newId) {
8610         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8611     }
8612 };/*
8613  * Based on:
8614  * Ext JS Library 1.1.1
8615  * Copyright(c) 2006-2007, Ext JS, LLC.
8616  *
8617  * Originally Released Under LGPL - original licence link has changed is not relivant.
8618  *
8619  * Fork - LGPL
8620  * <script type="text/javascript">
8621  */
8622
8623
8624
8625 /**
8626  * @class Roo.data.Store
8627  * @extends Roo.util.Observable
8628  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8629  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8630  * <p>
8631  * 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
8632  * has no knowledge of the format of the data returned by the Proxy.<br>
8633  * <p>
8634  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8635  * instances from the data object. These records are cached and made available through accessor functions.
8636  * @constructor
8637  * Creates a new Store.
8638  * @param {Object} config A config object containing the objects needed for the Store to access data,
8639  * and read the data into Records.
8640  */
8641 Roo.data.Store = function(config){
8642     this.data = new Roo.util.MixedCollection(false);
8643     this.data.getKey = function(o){
8644         return o.id;
8645     };
8646     this.baseParams = {};
8647     // private
8648     this.paramNames = {
8649         "start" : "start",
8650         "limit" : "limit",
8651         "sort" : "sort",
8652         "dir" : "dir",
8653         "multisort" : "_multisort"
8654     };
8655
8656     if(config && config.data){
8657         this.inlineData = config.data;
8658         delete config.data;
8659     }
8660
8661     Roo.apply(this, config);
8662     
8663     if(this.reader){ // reader passed
8664         this.reader = Roo.factory(this.reader, Roo.data);
8665         this.reader.xmodule = this.xmodule || false;
8666         if(!this.recordType){
8667             this.recordType = this.reader.recordType;
8668         }
8669         if(this.reader.onMetaChange){
8670             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8671         }
8672     }
8673
8674     if(this.recordType){
8675         this.fields = this.recordType.prototype.fields;
8676     }
8677     this.modified = [];
8678
8679     this.addEvents({
8680         /**
8681          * @event datachanged
8682          * Fires when the data cache has changed, and a widget which is using this Store
8683          * as a Record cache should refresh its view.
8684          * @param {Store} this
8685          */
8686         datachanged : true,
8687         /**
8688          * @event metachange
8689          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8690          * @param {Store} this
8691          * @param {Object} meta The JSON metadata
8692          */
8693         metachange : true,
8694         /**
8695          * @event add
8696          * Fires when Records have been added to the Store
8697          * @param {Store} this
8698          * @param {Roo.data.Record[]} records The array of Records added
8699          * @param {Number} index The index at which the record(s) were added
8700          */
8701         add : true,
8702         /**
8703          * @event remove
8704          * Fires when a Record has been removed from the Store
8705          * @param {Store} this
8706          * @param {Roo.data.Record} record The Record that was removed
8707          * @param {Number} index The index at which the record was removed
8708          */
8709         remove : true,
8710         /**
8711          * @event update
8712          * Fires when a Record has been updated
8713          * @param {Store} this
8714          * @param {Roo.data.Record} record The Record that was updated
8715          * @param {String} operation The update operation being performed.  Value may be one of:
8716          * <pre><code>
8717  Roo.data.Record.EDIT
8718  Roo.data.Record.REJECT
8719  Roo.data.Record.COMMIT
8720          * </code></pre>
8721          */
8722         update : true,
8723         /**
8724          * @event clear
8725          * Fires when the data cache has been cleared.
8726          * @param {Store} this
8727          */
8728         clear : true,
8729         /**
8730          * @event beforeload
8731          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8732          * the load action will be canceled.
8733          * @param {Store} this
8734          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8735          */
8736         beforeload : true,
8737         /**
8738          * @event beforeloadadd
8739          * Fires after a new set of Records has been loaded.
8740          * @param {Store} this
8741          * @param {Roo.data.Record[]} records The Records that were loaded
8742          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8743          */
8744         beforeloadadd : true,
8745         /**
8746          * @event load
8747          * Fires after a new set of Records has been loaded, before they are added to the store.
8748          * @param {Store} this
8749          * @param {Roo.data.Record[]} records The Records that were loaded
8750          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8751          * @params {Object} return from reader
8752          */
8753         load : true,
8754         /**
8755          * @event loadexception
8756          * Fires if an exception occurs in the Proxy during loading.
8757          * Called with the signature of the Proxy's "loadexception" event.
8758          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8759          * 
8760          * @param {Proxy} 
8761          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8762          * @param {Object} load options 
8763          * @param {Object} jsonData from your request (normally this contains the Exception)
8764          */
8765         loadexception : true
8766     });
8767     
8768     if(this.proxy){
8769         this.proxy = Roo.factory(this.proxy, Roo.data);
8770         this.proxy.xmodule = this.xmodule || false;
8771         this.relayEvents(this.proxy,  ["loadexception"]);
8772     }
8773     this.sortToggle = {};
8774     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8775
8776     Roo.data.Store.superclass.constructor.call(this);
8777
8778     if(this.inlineData){
8779         this.loadData(this.inlineData);
8780         delete this.inlineData;
8781     }
8782 };
8783
8784 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8785      /**
8786     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8787     * without a remote query - used by combo/forms at present.
8788     */
8789     
8790     /**
8791     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8792     */
8793     /**
8794     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8795     */
8796     /**
8797     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8798     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8799     */
8800     /**
8801     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8802     * on any HTTP request
8803     */
8804     /**
8805     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8806     */
8807     /**
8808     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8809     */
8810     multiSort: false,
8811     /**
8812     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8813     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8814     */
8815     remoteSort : false,
8816
8817     /**
8818     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8819      * loaded or when a record is removed. (defaults to false).
8820     */
8821     pruneModifiedRecords : false,
8822
8823     // private
8824     lastOptions : null,
8825
8826     /**
8827      * Add Records to the Store and fires the add event.
8828      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8829      */
8830     add : function(records){
8831         records = [].concat(records);
8832         for(var i = 0, len = records.length; i < len; i++){
8833             records[i].join(this);
8834         }
8835         var index = this.data.length;
8836         this.data.addAll(records);
8837         this.fireEvent("add", this, records, index);
8838     },
8839
8840     /**
8841      * Remove a Record from the Store and fires the remove event.
8842      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8843      */
8844     remove : function(record){
8845         var index = this.data.indexOf(record);
8846         this.data.removeAt(index);
8847         if(this.pruneModifiedRecords){
8848             this.modified.remove(record);
8849         }
8850         this.fireEvent("remove", this, record, index);
8851     },
8852
8853     /**
8854      * Remove all Records from the Store and fires the clear event.
8855      */
8856     removeAll : function(){
8857         this.data.clear();
8858         if(this.pruneModifiedRecords){
8859             this.modified = [];
8860         }
8861         this.fireEvent("clear", this);
8862     },
8863
8864     /**
8865      * Inserts Records to the Store at the given index and fires the add event.
8866      * @param {Number} index The start index at which to insert the passed Records.
8867      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8868      */
8869     insert : function(index, records){
8870         records = [].concat(records);
8871         for(var i = 0, len = records.length; i < len; i++){
8872             this.data.insert(index, records[i]);
8873             records[i].join(this);
8874         }
8875         this.fireEvent("add", this, records, index);
8876     },
8877
8878     /**
8879      * Get the index within the cache of the passed Record.
8880      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8881      * @return {Number} The index of the passed Record. Returns -1 if not found.
8882      */
8883     indexOf : function(record){
8884         return this.data.indexOf(record);
8885     },
8886
8887     /**
8888      * Get the index within the cache of the Record with the passed id.
8889      * @param {String} id The id of the Record to find.
8890      * @return {Number} The index of the Record. Returns -1 if not found.
8891      */
8892     indexOfId : function(id){
8893         return this.data.indexOfKey(id);
8894     },
8895
8896     /**
8897      * Get the Record with the specified id.
8898      * @param {String} id The id of the Record to find.
8899      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8900      */
8901     getById : function(id){
8902         return this.data.key(id);
8903     },
8904
8905     /**
8906      * Get the Record at the specified index.
8907      * @param {Number} index The index of the Record to find.
8908      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8909      */
8910     getAt : function(index){
8911         return this.data.itemAt(index);
8912     },
8913
8914     /**
8915      * Returns a range of Records between specified indices.
8916      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8917      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8918      * @return {Roo.data.Record[]} An array of Records
8919      */
8920     getRange : function(start, end){
8921         return this.data.getRange(start, end);
8922     },
8923
8924     // private
8925     storeOptions : function(o){
8926         o = Roo.apply({}, o);
8927         delete o.callback;
8928         delete o.scope;
8929         this.lastOptions = o;
8930     },
8931
8932     /**
8933      * Loads the Record cache from the configured Proxy using the configured Reader.
8934      * <p>
8935      * If using remote paging, then the first load call must specify the <em>start</em>
8936      * and <em>limit</em> properties in the options.params property to establish the initial
8937      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8938      * <p>
8939      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8940      * and this call will return before the new data has been loaded. Perform any post-processing
8941      * in a callback function, or in a "load" event handler.</strong>
8942      * <p>
8943      * @param {Object} options An object containing properties which control loading options:<ul>
8944      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8945      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8946      * passed the following arguments:<ul>
8947      * <li>r : Roo.data.Record[]</li>
8948      * <li>options: Options object from the load call</li>
8949      * <li>success: Boolean success indicator</li></ul></li>
8950      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8951      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8952      * </ul>
8953      */
8954     load : function(options){
8955         options = options || {};
8956         if(this.fireEvent("beforeload", this, options) !== false){
8957             this.storeOptions(options);
8958             var p = Roo.apply(options.params || {}, this.baseParams);
8959             // if meta was not loaded from remote source.. try requesting it.
8960             if (!this.reader.metaFromRemote) {
8961                 p._requestMeta = 1;
8962             }
8963             if(this.sortInfo && this.remoteSort){
8964                 var pn = this.paramNames;
8965                 p[pn["sort"]] = this.sortInfo.field;
8966                 p[pn["dir"]] = this.sortInfo.direction;
8967             }
8968             if (this.multiSort) {
8969                 var pn = this.paramNames;
8970                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8971             }
8972             
8973             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8974         }
8975     },
8976
8977     /**
8978      * Reloads the Record cache from the configured Proxy using the configured Reader and
8979      * the options from the last load operation performed.
8980      * @param {Object} options (optional) An object containing properties which may override the options
8981      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8982      * the most recently used options are reused).
8983      */
8984     reload : function(options){
8985         this.load(Roo.applyIf(options||{}, this.lastOptions));
8986     },
8987
8988     // private
8989     // Called as a callback by the Reader during a load operation.
8990     loadRecords : function(o, options, success){
8991         if(!o || success === false){
8992             if(success !== false){
8993                 this.fireEvent("load", this, [], options, o);
8994             }
8995             if(options.callback){
8996                 options.callback.call(options.scope || this, [], options, false);
8997             }
8998             return;
8999         }
9000         // if data returned failure - throw an exception.
9001         if (o.success === false) {
9002             // show a message if no listener is registered.
9003             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9004                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9005             }
9006             // loadmask wil be hooked into this..
9007             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9008             return;
9009         }
9010         var r = o.records, t = o.totalRecords || r.length;
9011         
9012         this.fireEvent("beforeloadadd", this, r, options, o);
9013         
9014         if(!options || options.add !== true){
9015             if(this.pruneModifiedRecords){
9016                 this.modified = [];
9017             }
9018             for(var i = 0, len = r.length; i < len; i++){
9019                 r[i].join(this);
9020             }
9021             if(this.snapshot){
9022                 this.data = this.snapshot;
9023                 delete this.snapshot;
9024             }
9025             this.data.clear();
9026             this.data.addAll(r);
9027             this.totalLength = t;
9028             this.applySort();
9029             this.fireEvent("datachanged", this);
9030         }else{
9031             this.totalLength = Math.max(t, this.data.length+r.length);
9032             this.add(r);
9033         }
9034         this.fireEvent("load", this, r, options, o);
9035         if(options.callback){
9036             options.callback.call(options.scope || this, r, options, true);
9037         }
9038     },
9039
9040
9041     /**
9042      * Loads data from a passed data block. A Reader which understands the format of the data
9043      * must have been configured in the constructor.
9044      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9045      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9046      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9047      */
9048     loadData : function(o, append){
9049         var r = this.reader.readRecords(o);
9050         this.loadRecords(r, {add: append}, true);
9051     },
9052
9053     /**
9054      * Gets the number of cached records.
9055      * <p>
9056      * <em>If using paging, this may not be the total size of the dataset. If the data object
9057      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9058      * the data set size</em>
9059      */
9060     getCount : function(){
9061         return this.data.length || 0;
9062     },
9063
9064     /**
9065      * Gets the total number of records in the dataset as returned by the server.
9066      * <p>
9067      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9068      * the dataset size</em>
9069      */
9070     getTotalCount : function(){
9071         return this.totalLength || 0;
9072     },
9073
9074     /**
9075      * Returns the sort state of the Store as an object with two properties:
9076      * <pre><code>
9077  field {String} The name of the field by which the Records are sorted
9078  direction {String} The sort order, "ASC" or "DESC"
9079      * </code></pre>
9080      */
9081     getSortState : function(){
9082         return this.sortInfo;
9083     },
9084
9085     // private
9086     applySort : function(){
9087         if(this.sortInfo && !this.remoteSort){
9088             var s = this.sortInfo, f = s.field;
9089             var st = this.fields.get(f).sortType;
9090             var fn = function(r1, r2){
9091                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9092                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9093             };
9094             this.data.sort(s.direction, fn);
9095             if(this.snapshot && this.snapshot != this.data){
9096                 this.snapshot.sort(s.direction, fn);
9097             }
9098         }
9099     },
9100
9101     /**
9102      * Sets the default sort column and order to be used by the next load operation.
9103      * @param {String} fieldName The name of the field to sort by.
9104      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9105      */
9106     setDefaultSort : function(field, dir){
9107         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9108     },
9109
9110     /**
9111      * Sort the Records.
9112      * If remote sorting is used, the sort is performed on the server, and the cache is
9113      * reloaded. If local sorting is used, the cache is sorted internally.
9114      * @param {String} fieldName The name of the field to sort by.
9115      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9116      */
9117     sort : function(fieldName, dir){
9118         var f = this.fields.get(fieldName);
9119         if(!dir){
9120             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9121             
9122             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9123                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9124             }else{
9125                 dir = f.sortDir;
9126             }
9127         }
9128         this.sortToggle[f.name] = dir;
9129         this.sortInfo = {field: f.name, direction: dir};
9130         if(!this.remoteSort){
9131             this.applySort();
9132             this.fireEvent("datachanged", this);
9133         }else{
9134             this.load(this.lastOptions);
9135         }
9136     },
9137
9138     /**
9139      * Calls the specified function for each of the Records in the cache.
9140      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9141      * Returning <em>false</em> aborts and exits the iteration.
9142      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9143      */
9144     each : function(fn, scope){
9145         this.data.each(fn, scope);
9146     },
9147
9148     /**
9149      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9150      * (e.g., during paging).
9151      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9152      */
9153     getModifiedRecords : function(){
9154         return this.modified;
9155     },
9156
9157     // private
9158     createFilterFn : function(property, value, anyMatch){
9159         if(!value.exec){ // not a regex
9160             value = String(value);
9161             if(value.length == 0){
9162                 return false;
9163             }
9164             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9165         }
9166         return function(r){
9167             return value.test(r.data[property]);
9168         };
9169     },
9170
9171     /**
9172      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9173      * @param {String} property A field on your records
9174      * @param {Number} start The record index to start at (defaults to 0)
9175      * @param {Number} end The last record index to include (defaults to length - 1)
9176      * @return {Number} The sum
9177      */
9178     sum : function(property, start, end){
9179         var rs = this.data.items, v = 0;
9180         start = start || 0;
9181         end = (end || end === 0) ? end : rs.length-1;
9182
9183         for(var i = start; i <= end; i++){
9184             v += (rs[i].data[property] || 0);
9185         }
9186         return v;
9187     },
9188
9189     /**
9190      * Filter the records by a specified property.
9191      * @param {String} field A field on your records
9192      * @param {String/RegExp} value Either a string that the field
9193      * should start with or a RegExp to test against the field
9194      * @param {Boolean} anyMatch True to match any part not just the beginning
9195      */
9196     filter : function(property, value, anyMatch){
9197         var fn = this.createFilterFn(property, value, anyMatch);
9198         return fn ? this.filterBy(fn) : this.clearFilter();
9199     },
9200
9201     /**
9202      * Filter by a function. The specified function will be called with each
9203      * record in this data source. If the function returns true the record is included,
9204      * otherwise it is filtered.
9205      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9206      * @param {Object} scope (optional) The scope of the function (defaults to this)
9207      */
9208     filterBy : function(fn, scope){
9209         this.snapshot = this.snapshot || this.data;
9210         this.data = this.queryBy(fn, scope||this);
9211         this.fireEvent("datachanged", this);
9212     },
9213
9214     /**
9215      * Query the records by a specified property.
9216      * @param {String} field A field on your records
9217      * @param {String/RegExp} value Either a string that the field
9218      * should start with or a RegExp to test against the field
9219      * @param {Boolean} anyMatch True to match any part not just the beginning
9220      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9221      */
9222     query : function(property, value, anyMatch){
9223         var fn = this.createFilterFn(property, value, anyMatch);
9224         return fn ? this.queryBy(fn) : this.data.clone();
9225     },
9226
9227     /**
9228      * Query by a function. The specified function will be called with each
9229      * record in this data source. If the function returns true the record is included
9230      * in the results.
9231      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9232      * @param {Object} scope (optional) The scope of the function (defaults to this)
9233       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9234      **/
9235     queryBy : function(fn, scope){
9236         var data = this.snapshot || this.data;
9237         return data.filterBy(fn, scope||this);
9238     },
9239
9240     /**
9241      * Collects unique values for a particular dataIndex from this store.
9242      * @param {String} dataIndex The property to collect
9243      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9244      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9245      * @return {Array} An array of the unique values
9246      **/
9247     collect : function(dataIndex, allowNull, bypassFilter){
9248         var d = (bypassFilter === true && this.snapshot) ?
9249                 this.snapshot.items : this.data.items;
9250         var v, sv, r = [], l = {};
9251         for(var i = 0, len = d.length; i < len; i++){
9252             v = d[i].data[dataIndex];
9253             sv = String(v);
9254             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9255                 l[sv] = true;
9256                 r[r.length] = v;
9257             }
9258         }
9259         return r;
9260     },
9261
9262     /**
9263      * Revert to a view of the Record cache with no filtering applied.
9264      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9265      */
9266     clearFilter : function(suppressEvent){
9267         if(this.snapshot && this.snapshot != this.data){
9268             this.data = this.snapshot;
9269             delete this.snapshot;
9270             if(suppressEvent !== true){
9271                 this.fireEvent("datachanged", this);
9272             }
9273         }
9274     },
9275
9276     // private
9277     afterEdit : function(record){
9278         if(this.modified.indexOf(record) == -1){
9279             this.modified.push(record);
9280         }
9281         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9282     },
9283     
9284     // private
9285     afterReject : function(record){
9286         this.modified.remove(record);
9287         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9288     },
9289
9290     // private
9291     afterCommit : function(record){
9292         this.modified.remove(record);
9293         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9294     },
9295
9296     /**
9297      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9298      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9299      */
9300     commitChanges : function(){
9301         var m = this.modified.slice(0);
9302         this.modified = [];
9303         for(var i = 0, len = m.length; i < len; i++){
9304             m[i].commit();
9305         }
9306     },
9307
9308     /**
9309      * Cancel outstanding changes on all changed records.
9310      */
9311     rejectChanges : function(){
9312         var m = this.modified.slice(0);
9313         this.modified = [];
9314         for(var i = 0, len = m.length; i < len; i++){
9315             m[i].reject();
9316         }
9317     },
9318
9319     onMetaChange : function(meta, rtype, o){
9320         this.recordType = rtype;
9321         this.fields = rtype.prototype.fields;
9322         delete this.snapshot;
9323         this.sortInfo = meta.sortInfo || this.sortInfo;
9324         this.modified = [];
9325         this.fireEvent('metachange', this, this.reader.meta);
9326     },
9327     
9328     moveIndex : function(data, type)
9329     {
9330         var index = this.indexOf(data);
9331         
9332         var newIndex = index + type;
9333         
9334         this.remove(data);
9335         
9336         this.insert(newIndex, data);
9337         
9338     }
9339 });/*
9340  * Based on:
9341  * Ext JS Library 1.1.1
9342  * Copyright(c) 2006-2007, Ext JS, LLC.
9343  *
9344  * Originally Released Under LGPL - original licence link has changed is not relivant.
9345  *
9346  * Fork - LGPL
9347  * <script type="text/javascript">
9348  */
9349
9350 /**
9351  * @class Roo.data.SimpleStore
9352  * @extends Roo.data.Store
9353  * Small helper class to make creating Stores from Array data easier.
9354  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9355  * @cfg {Array} fields An array of field definition objects, or field name strings.
9356  * @cfg {Array} data The multi-dimensional array of data
9357  * @constructor
9358  * @param {Object} config
9359  */
9360 Roo.data.SimpleStore = function(config){
9361     Roo.data.SimpleStore.superclass.constructor.call(this, {
9362         isLocal : true,
9363         reader: new Roo.data.ArrayReader({
9364                 id: config.id
9365             },
9366             Roo.data.Record.create(config.fields)
9367         ),
9368         proxy : new Roo.data.MemoryProxy(config.data)
9369     });
9370     this.load();
9371 };
9372 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9373  * Based on:
9374  * Ext JS Library 1.1.1
9375  * Copyright(c) 2006-2007, Ext JS, LLC.
9376  *
9377  * Originally Released Under LGPL - original licence link has changed is not relivant.
9378  *
9379  * Fork - LGPL
9380  * <script type="text/javascript">
9381  */
9382
9383 /**
9384 /**
9385  * @extends Roo.data.Store
9386  * @class Roo.data.JsonStore
9387  * Small helper class to make creating Stores for JSON data easier. <br/>
9388 <pre><code>
9389 var store = new Roo.data.JsonStore({
9390     url: 'get-images.php',
9391     root: 'images',
9392     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9393 });
9394 </code></pre>
9395  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9396  * JsonReader and HttpProxy (unless inline data is provided).</b>
9397  * @cfg {Array} fields An array of field definition objects, or field name strings.
9398  * @constructor
9399  * @param {Object} config
9400  */
9401 Roo.data.JsonStore = function(c){
9402     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9403         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9404         reader: new Roo.data.JsonReader(c, c.fields)
9405     }));
9406 };
9407 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9408  * Based on:
9409  * Ext JS Library 1.1.1
9410  * Copyright(c) 2006-2007, Ext JS, LLC.
9411  *
9412  * Originally Released Under LGPL - original licence link has changed is not relivant.
9413  *
9414  * Fork - LGPL
9415  * <script type="text/javascript">
9416  */
9417
9418  
9419 Roo.data.Field = function(config){
9420     if(typeof config == "string"){
9421         config = {name: config};
9422     }
9423     Roo.apply(this, config);
9424     
9425     if(!this.type){
9426         this.type = "auto";
9427     }
9428     
9429     var st = Roo.data.SortTypes;
9430     // named sortTypes are supported, here we look them up
9431     if(typeof this.sortType == "string"){
9432         this.sortType = st[this.sortType];
9433     }
9434     
9435     // set default sortType for strings and dates
9436     if(!this.sortType){
9437         switch(this.type){
9438             case "string":
9439                 this.sortType = st.asUCString;
9440                 break;
9441             case "date":
9442                 this.sortType = st.asDate;
9443                 break;
9444             default:
9445                 this.sortType = st.none;
9446         }
9447     }
9448
9449     // define once
9450     var stripRe = /[\$,%]/g;
9451
9452     // prebuilt conversion function for this field, instead of
9453     // switching every time we're reading a value
9454     if(!this.convert){
9455         var cv, dateFormat = this.dateFormat;
9456         switch(this.type){
9457             case "":
9458             case "auto":
9459             case undefined:
9460                 cv = function(v){ return v; };
9461                 break;
9462             case "string":
9463                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9464                 break;
9465             case "int":
9466                 cv = function(v){
9467                     return v !== undefined && v !== null && v !== '' ?
9468                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9469                     };
9470                 break;
9471             case "float":
9472                 cv = function(v){
9473                     return v !== undefined && v !== null && v !== '' ?
9474                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9475                     };
9476                 break;
9477             case "bool":
9478             case "boolean":
9479                 cv = function(v){ return v === true || v === "true" || v == 1; };
9480                 break;
9481             case "date":
9482                 cv = function(v){
9483                     if(!v){
9484                         return '';
9485                     }
9486                     if(v instanceof Date){
9487                         return v;
9488                     }
9489                     if(dateFormat){
9490                         if(dateFormat == "timestamp"){
9491                             return new Date(v*1000);
9492                         }
9493                         return Date.parseDate(v, dateFormat);
9494                     }
9495                     var parsed = Date.parse(v);
9496                     return parsed ? new Date(parsed) : null;
9497                 };
9498              break;
9499             
9500         }
9501         this.convert = cv;
9502     }
9503 };
9504
9505 Roo.data.Field.prototype = {
9506     dateFormat: null,
9507     defaultValue: "",
9508     mapping: null,
9509     sortType : null,
9510     sortDir : "ASC"
9511 };/*
9512  * Based on:
9513  * Ext JS Library 1.1.1
9514  * Copyright(c) 2006-2007, Ext JS, LLC.
9515  *
9516  * Originally Released Under LGPL - original licence link has changed is not relivant.
9517  *
9518  * Fork - LGPL
9519  * <script type="text/javascript">
9520  */
9521  
9522 // Base class for reading structured data from a data source.  This class is intended to be
9523 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9524
9525 /**
9526  * @class Roo.data.DataReader
9527  * Base class for reading structured data from a data source.  This class is intended to be
9528  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9529  */
9530
9531 Roo.data.DataReader = function(meta, recordType){
9532     
9533     this.meta = meta;
9534     
9535     this.recordType = recordType instanceof Array ? 
9536         Roo.data.Record.create(recordType) : recordType;
9537 };
9538
9539 Roo.data.DataReader.prototype = {
9540      /**
9541      * Create an empty record
9542      * @param {Object} data (optional) - overlay some values
9543      * @return {Roo.data.Record} record created.
9544      */
9545     newRow :  function(d) {
9546         var da =  {};
9547         this.recordType.prototype.fields.each(function(c) {
9548             switch( c.type) {
9549                 case 'int' : da[c.name] = 0; break;
9550                 case 'date' : da[c.name] = new Date(); break;
9551                 case 'float' : da[c.name] = 0.0; break;
9552                 case 'boolean' : da[c.name] = false; break;
9553                 default : da[c.name] = ""; break;
9554             }
9555             
9556         });
9557         return new this.recordType(Roo.apply(da, d));
9558     }
9559     
9560 };/*
9561  * Based on:
9562  * Ext JS Library 1.1.1
9563  * Copyright(c) 2006-2007, Ext JS, LLC.
9564  *
9565  * Originally Released Under LGPL - original licence link has changed is not relivant.
9566  *
9567  * Fork - LGPL
9568  * <script type="text/javascript">
9569  */
9570
9571 /**
9572  * @class Roo.data.DataProxy
9573  * @extends Roo.data.Observable
9574  * This class is an abstract base class for implementations which provide retrieval of
9575  * unformatted data objects.<br>
9576  * <p>
9577  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9578  * (of the appropriate type which knows how to parse the data object) to provide a block of
9579  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9580  * <p>
9581  * Custom implementations must implement the load method as described in
9582  * {@link Roo.data.HttpProxy#load}.
9583  */
9584 Roo.data.DataProxy = function(){
9585     this.addEvents({
9586         /**
9587          * @event beforeload
9588          * Fires before a network request is made to retrieve a data object.
9589          * @param {Object} This DataProxy object.
9590          * @param {Object} params The params parameter to the load function.
9591          */
9592         beforeload : true,
9593         /**
9594          * @event load
9595          * Fires before the load method's callback is called.
9596          * @param {Object} This DataProxy object.
9597          * @param {Object} o The data object.
9598          * @param {Object} arg The callback argument object passed to the load function.
9599          */
9600         load : true,
9601         /**
9602          * @event loadexception
9603          * Fires if an Exception occurs during data retrieval.
9604          * @param {Object} This DataProxy object.
9605          * @param {Object} o The data object.
9606          * @param {Object} arg The callback argument object passed to the load function.
9607          * @param {Object} e The Exception.
9608          */
9609         loadexception : true
9610     });
9611     Roo.data.DataProxy.superclass.constructor.call(this);
9612 };
9613
9614 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9615
9616     /**
9617      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9618      */
9619 /*
9620  * Based on:
9621  * Ext JS Library 1.1.1
9622  * Copyright(c) 2006-2007, Ext JS, LLC.
9623  *
9624  * Originally Released Under LGPL - original licence link has changed is not relivant.
9625  *
9626  * Fork - LGPL
9627  * <script type="text/javascript">
9628  */
9629 /**
9630  * @class Roo.data.MemoryProxy
9631  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9632  * to the Reader when its load method is called.
9633  * @constructor
9634  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9635  */
9636 Roo.data.MemoryProxy = function(data){
9637     if (data.data) {
9638         data = data.data;
9639     }
9640     Roo.data.MemoryProxy.superclass.constructor.call(this);
9641     this.data = data;
9642 };
9643
9644 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9645     /**
9646      * Load data from the requested source (in this case an in-memory
9647      * data object passed to the constructor), read the data object into
9648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9649      * process that block using the passed callback.
9650      * @param {Object} params This parameter is not used by the MemoryProxy class.
9651      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9652      * object into a block of Roo.data.Records.
9653      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9654      * The function must be passed <ul>
9655      * <li>The Record block object</li>
9656      * <li>The "arg" argument from the load function</li>
9657      * <li>A boolean success indicator</li>
9658      * </ul>
9659      * @param {Object} scope The scope in which to call the callback
9660      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9661      */
9662     load : function(params, reader, callback, scope, arg){
9663         params = params || {};
9664         var result;
9665         try {
9666             result = reader.readRecords(this.data);
9667         }catch(e){
9668             this.fireEvent("loadexception", this, arg, null, e);
9669             callback.call(scope, null, arg, false);
9670             return;
9671         }
9672         callback.call(scope, result, arg, true);
9673     },
9674     
9675     // private
9676     update : function(params, records){
9677         
9678     }
9679 });/*
9680  * Based on:
9681  * Ext JS Library 1.1.1
9682  * Copyright(c) 2006-2007, Ext JS, LLC.
9683  *
9684  * Originally Released Under LGPL - original licence link has changed is not relivant.
9685  *
9686  * Fork - LGPL
9687  * <script type="text/javascript">
9688  */
9689 /**
9690  * @class Roo.data.HttpProxy
9691  * @extends Roo.data.DataProxy
9692  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9693  * configured to reference a certain URL.<br><br>
9694  * <p>
9695  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9696  * from which the running page was served.<br><br>
9697  * <p>
9698  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9699  * <p>
9700  * Be aware that to enable the browser to parse an XML document, the server must set
9701  * the Content-Type header in the HTTP response to "text/xml".
9702  * @constructor
9703  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9704  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9705  * will be used to make the request.
9706  */
9707 Roo.data.HttpProxy = function(conn){
9708     Roo.data.HttpProxy.superclass.constructor.call(this);
9709     // is conn a conn config or a real conn?
9710     this.conn = conn;
9711     this.useAjax = !conn || !conn.events;
9712   
9713 };
9714
9715 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9716     // thse are take from connection...
9717     
9718     /**
9719      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9720      */
9721     /**
9722      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9723      * extra parameters to each request made by this object. (defaults to undefined)
9724      */
9725     /**
9726      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9727      *  to each request made by this object. (defaults to undefined)
9728      */
9729     /**
9730      * @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)
9731      */
9732     /**
9733      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9734      */
9735      /**
9736      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9737      * @type Boolean
9738      */
9739   
9740
9741     /**
9742      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9743      * @type Boolean
9744      */
9745     /**
9746      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9747      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9748      * a finer-grained basis than the DataProxy events.
9749      */
9750     getConnection : function(){
9751         return this.useAjax ? Roo.Ajax : this.conn;
9752     },
9753
9754     /**
9755      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9756      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9757      * process that block using the passed callback.
9758      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9759      * for the request to the remote server.
9760      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9761      * object into a block of Roo.data.Records.
9762      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9763      * The function must be passed <ul>
9764      * <li>The Record block object</li>
9765      * <li>The "arg" argument from the load function</li>
9766      * <li>A boolean success indicator</li>
9767      * </ul>
9768      * @param {Object} scope The scope in which to call the callback
9769      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9770      */
9771     load : function(params, reader, callback, scope, arg){
9772         if(this.fireEvent("beforeload", this, params) !== false){
9773             var  o = {
9774                 params : params || {},
9775                 request: {
9776                     callback : callback,
9777                     scope : scope,
9778                     arg : arg
9779                 },
9780                 reader: reader,
9781                 callback : this.loadResponse,
9782                 scope: this
9783             };
9784             if(this.useAjax){
9785                 Roo.applyIf(o, this.conn);
9786                 if(this.activeRequest){
9787                     Roo.Ajax.abort(this.activeRequest);
9788                 }
9789                 this.activeRequest = Roo.Ajax.request(o);
9790             }else{
9791                 this.conn.request(o);
9792             }
9793         }else{
9794             callback.call(scope||this, null, arg, false);
9795         }
9796     },
9797
9798     // private
9799     loadResponse : function(o, success, response){
9800         delete this.activeRequest;
9801         if(!success){
9802             this.fireEvent("loadexception", this, o, response);
9803             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9804             return;
9805         }
9806         var result;
9807         try {
9808             result = o.reader.read(response);
9809         }catch(e){
9810             this.fireEvent("loadexception", this, o, response, e);
9811             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9812             return;
9813         }
9814         
9815         this.fireEvent("load", this, o, o.request.arg);
9816         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9817     },
9818
9819     // private
9820     update : function(dataSet){
9821
9822     },
9823
9824     // private
9825     updateResponse : function(dataSet){
9826
9827     }
9828 });/*
9829  * Based on:
9830  * Ext JS Library 1.1.1
9831  * Copyright(c) 2006-2007, Ext JS, LLC.
9832  *
9833  * Originally Released Under LGPL - original licence link has changed is not relivant.
9834  *
9835  * Fork - LGPL
9836  * <script type="text/javascript">
9837  */
9838
9839 /**
9840  * @class Roo.data.ScriptTagProxy
9841  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9842  * other than the originating domain of the running page.<br><br>
9843  * <p>
9844  * <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
9845  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9846  * <p>
9847  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9848  * source code that is used as the source inside a &lt;script> tag.<br><br>
9849  * <p>
9850  * In order for the browser to process the returned data, the server must wrap the data object
9851  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9852  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9853  * depending on whether the callback name was passed:
9854  * <p>
9855  * <pre><code>
9856 boolean scriptTag = false;
9857 String cb = request.getParameter("callback");
9858 if (cb != null) {
9859     scriptTag = true;
9860     response.setContentType("text/javascript");
9861 } else {
9862     response.setContentType("application/x-json");
9863 }
9864 Writer out = response.getWriter();
9865 if (scriptTag) {
9866     out.write(cb + "(");
9867 }
9868 out.print(dataBlock.toJsonString());
9869 if (scriptTag) {
9870     out.write(");");
9871 }
9872 </pre></code>
9873  *
9874  * @constructor
9875  * @param {Object} config A configuration object.
9876  */
9877 Roo.data.ScriptTagProxy = function(config){
9878     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9879     Roo.apply(this, config);
9880     this.head = document.getElementsByTagName("head")[0];
9881 };
9882
9883 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9884
9885 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9886     /**
9887      * @cfg {String} url The URL from which to request the data object.
9888      */
9889     /**
9890      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9891      */
9892     timeout : 30000,
9893     /**
9894      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9895      * the server the name of the callback function set up by the load call to process the returned data object.
9896      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9897      * javascript output which calls this named function passing the data object as its only parameter.
9898      */
9899     callbackParam : "callback",
9900     /**
9901      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9902      * name to the request.
9903      */
9904     nocache : true,
9905
9906     /**
9907      * Load data from the configured URL, read the data object into
9908      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9909      * process that block using the passed callback.
9910      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9911      * for the request to the remote server.
9912      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9913      * object into a block of Roo.data.Records.
9914      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9915      * The function must be passed <ul>
9916      * <li>The Record block object</li>
9917      * <li>The "arg" argument from the load function</li>
9918      * <li>A boolean success indicator</li>
9919      * </ul>
9920      * @param {Object} scope The scope in which to call the callback
9921      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9922      */
9923     load : function(params, reader, callback, scope, arg){
9924         if(this.fireEvent("beforeload", this, params) !== false){
9925
9926             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9927
9928             var url = this.url;
9929             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9930             if(this.nocache){
9931                 url += "&_dc=" + (new Date().getTime());
9932             }
9933             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9934             var trans = {
9935                 id : transId,
9936                 cb : "stcCallback"+transId,
9937                 scriptId : "stcScript"+transId,
9938                 params : params,
9939                 arg : arg,
9940                 url : url,
9941                 callback : callback,
9942                 scope : scope,
9943                 reader : reader
9944             };
9945             var conn = this;
9946
9947             window[trans.cb] = function(o){
9948                 conn.handleResponse(o, trans);
9949             };
9950
9951             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9952
9953             if(this.autoAbort !== false){
9954                 this.abort();
9955             }
9956
9957             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9958
9959             var script = document.createElement("script");
9960             script.setAttribute("src", url);
9961             script.setAttribute("type", "text/javascript");
9962             script.setAttribute("id", trans.scriptId);
9963             this.head.appendChild(script);
9964
9965             this.trans = trans;
9966         }else{
9967             callback.call(scope||this, null, arg, false);
9968         }
9969     },
9970
9971     // private
9972     isLoading : function(){
9973         return this.trans ? true : false;
9974     },
9975
9976     /**
9977      * Abort the current server request.
9978      */
9979     abort : function(){
9980         if(this.isLoading()){
9981             this.destroyTrans(this.trans);
9982         }
9983     },
9984
9985     // private
9986     destroyTrans : function(trans, isLoaded){
9987         this.head.removeChild(document.getElementById(trans.scriptId));
9988         clearTimeout(trans.timeoutId);
9989         if(isLoaded){
9990             window[trans.cb] = undefined;
9991             try{
9992                 delete window[trans.cb];
9993             }catch(e){}
9994         }else{
9995             // if hasn't been loaded, wait for load to remove it to prevent script error
9996             window[trans.cb] = function(){
9997                 window[trans.cb] = undefined;
9998                 try{
9999                     delete window[trans.cb];
10000                 }catch(e){}
10001             };
10002         }
10003     },
10004
10005     // private
10006     handleResponse : function(o, trans){
10007         this.trans = false;
10008         this.destroyTrans(trans, true);
10009         var result;
10010         try {
10011             result = trans.reader.readRecords(o);
10012         }catch(e){
10013             this.fireEvent("loadexception", this, o, trans.arg, e);
10014             trans.callback.call(trans.scope||window, null, trans.arg, false);
10015             return;
10016         }
10017         this.fireEvent("load", this, o, trans.arg);
10018         trans.callback.call(trans.scope||window, result, trans.arg, true);
10019     },
10020
10021     // private
10022     handleFailure : function(trans){
10023         this.trans = false;
10024         this.destroyTrans(trans, false);
10025         this.fireEvent("loadexception", this, null, trans.arg);
10026         trans.callback.call(trans.scope||window, null, trans.arg, false);
10027     }
10028 });/*
10029  * Based on:
10030  * Ext JS Library 1.1.1
10031  * Copyright(c) 2006-2007, Ext JS, LLC.
10032  *
10033  * Originally Released Under LGPL - original licence link has changed is not relivant.
10034  *
10035  * Fork - LGPL
10036  * <script type="text/javascript">
10037  */
10038
10039 /**
10040  * @class Roo.data.JsonReader
10041  * @extends Roo.data.DataReader
10042  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10043  * based on mappings in a provided Roo.data.Record constructor.
10044  * 
10045  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10046  * in the reply previously. 
10047  * 
10048  * <p>
10049  * Example code:
10050  * <pre><code>
10051 var RecordDef = Roo.data.Record.create([
10052     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10053     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10054 ]);
10055 var myReader = new Roo.data.JsonReader({
10056     totalProperty: "results",    // The property which contains the total dataset size (optional)
10057     root: "rows",                // The property which contains an Array of row objects
10058     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10059 }, RecordDef);
10060 </code></pre>
10061  * <p>
10062  * This would consume a JSON file like this:
10063  * <pre><code>
10064 { 'results': 2, 'rows': [
10065     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10066     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10067 }
10068 </code></pre>
10069  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10070  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10071  * paged from the remote server.
10072  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10073  * @cfg {String} root name of the property which contains the Array of row objects.
10074  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10075  * @constructor
10076  * Create a new JsonReader
10077  * @param {Object} meta Metadata configuration options
10078  * @param {Object} recordType Either an Array of field definition objects,
10079  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10080  */
10081 Roo.data.JsonReader = function(meta, recordType){
10082     
10083     meta = meta || {};
10084     // set some defaults:
10085     Roo.applyIf(meta, {
10086         totalProperty: 'total',
10087         successProperty : 'success',
10088         root : 'data',
10089         id : 'id'
10090     });
10091     
10092     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10093 };
10094 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10095     
10096     /**
10097      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10098      * Used by Store query builder to append _requestMeta to params.
10099      * 
10100      */
10101     metaFromRemote : false,
10102     /**
10103      * This method is only used by a DataProxy which has retrieved data from a remote server.
10104      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10105      * @return {Object} data A data block which is used by an Roo.data.Store object as
10106      * a cache of Roo.data.Records.
10107      */
10108     read : function(response){
10109         var json = response.responseText;
10110        
10111         var o = /* eval:var:o */ eval("("+json+")");
10112         if(!o) {
10113             throw {message: "JsonReader.read: Json object not found"};
10114         }
10115         
10116         if(o.metaData){
10117             
10118             delete this.ef;
10119             this.metaFromRemote = true;
10120             this.meta = o.metaData;
10121             this.recordType = Roo.data.Record.create(o.metaData.fields);
10122             this.onMetaChange(this.meta, this.recordType, o);
10123         }
10124         return this.readRecords(o);
10125     },
10126
10127     // private function a store will implement
10128     onMetaChange : function(meta, recordType, o){
10129
10130     },
10131
10132     /**
10133          * @ignore
10134          */
10135     simpleAccess: function(obj, subsc) {
10136         return obj[subsc];
10137     },
10138
10139         /**
10140          * @ignore
10141          */
10142     getJsonAccessor: function(){
10143         var re = /[\[\.]/;
10144         return function(expr) {
10145             try {
10146                 return(re.test(expr))
10147                     ? new Function("obj", "return obj." + expr)
10148                     : function(obj){
10149                         return obj[expr];
10150                     };
10151             } catch(e){}
10152             return Roo.emptyFn;
10153         };
10154     }(),
10155
10156     /**
10157      * Create a data block containing Roo.data.Records from an XML document.
10158      * @param {Object} o An object which contains an Array of row objects in the property specified
10159      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10160      * which contains the total size of the dataset.
10161      * @return {Object} data A data block which is used by an Roo.data.Store object as
10162      * a cache of Roo.data.Records.
10163      */
10164     readRecords : function(o){
10165         /**
10166          * After any data loads, the raw JSON data is available for further custom processing.
10167          * @type Object
10168          */
10169         this.o = o;
10170         var s = this.meta, Record = this.recordType,
10171             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10172
10173 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10174         if (!this.ef) {
10175             if(s.totalProperty) {
10176                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10177                 }
10178                 if(s.successProperty) {
10179                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10180                 }
10181                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10182                 if (s.id) {
10183                         var g = this.getJsonAccessor(s.id);
10184                         this.getId = function(rec) {
10185                                 var r = g(rec);  
10186                                 return (r === undefined || r === "") ? null : r;
10187                         };
10188                 } else {
10189                         this.getId = function(){return null;};
10190                 }
10191             this.ef = [];
10192             for(var jj = 0; jj < fl; jj++){
10193                 f = fi[jj];
10194                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10195                 this.ef[jj] = this.getJsonAccessor(map);
10196             }
10197         }
10198
10199         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10200         if(s.totalProperty){
10201             var vt = parseInt(this.getTotal(o), 10);
10202             if(!isNaN(vt)){
10203                 totalRecords = vt;
10204             }
10205         }
10206         if(s.successProperty){
10207             var vs = this.getSuccess(o);
10208             if(vs === false || vs === 'false'){
10209                 success = false;
10210             }
10211         }
10212         var records = [];
10213         for(var i = 0; i < c; i++){
10214                 var n = root[i];
10215             var values = {};
10216             var id = this.getId(n);
10217             for(var j = 0; j < fl; j++){
10218                 f = fi[j];
10219             var v = this.ef[j](n);
10220             if (!f.convert) {
10221                 Roo.log('missing convert for ' + f.name);
10222                 Roo.log(f);
10223                 continue;
10224             }
10225             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10226             }
10227             var record = new Record(values, id);
10228             record.json = n;
10229             records[i] = record;
10230         }
10231         return {
10232             raw : o,
10233             success : success,
10234             records : records,
10235             totalRecords : totalRecords
10236         };
10237     }
10238 });/*
10239  * Based on:
10240  * Ext JS Library 1.1.1
10241  * Copyright(c) 2006-2007, Ext JS, LLC.
10242  *
10243  * Originally Released Under LGPL - original licence link has changed is not relivant.
10244  *
10245  * Fork - LGPL
10246  * <script type="text/javascript">
10247  */
10248
10249 /**
10250  * @class Roo.data.ArrayReader
10251  * @extends Roo.data.DataReader
10252  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10253  * Each element of that Array represents a row of data fields. The
10254  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10255  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10256  * <p>
10257  * Example code:.
10258  * <pre><code>
10259 var RecordDef = Roo.data.Record.create([
10260     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10261     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10262 ]);
10263 var myReader = new Roo.data.ArrayReader({
10264     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10265 }, RecordDef);
10266 </code></pre>
10267  * <p>
10268  * This would consume an Array like this:
10269  * <pre><code>
10270 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10271   </code></pre>
10272  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10273  * @constructor
10274  * Create a new JsonReader
10275  * @param {Object} meta Metadata configuration options.
10276  * @param {Object} recordType Either an Array of field definition objects
10277  * as specified to {@link Roo.data.Record#create},
10278  * or an {@link Roo.data.Record} object
10279  * created using {@link Roo.data.Record#create}.
10280  */
10281 Roo.data.ArrayReader = function(meta, recordType){
10282     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10283 };
10284
10285 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10286     /**
10287      * Create a data block containing Roo.data.Records from an XML document.
10288      * @param {Object} o An Array of row objects which represents the dataset.
10289      * @return {Object} data A data block which is used by an Roo.data.Store object as
10290      * a cache of Roo.data.Records.
10291      */
10292     readRecords : function(o){
10293         var sid = this.meta ? this.meta.id : null;
10294         var recordType = this.recordType, fields = recordType.prototype.fields;
10295         var records = [];
10296         var root = o;
10297             for(var i = 0; i < root.length; i++){
10298                     var n = root[i];
10299                 var values = {};
10300                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10301                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10302                 var f = fields.items[j];
10303                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10304                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10305                 v = f.convert(v);
10306                 values[f.name] = v;
10307             }
10308                 var record = new recordType(values, id);
10309                 record.json = n;
10310                 records[records.length] = record;
10311             }
10312             return {
10313                 records : records,
10314                 totalRecords : records.length
10315             };
10316     }
10317 });/*
10318  * - LGPL
10319  * * 
10320  */
10321
10322 /**
10323  * @class Roo.bootstrap.ComboBox
10324  * @extends Roo.bootstrap.TriggerField
10325  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10326  * @cfg {Boolean} append (true|false) default false
10327  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10328  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10329  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10330  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10331  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10332  * @constructor
10333  * Create a new ComboBox.
10334  * @param {Object} config Configuration options
10335  */
10336 Roo.bootstrap.ComboBox = function(config){
10337     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10338     this.addEvents({
10339         /**
10340          * @event expand
10341          * Fires when the dropdown list is expanded
10342              * @param {Roo.bootstrap.ComboBox} combo This combo box
10343              */
10344         'expand' : true,
10345         /**
10346          * @event collapse
10347          * Fires when the dropdown list is collapsed
10348              * @param {Roo.bootstrap.ComboBox} combo This combo box
10349              */
10350         'collapse' : true,
10351         /**
10352          * @event beforeselect
10353          * Fires before a list item is selected. Return false to cancel the selection.
10354              * @param {Roo.bootstrap.ComboBox} combo This combo box
10355              * @param {Roo.data.Record} record The data record returned from the underlying store
10356              * @param {Number} index The index of the selected item in the dropdown list
10357              */
10358         'beforeselect' : true,
10359         /**
10360          * @event select
10361          * Fires when a list item is selected
10362              * @param {Roo.bootstrap.ComboBox} combo This combo box
10363              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10364              * @param {Number} index The index of the selected item in the dropdown list
10365              */
10366         'select' : true,
10367         /**
10368          * @event beforequery
10369          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10370          * The event object passed has these properties:
10371              * @param {Roo.bootstrap.ComboBox} combo This combo box
10372              * @param {String} query The query
10373              * @param {Boolean} forceAll true to force "all" query
10374              * @param {Boolean} cancel true to cancel the query
10375              * @param {Object} e The query event object
10376              */
10377         'beforequery': true,
10378          /**
10379          * @event add
10380          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10381              * @param {Roo.bootstrap.ComboBox} combo This combo box
10382              */
10383         'add' : true,
10384         /**
10385          * @event edit
10386          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10387              * @param {Roo.bootstrap.ComboBox} combo This combo box
10388              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10389              */
10390         'edit' : true,
10391         /**
10392          * @event remove
10393          * Fires when the remove value from the combobox array
10394              * @param {Roo.bootstrap.ComboBox} combo This combo box
10395              */
10396         'remove' : true
10397         
10398     });
10399     
10400     this.item = [];
10401     this.tickItems = [];
10402     
10403     this.selectedIndex = -1;
10404     if(this.mode == 'local'){
10405         if(config.queryDelay === undefined){
10406             this.queryDelay = 10;
10407         }
10408         if(config.minChars === undefined){
10409             this.minChars = 0;
10410         }
10411     }
10412 };
10413
10414 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10415      
10416     /**
10417      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10418      * rendering into an Roo.Editor, defaults to false)
10419      */
10420     /**
10421      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10422      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10423      */
10424     /**
10425      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10426      */
10427     /**
10428      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10429      * the dropdown list (defaults to undefined, with no header element)
10430      */
10431
10432      /**
10433      * @cfg {String/Roo.Template} tpl The template to use to render the output
10434      */
10435      
10436      /**
10437      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10438      */
10439     listWidth: undefined,
10440     /**
10441      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10442      * mode = 'remote' or 'text' if mode = 'local')
10443      */
10444     displayField: undefined,
10445     /**
10446      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10447      * mode = 'remote' or 'value' if mode = 'local'). 
10448      * Note: use of a valueField requires the user make a selection
10449      * in order for a value to be mapped.
10450      */
10451     valueField: undefined,
10452     
10453     
10454     /**
10455      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10456      * field's data value (defaults to the underlying DOM element's name)
10457      */
10458     hiddenName: undefined,
10459     /**
10460      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10461      */
10462     listClass: '',
10463     /**
10464      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10465      */
10466     selectedClass: 'active',
10467     
10468     /**
10469      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10470      */
10471     shadow:'sides',
10472     /**
10473      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10474      * anchor positions (defaults to 'tl-bl')
10475      */
10476     listAlign: 'tl-bl?',
10477     /**
10478      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10479      */
10480     maxHeight: 300,
10481     /**
10482      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10483      * query specified by the allQuery config option (defaults to 'query')
10484      */
10485     triggerAction: 'query',
10486     /**
10487      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10488      * (defaults to 4, does not apply if editable = false)
10489      */
10490     minChars : 4,
10491     /**
10492      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10493      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10494      */
10495     typeAhead: false,
10496     /**
10497      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10498      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10499      */
10500     queryDelay: 500,
10501     /**
10502      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10503      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10504      */
10505     pageSize: 0,
10506     /**
10507      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10508      * when editable = true (defaults to false)
10509      */
10510     selectOnFocus:false,
10511     /**
10512      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10513      */
10514     queryParam: 'query',
10515     /**
10516      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10517      * when mode = 'remote' (defaults to 'Loading...')
10518      */
10519     loadingText: 'Loading...',
10520     /**
10521      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10522      */
10523     resizable: false,
10524     /**
10525      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10526      */
10527     handleHeight : 8,
10528     /**
10529      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10530      * traditional select (defaults to true)
10531      */
10532     editable: true,
10533     /**
10534      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10535      */
10536     allQuery: '',
10537     /**
10538      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10539      */
10540     mode: 'remote',
10541     /**
10542      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10543      * listWidth has a higher value)
10544      */
10545     minListWidth : 70,
10546     /**
10547      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10548      * allow the user to set arbitrary text into the field (defaults to false)
10549      */
10550     forceSelection:false,
10551     /**
10552      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10553      * if typeAhead = true (defaults to 250)
10554      */
10555     typeAheadDelay : 250,
10556     /**
10557      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10558      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10559      */
10560     valueNotFoundText : undefined,
10561     /**
10562      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10563      */
10564     blockFocus : false,
10565     
10566     /**
10567      * @cfg {Boolean} disableClear Disable showing of clear button.
10568      */
10569     disableClear : false,
10570     /**
10571      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10572      */
10573     alwaysQuery : false,
10574     
10575     /**
10576      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10577      */
10578     multiple : false,
10579     
10580     //private
10581     addicon : false,
10582     editicon: false,
10583     
10584     page: 0,
10585     hasQuery: false,
10586     append: false,
10587     loadNext: false,
10588     autoFocus : true,
10589     tickable : false,
10590     btnPosition : 'right',
10591     triggerList : true,
10592     showToggleBtn : true,
10593     // element that contains real text value.. (when hidden is used..)
10594     
10595     getAutoCreate : function()
10596     {
10597         var cfg = false;
10598         
10599         /*
10600          *  Normal ComboBox
10601          */
10602         if(!this.tickable){
10603             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10604             return cfg;
10605         }
10606         
10607         /*
10608          *  ComboBox with tickable selections
10609          */
10610              
10611         var align = this.labelAlign || this.parentLabelAlign();
10612         
10613         cfg = {
10614             cls : 'form-group roo-combobox-tickable' //input-group
10615         };
10616         
10617         
10618         var buttons = {
10619             tag : 'div',
10620             cls : 'tickable-buttons',
10621             cn : [
10622                 {
10623                     tag : 'button',
10624                     type : 'button',
10625                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10626                     html : 'Edit'
10627                 },
10628                 {
10629                     tag : 'button',
10630                     type : 'button',
10631                     name : 'ok',
10632                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10633                     html : 'Done'
10634                 },
10635                 {
10636                     tag : 'button',
10637                     type : 'button',
10638                     name : 'cancel',
10639                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10640                     html : 'Cancel'
10641                 }
10642             ]
10643         };
10644         
10645         var _this = this;
10646         Roo.each(buttons.cn, function(c){
10647             if (_this.size) {
10648                 c.cls += ' btn-' + _this.size;
10649             }
10650
10651             if (_this.disabled) {
10652                 c.disabled = true;
10653             }
10654         });
10655         
10656         var box = {
10657             tag: 'div',
10658             cn: [
10659                 {
10660                     tag: 'input',
10661                     type : 'hidden',
10662                     cls: 'form-hidden-field'
10663                 },
10664                 {
10665                     tag: 'ul',
10666                     cls: 'select2-choices',
10667                     cn:[
10668                         {
10669                             tag: 'li',
10670                             cls: 'select2-search-field',
10671                             cn: [
10672
10673                                 buttons
10674                             ]
10675                         }
10676                     ]
10677                 }
10678             ]
10679         }
10680         
10681         var combobox = {
10682             cls: 'select2-container input-group select2-container-multi',
10683             cn: [
10684                 box
10685 //                {
10686 //                    tag: 'ul',
10687 //                    cls: 'typeahead typeahead-long dropdown-menu',
10688 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10689 //                }
10690             ]
10691         };
10692         
10693         if (align ==='left' && this.fieldLabel.length) {
10694             
10695                 Roo.log("left and has label");
10696                 cfg.cn = [
10697                     
10698                     {
10699                         tag: 'label',
10700                         'for' :  id,
10701                         cls : 'control-label col-sm-' + this.labelWidth,
10702                         html : this.fieldLabel
10703                         
10704                     },
10705                     {
10706                         cls : "col-sm-" + (12 - this.labelWidth), 
10707                         cn: [
10708                             combobox
10709                         ]
10710                     }
10711                     
10712                 ];
10713         } else if ( this.fieldLabel.length) {
10714                 Roo.log(" label");
10715                  cfg.cn = [
10716                    
10717                     {
10718                         tag: 'label',
10719                         //cls : 'input-group-addon',
10720                         html : this.fieldLabel
10721                         
10722                     },
10723                     
10724                     combobox
10725                     
10726                 ];
10727
10728         } else {
10729             
10730                 Roo.log(" no label && no align");
10731                 cfg = combobox
10732                      
10733                 
10734         }
10735          
10736         var settings=this;
10737         ['xs','sm','md','lg'].map(function(size){
10738             if (settings[size]) {
10739                 cfg.cls += ' col-' + size + '-' + settings[size];
10740             }
10741         });
10742         
10743         return cfg;
10744         
10745     },
10746     
10747     // private
10748     initEvents: function()
10749     {
10750         
10751         if (!this.store) {
10752             throw "can not find store for combo";
10753         }
10754         this.store = Roo.factory(this.store, Roo.data);
10755         
10756         if(this.tickable){
10757             this.initTickableEvents();
10758             return;
10759         }
10760         
10761         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10762         
10763         if(this.hiddenName){
10764             
10765             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10766             
10767             this.hiddenField.dom.value =
10768                 this.hiddenValue !== undefined ? this.hiddenValue :
10769                 this.value !== undefined ? this.value : '';
10770
10771             // prevent input submission
10772             this.el.dom.removeAttribute('name');
10773             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10774              
10775              
10776         }
10777         //if(Roo.isGecko){
10778         //    this.el.dom.setAttribute('autocomplete', 'off');
10779         //}
10780         
10781         var cls = 'x-combo-list';
10782         
10783         //this.list = new Roo.Layer({
10784         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10785         //});
10786         
10787         var _this = this;
10788         
10789         (function(){
10790             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10791             _this.list.setWidth(lw);
10792         }).defer(100);
10793         
10794         this.list.on('mouseover', this.onViewOver, this);
10795         this.list.on('mousemove', this.onViewMove, this);
10796         
10797         this.list.on('scroll', this.onViewScroll, this);
10798         
10799         /*
10800         this.list.swallowEvent('mousewheel');
10801         this.assetHeight = 0;
10802
10803         if(this.title){
10804             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10805             this.assetHeight += this.header.getHeight();
10806         }
10807
10808         this.innerList = this.list.createChild({cls:cls+'-inner'});
10809         this.innerList.on('mouseover', this.onViewOver, this);
10810         this.innerList.on('mousemove', this.onViewMove, this);
10811         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10812         
10813         if(this.allowBlank && !this.pageSize && !this.disableClear){
10814             this.footer = this.list.createChild({cls:cls+'-ft'});
10815             this.pageTb = new Roo.Toolbar(this.footer);
10816            
10817         }
10818         if(this.pageSize){
10819             this.footer = this.list.createChild({cls:cls+'-ft'});
10820             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10821                     {pageSize: this.pageSize});
10822             
10823         }
10824         
10825         if (this.pageTb && this.allowBlank && !this.disableClear) {
10826             var _this = this;
10827             this.pageTb.add(new Roo.Toolbar.Fill(), {
10828                 cls: 'x-btn-icon x-btn-clear',
10829                 text: '&#160;',
10830                 handler: function()
10831                 {
10832                     _this.collapse();
10833                     _this.clearValue();
10834                     _this.onSelect(false, -1);
10835                 }
10836             });
10837         }
10838         if (this.footer) {
10839             this.assetHeight += this.footer.getHeight();
10840         }
10841         */
10842             
10843         if(!this.tpl){
10844             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10845         }
10846
10847         this.view = new Roo.View(this.list, this.tpl, {
10848             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10849         });
10850         //this.view.wrapEl.setDisplayed(false);
10851         this.view.on('click', this.onViewClick, this);
10852         
10853         
10854         
10855         this.store.on('beforeload', this.onBeforeLoad, this);
10856         this.store.on('load', this.onLoad, this);
10857         this.store.on('loadexception', this.onLoadException, this);
10858         /*
10859         if(this.resizable){
10860             this.resizer = new Roo.Resizable(this.list,  {
10861                pinned:true, handles:'se'
10862             });
10863             this.resizer.on('resize', function(r, w, h){
10864                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10865                 this.listWidth = w;
10866                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10867                 this.restrictHeight();
10868             }, this);
10869             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10870         }
10871         */
10872         if(!this.editable){
10873             this.editable = true;
10874             this.setEditable(false);
10875         }
10876         
10877         /*
10878         
10879         if (typeof(this.events.add.listeners) != 'undefined') {
10880             
10881             this.addicon = this.wrap.createChild(
10882                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10883        
10884             this.addicon.on('click', function(e) {
10885                 this.fireEvent('add', this);
10886             }, this);
10887         }
10888         if (typeof(this.events.edit.listeners) != 'undefined') {
10889             
10890             this.editicon = this.wrap.createChild(
10891                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10892             if (this.addicon) {
10893                 this.editicon.setStyle('margin-left', '40px');
10894             }
10895             this.editicon.on('click', function(e) {
10896                 
10897                 // we fire even  if inothing is selected..
10898                 this.fireEvent('edit', this, this.lastData );
10899                 
10900             }, this);
10901         }
10902         */
10903         
10904         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10905             "up" : function(e){
10906                 this.inKeyMode = true;
10907                 this.selectPrev();
10908             },
10909
10910             "down" : function(e){
10911                 if(!this.isExpanded()){
10912                     this.onTriggerClick();
10913                 }else{
10914                     this.inKeyMode = true;
10915                     this.selectNext();
10916                 }
10917             },
10918
10919             "enter" : function(e){
10920 //                this.onViewClick();
10921                 //return true;
10922                 this.collapse();
10923                 
10924                 if(this.fireEvent("specialkey", this, e)){
10925                     this.onViewClick(false);
10926                 }
10927                 
10928                 return true;
10929             },
10930
10931             "esc" : function(e){
10932                 this.collapse();
10933             },
10934
10935             "tab" : function(e){
10936                 this.collapse();
10937                 
10938                 if(this.fireEvent("specialkey", this, e)){
10939                     this.onViewClick(false);
10940                 }
10941                 
10942                 return true;
10943             },
10944
10945             scope : this,
10946
10947             doRelay : function(foo, bar, hname){
10948                 if(hname == 'down' || this.scope.isExpanded()){
10949                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10950                 }
10951                 return true;
10952             },
10953
10954             forceKeyDown: true
10955         });
10956         
10957         
10958         this.queryDelay = Math.max(this.queryDelay || 10,
10959                 this.mode == 'local' ? 10 : 250);
10960         
10961         
10962         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10963         
10964         if(this.typeAhead){
10965             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10966         }
10967         if(this.editable !== false){
10968             this.inputEl().on("keyup", this.onKeyUp, this);
10969         }
10970         if(this.forceSelection){
10971             this.inputEl().on('blur', this.doForce, this);
10972         }
10973         
10974         if(this.multiple){
10975             this.choices = this.el.select('ul.select2-choices', true).first();
10976             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10977         }
10978     },
10979     
10980     initTickableEvents: function()
10981     {   
10982         this.createList();
10983         
10984         if(this.hiddenName){
10985             
10986             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10987             
10988             this.hiddenField.dom.value =
10989                 this.hiddenValue !== undefined ? this.hiddenValue :
10990                 this.value !== undefined ? this.value : '';
10991
10992             // prevent input submission
10993             this.el.dom.removeAttribute('name');
10994             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10995              
10996              
10997         }
10998         
10999 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11000         
11001         this.choices = this.el.select('ul.select2-choices', true).first();
11002         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11003         if(this.triggerList){
11004             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11005         }
11006          
11007         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11008         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11009         
11010         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11011         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11012         
11013         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11014         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11015         
11016         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11017         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11018         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11019         
11020         this.okBtn.hide();
11021         this.cancelBtn.hide();
11022         
11023         var _this = this;
11024         
11025         (function(){
11026             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11027             _this.list.setWidth(lw);
11028         }).defer(100);
11029         
11030         this.list.on('mouseover', this.onViewOver, this);
11031         this.list.on('mousemove', this.onViewMove, this);
11032         
11033         this.list.on('scroll', this.onViewScroll, this);
11034         
11035         if(!this.tpl){
11036             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>';
11037         }
11038
11039         this.view = new Roo.View(this.list, this.tpl, {
11040             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11041         });
11042         
11043         //this.view.wrapEl.setDisplayed(false);
11044         this.view.on('click', this.onViewClick, this);
11045         
11046         
11047         
11048         this.store.on('beforeload', this.onBeforeLoad, this);
11049         this.store.on('load', this.onLoad, this);
11050         this.store.on('loadexception', this.onLoadException, this);
11051         
11052 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11053 //            "up" : function(e){
11054 //                this.inKeyMode = true;
11055 //                this.selectPrev();
11056 //            },
11057 //
11058 //            "down" : function(e){
11059 //                if(!this.isExpanded()){
11060 //                    this.onTriggerClick();
11061 //                }else{
11062 //                    this.inKeyMode = true;
11063 //                    this.selectNext();
11064 //                }
11065 //            },
11066 //
11067 //            "enter" : function(e){
11068 ////                this.onViewClick();
11069 //                //return true;
11070 //                this.collapse();
11071 //                
11072 //                if(this.fireEvent("specialkey", this, e)){
11073 //                    this.onViewClick(false);
11074 //                }
11075 //                
11076 //                return true;
11077 //            },
11078 //
11079 //            "esc" : function(e){
11080 //                this.collapse();
11081 //            },
11082 //
11083 //            "tab" : function(e){
11084 //                this.collapse();
11085 //                
11086 //                if(this.fireEvent("specialkey", this, e)){
11087 //                    this.onViewClick(false);
11088 //                }
11089 //                
11090 //                return true;
11091 //            },
11092 //
11093 //            scope : this,
11094 //
11095 //            doRelay : function(foo, bar, hname){
11096 //                if(hname == 'down' || this.scope.isExpanded()){
11097 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11098 //                }
11099 //                return true;
11100 //            },
11101 //
11102 //            forceKeyDown: true
11103 //        });
11104         
11105         
11106         this.queryDelay = Math.max(this.queryDelay || 10,
11107                 this.mode == 'local' ? 10 : 250);
11108         
11109         
11110         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11111         
11112         if(this.typeAhead){
11113             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11114         }
11115     },
11116
11117     onDestroy : function(){
11118         if(this.view){
11119             this.view.setStore(null);
11120             this.view.el.removeAllListeners();
11121             this.view.el.remove();
11122             this.view.purgeListeners();
11123         }
11124         if(this.list){
11125             this.list.dom.innerHTML  = '';
11126         }
11127         
11128         if(this.store){
11129             this.store.un('beforeload', this.onBeforeLoad, this);
11130             this.store.un('load', this.onLoad, this);
11131             this.store.un('loadexception', this.onLoadException, this);
11132         }
11133         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11134     },
11135
11136     // private
11137     fireKey : function(e){
11138         if(e.isNavKeyPress() && !this.list.isVisible()){
11139             this.fireEvent("specialkey", this, e);
11140         }
11141     },
11142
11143     // private
11144     onResize: function(w, h){
11145 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11146 //        
11147 //        if(typeof w != 'number'){
11148 //            // we do not handle it!?!?
11149 //            return;
11150 //        }
11151 //        var tw = this.trigger.getWidth();
11152 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11153 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11154 //        var x = w - tw;
11155 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11156 //            
11157 //        //this.trigger.setStyle('left', x+'px');
11158 //        
11159 //        if(this.list && this.listWidth === undefined){
11160 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11161 //            this.list.setWidth(lw);
11162 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11163 //        }
11164         
11165     
11166         
11167     },
11168
11169     /**
11170      * Allow or prevent the user from directly editing the field text.  If false is passed,
11171      * the user will only be able to select from the items defined in the dropdown list.  This method
11172      * is the runtime equivalent of setting the 'editable' config option at config time.
11173      * @param {Boolean} value True to allow the user to directly edit the field text
11174      */
11175     setEditable : function(value){
11176         if(value == this.editable){
11177             return;
11178         }
11179         this.editable = value;
11180         if(!value){
11181             this.inputEl().dom.setAttribute('readOnly', true);
11182             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11183             this.inputEl().addClass('x-combo-noedit');
11184         }else{
11185             this.inputEl().dom.setAttribute('readOnly', false);
11186             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11187             this.inputEl().removeClass('x-combo-noedit');
11188         }
11189     },
11190
11191     // private
11192     
11193     onBeforeLoad : function(combo,opts){
11194         if(!this.hasFocus){
11195             return;
11196         }
11197          if (!opts.add) {
11198             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11199          }
11200         this.restrictHeight();
11201         this.selectedIndex = -1;
11202     },
11203
11204     // private
11205     onLoad : function(){
11206         
11207         this.hasQuery = false;
11208         
11209         if(!this.hasFocus){
11210             return;
11211         }
11212         
11213         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11214             this.loading.hide();
11215         }
11216         
11217         if(this.store.getCount() > 0){
11218             this.expand();
11219 //            this.restrictHeight();
11220             if(this.lastQuery == this.allQuery){
11221                 if(this.editable && !this.tickable){
11222                     this.inputEl().dom.select();
11223                 }
11224                 
11225                 if(
11226                     !this.selectByValue(this.value, true) &&
11227                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11228                     this.store.lastOptions.add != true)
11229                 ){
11230                     this.select(0, true);
11231                 }
11232             }else{
11233                 if(this.autoFocus){
11234                     this.selectNext();
11235                 }
11236                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11237                     this.taTask.delay(this.typeAheadDelay);
11238                 }
11239             }
11240         }else{
11241             this.onEmptyResults();
11242         }
11243         
11244         //this.el.focus();
11245     },
11246     // private
11247     onLoadException : function()
11248     {
11249         this.hasQuery = false;
11250         
11251         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11252             this.loading.hide();
11253         }
11254         
11255         this.collapse();
11256         Roo.log(this.store.reader.jsonData);
11257         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11258             // fixme
11259             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11260         }
11261         
11262         
11263     },
11264     // private
11265     onTypeAhead : function(){
11266         if(this.store.getCount() > 0){
11267             var r = this.store.getAt(0);
11268             var newValue = r.data[this.displayField];
11269             var len = newValue.length;
11270             var selStart = this.getRawValue().length;
11271             
11272             if(selStart != len){
11273                 this.setRawValue(newValue);
11274                 this.selectText(selStart, newValue.length);
11275             }
11276         }
11277     },
11278
11279     // private
11280     onSelect : function(record, index){
11281         
11282         if(this.fireEvent('beforeselect', this, record, index) !== false){
11283         
11284             this.setFromData(index > -1 ? record.data : false);
11285             
11286             this.collapse();
11287             this.fireEvent('select', this, record, index);
11288         }
11289     },
11290
11291     /**
11292      * Returns the currently selected field value or empty string if no value is set.
11293      * @return {String} value The selected value
11294      */
11295     getValue : function(){
11296         
11297         if(this.multiple){
11298             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11299         }
11300         
11301         if(this.valueField){
11302             return typeof this.value != 'undefined' ? this.value : '';
11303         }else{
11304             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11305         }
11306     },
11307
11308     /**
11309      * Clears any text/value currently set in the field
11310      */
11311     clearValue : function(){
11312         if(this.hiddenField){
11313             this.hiddenField.dom.value = '';
11314         }
11315         this.value = '';
11316         this.setRawValue('');
11317         this.lastSelectionText = '';
11318         
11319     },
11320
11321     /**
11322      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11323      * will be displayed in the field.  If the value does not match the data value of an existing item,
11324      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11325      * Otherwise the field will be blank (although the value will still be set).
11326      * @param {String} value The value to match
11327      */
11328     setValue : function(v){
11329         if(this.multiple){
11330             this.syncValue();
11331             return;
11332         }
11333         
11334         var text = v;
11335         if(this.valueField){
11336             var r = this.findRecord(this.valueField, v);
11337             if(r){
11338                 text = r.data[this.displayField];
11339             }else if(this.valueNotFoundText !== undefined){
11340                 text = this.valueNotFoundText;
11341             }
11342         }
11343         this.lastSelectionText = text;
11344         if(this.hiddenField){
11345             this.hiddenField.dom.value = v;
11346         }
11347         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11348         this.value = v;
11349     },
11350     /**
11351      * @property {Object} the last set data for the element
11352      */
11353     
11354     lastData : false,
11355     /**
11356      * Sets the value of the field based on a object which is related to the record format for the store.
11357      * @param {Object} value the value to set as. or false on reset?
11358      */
11359     setFromData : function(o){
11360         
11361         if(this.multiple){
11362             this.addItem(o);
11363             return;
11364         }
11365             
11366         var dv = ''; // display value
11367         var vv = ''; // value value..
11368         this.lastData = o;
11369         if (this.displayField) {
11370             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11371         } else {
11372             // this is an error condition!!!
11373             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11374         }
11375         
11376         if(this.valueField){
11377             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11378         }
11379         
11380         if(this.hiddenField){
11381             this.hiddenField.dom.value = vv;
11382             
11383             this.lastSelectionText = dv;
11384             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11385             this.value = vv;
11386             return;
11387         }
11388         // no hidden field.. - we store the value in 'value', but still display
11389         // display field!!!!
11390         this.lastSelectionText = dv;
11391         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11392         this.value = vv;
11393         
11394         
11395     },
11396     // private
11397     reset : function(){
11398         // overridden so that last data is reset..
11399         this.setValue(this.originalValue);
11400         this.clearInvalid();
11401         this.lastData = false;
11402         if (this.view) {
11403             this.view.clearSelections();
11404         }
11405     },
11406     // private
11407     findRecord : function(prop, value){
11408         var record;
11409         if(this.store.getCount() > 0){
11410             this.store.each(function(r){
11411                 if(r.data[prop] == value){
11412                     record = r;
11413                     return false;
11414                 }
11415                 return true;
11416             });
11417         }
11418         return record;
11419     },
11420     
11421     getName: function()
11422     {
11423         // returns hidden if it's set..
11424         if (!this.rendered) {return ''};
11425         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11426         
11427     },
11428     // private
11429     onViewMove : function(e, t){
11430         this.inKeyMode = false;
11431     },
11432
11433     // private
11434     onViewOver : function(e, t){
11435         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11436             return;
11437         }
11438         var item = this.view.findItemFromChild(t);
11439         
11440         if(item){
11441             var index = this.view.indexOf(item);
11442             this.select(index, false);
11443         }
11444     },
11445
11446     // private
11447     onViewClick : function(view, doFocus, el, e)
11448     {
11449         var index = this.view.getSelectedIndexes()[0];
11450         
11451         var r = this.store.getAt(index);
11452         
11453         if(this.tickable){
11454             
11455             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11456                 return;
11457             }
11458             
11459             var rm = false;
11460             var _this = this;
11461             
11462             Roo.each(this.tickItems, function(v,k){
11463                 
11464                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11465                     _this.tickItems.splice(k, 1);
11466                     rm = true;
11467                     return;
11468                 }
11469             })
11470             
11471             if(rm){
11472                 return;
11473             }
11474             
11475             this.tickItems.push(r.data);
11476             return;
11477         }
11478         
11479         if(r){
11480             this.onSelect(r, index);
11481         }
11482         if(doFocus !== false && !this.blockFocus){
11483             this.inputEl().focus();
11484         }
11485     },
11486
11487     // private
11488     restrictHeight : function(){
11489         //this.innerList.dom.style.height = '';
11490         //var inner = this.innerList.dom;
11491         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11492         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11493         //this.list.beginUpdate();
11494         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11495         this.list.alignTo(this.inputEl(), this.listAlign);
11496         this.list.alignTo(this.inputEl(), this.listAlign);
11497         //this.list.endUpdate();
11498     },
11499
11500     // private
11501     onEmptyResults : function(){
11502         this.collapse();
11503     },
11504
11505     /**
11506      * Returns true if the dropdown list is expanded, else false.
11507      */
11508     isExpanded : function(){
11509         return this.list.isVisible();
11510     },
11511
11512     /**
11513      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11514      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11515      * @param {String} value The data value of the item to select
11516      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11517      * selected item if it is not currently in view (defaults to true)
11518      * @return {Boolean} True if the value matched an item in the list, else false
11519      */
11520     selectByValue : function(v, scrollIntoView){
11521         if(v !== undefined && v !== null){
11522             var r = this.findRecord(this.valueField || this.displayField, v);
11523             if(r){
11524                 this.select(this.store.indexOf(r), scrollIntoView);
11525                 return true;
11526             }
11527         }
11528         return false;
11529     },
11530
11531     /**
11532      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11533      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11534      * @param {Number} index The zero-based index of the list item to select
11535      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11536      * selected item if it is not currently in view (defaults to true)
11537      */
11538     select : function(index, scrollIntoView){
11539         this.selectedIndex = index;
11540         this.view.select(index);
11541         if(scrollIntoView !== false){
11542             var el = this.view.getNode(index);
11543             if(el && !this.multiple && !this.tickable){
11544                 this.list.scrollChildIntoView(el, false);
11545             }
11546         }
11547     },
11548
11549     // private
11550     selectNext : function(){
11551         var ct = this.store.getCount();
11552         if(ct > 0){
11553             if(this.selectedIndex == -1){
11554                 this.select(0);
11555             }else if(this.selectedIndex < ct-1){
11556                 this.select(this.selectedIndex+1);
11557             }
11558         }
11559     },
11560
11561     // private
11562     selectPrev : function(){
11563         var ct = this.store.getCount();
11564         if(ct > 0){
11565             if(this.selectedIndex == -1){
11566                 this.select(0);
11567             }else if(this.selectedIndex != 0){
11568                 this.select(this.selectedIndex-1);
11569             }
11570         }
11571     },
11572
11573     // private
11574     onKeyUp : function(e){
11575         if(this.editable !== false && !e.isSpecialKey()){
11576             this.lastKey = e.getKey();
11577             this.dqTask.delay(this.queryDelay);
11578         }
11579     },
11580
11581     // private
11582     validateBlur : function(){
11583         return !this.list || !this.list.isVisible();   
11584     },
11585
11586     // private
11587     initQuery : function(){
11588         this.doQuery(this.getRawValue());
11589     },
11590
11591     // private
11592     doForce : function(){
11593         if(this.inputEl().dom.value.length > 0){
11594             this.inputEl().dom.value =
11595                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11596              
11597         }
11598     },
11599
11600     /**
11601      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11602      * query allowing the query action to be canceled if needed.
11603      * @param {String} query The SQL query to execute
11604      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11605      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11606      * saved in the current store (defaults to false)
11607      */
11608     doQuery : function(q, forceAll){
11609         
11610         if(q === undefined || q === null){
11611             q = '';
11612         }
11613         var qe = {
11614             query: q,
11615             forceAll: forceAll,
11616             combo: this,
11617             cancel:false
11618         };
11619         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11620             return false;
11621         }
11622         q = qe.query;
11623         
11624         forceAll = qe.forceAll;
11625         if(forceAll === true || (q.length >= this.minChars)){
11626             
11627             this.hasQuery = true;
11628             
11629             if(this.lastQuery != q || this.alwaysQuery){
11630                 this.lastQuery = q;
11631                 if(this.mode == 'local'){
11632                     this.selectedIndex = -1;
11633                     if(forceAll){
11634                         this.store.clearFilter();
11635                     }else{
11636                         this.store.filter(this.displayField, q);
11637                     }
11638                     this.onLoad();
11639                 }else{
11640                     this.store.baseParams[this.queryParam] = q;
11641                     
11642                     var options = {params : this.getParams(q)};
11643                     
11644                     if(this.loadNext){
11645                         options.add = true;
11646                         options.params.start = this.page * this.pageSize;
11647                     }
11648                     
11649                     this.store.load(options);
11650                     /*
11651                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11652                      *  we should expand the list on onLoad
11653                      *  so command out it
11654                      */
11655 //                    this.expand();
11656                 }
11657             }else{
11658                 this.selectedIndex = -1;
11659                 this.onLoad();   
11660             }
11661         }
11662         
11663         this.loadNext = false;
11664     },
11665
11666     // private
11667     getParams : function(q){
11668         var p = {};
11669         //p[this.queryParam] = q;
11670         
11671         if(this.pageSize){
11672             p.start = 0;
11673             p.limit = this.pageSize;
11674         }
11675         return p;
11676     },
11677
11678     /**
11679      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11680      */
11681     collapse : function(){
11682         if(!this.isExpanded()){
11683             return;
11684         }
11685         
11686         this.list.hide();
11687         
11688         if(this.tickable){
11689             this.okBtn.hide();
11690             this.cancelBtn.hide();
11691             this.trigger.show();
11692         }
11693         
11694         Roo.get(document).un('mousedown', this.collapseIf, this);
11695         Roo.get(document).un('mousewheel', this.collapseIf, this);
11696         if (!this.editable) {
11697             Roo.get(document).un('keydown', this.listKeyPress, this);
11698         }
11699         this.fireEvent('collapse', this);
11700     },
11701
11702     // private
11703     collapseIf : function(e){
11704         var in_combo  = e.within(this.el);
11705         var in_list =  e.within(this.list);
11706         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11707         
11708         if (in_combo || in_list || is_list) {
11709             //e.stopPropagation();
11710             return;
11711         }
11712         
11713         if(this.tickable){
11714             this.onTickableFooterButtonClick(e, false, false);
11715         }
11716
11717         this.collapse();
11718         
11719     },
11720
11721     /**
11722      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11723      */
11724     expand : function(){
11725        
11726         if(this.isExpanded() || !this.hasFocus){
11727             return;
11728         }
11729         
11730         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11731         this.list.setWidth(lw);
11732         
11733         
11734          Roo.log('expand');
11735         
11736         this.list.show();
11737         
11738         this.restrictHeight();
11739         
11740         if(this.tickable){
11741             
11742             this.tickItems = Roo.apply([], this.item);
11743             
11744             this.okBtn.show();
11745             this.cancelBtn.show();
11746             this.trigger.hide();
11747             
11748         }
11749         
11750         Roo.get(document).on('mousedown', this.collapseIf, this);
11751         Roo.get(document).on('mousewheel', this.collapseIf, this);
11752         if (!this.editable) {
11753             Roo.get(document).on('keydown', this.listKeyPress, this);
11754         }
11755         
11756         this.fireEvent('expand', this);
11757     },
11758
11759     // private
11760     // Implements the default empty TriggerField.onTriggerClick function
11761     onTriggerClick : function(e)
11762     {
11763         Roo.log('trigger click');
11764         
11765         if(this.disabled || !this.triggerList){
11766             return;
11767         }
11768         
11769         this.page = 0;
11770         this.loadNext = false;
11771         
11772         if(this.isExpanded()){
11773             this.collapse();
11774             if (!this.blockFocus) {
11775                 this.inputEl().focus();
11776             }
11777             
11778         }else {
11779             this.hasFocus = true;
11780             if(this.triggerAction == 'all') {
11781                 this.doQuery(this.allQuery, true);
11782             } else {
11783                 this.doQuery(this.getRawValue());
11784             }
11785             if (!this.blockFocus) {
11786                 this.inputEl().focus();
11787             }
11788         }
11789     },
11790     
11791     onTickableTriggerClick : function(e)
11792     {
11793         if(this.disabled){
11794             return;
11795         }
11796         
11797         this.page = 0;
11798         this.loadNext = false;
11799         this.hasFocus = true;
11800         
11801         if(this.triggerAction == 'all') {
11802             this.doQuery(this.allQuery, true);
11803         } else {
11804             this.doQuery(this.getRawValue());
11805         }
11806     },
11807     
11808     onSearchFieldClick : function(e)
11809     {
11810         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11811             return;
11812         }
11813         
11814         this.page = 0;
11815         this.loadNext = false;
11816         this.hasFocus = true;
11817         
11818         if(this.triggerAction == 'all') {
11819             this.doQuery(this.allQuery, true);
11820         } else {
11821             this.doQuery(this.getRawValue());
11822         }
11823     },
11824     
11825     listKeyPress : function(e)
11826     {
11827         //Roo.log('listkeypress');
11828         // scroll to first matching element based on key pres..
11829         if (e.isSpecialKey()) {
11830             return false;
11831         }
11832         var k = String.fromCharCode(e.getKey()).toUpperCase();
11833         //Roo.log(k);
11834         var match  = false;
11835         var csel = this.view.getSelectedNodes();
11836         var cselitem = false;
11837         if (csel.length) {
11838             var ix = this.view.indexOf(csel[0]);
11839             cselitem  = this.store.getAt(ix);
11840             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11841                 cselitem = false;
11842             }
11843             
11844         }
11845         
11846         this.store.each(function(v) { 
11847             if (cselitem) {
11848                 // start at existing selection.
11849                 if (cselitem.id == v.id) {
11850                     cselitem = false;
11851                 }
11852                 return true;
11853             }
11854                 
11855             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11856                 match = this.store.indexOf(v);
11857                 return false;
11858             }
11859             return true;
11860         }, this);
11861         
11862         if (match === false) {
11863             return true; // no more action?
11864         }
11865         // scroll to?
11866         this.view.select(match);
11867         var sn = Roo.get(this.view.getSelectedNodes()[0])
11868         sn.scrollIntoView(sn.dom.parentNode, false);
11869     },
11870     
11871     onViewScroll : function(e, t){
11872         
11873         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){
11874             return;
11875         }
11876         
11877         this.hasQuery = true;
11878         
11879         this.loading = this.list.select('.loading', true).first();
11880         
11881         if(this.loading === null){
11882             this.list.createChild({
11883                 tag: 'div',
11884                 cls: 'loading select2-more-results select2-active',
11885                 html: 'Loading more results...'
11886             })
11887             
11888             this.loading = this.list.select('.loading', true).first();
11889             
11890             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11891             
11892             this.loading.hide();
11893         }
11894         
11895         this.loading.show();
11896         
11897         var _combo = this;
11898         
11899         this.page++;
11900         this.loadNext = true;
11901         
11902         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11903         
11904         return;
11905     },
11906     
11907     addItem : function(o)
11908     {   
11909         var dv = ''; // display value
11910         
11911         if (this.displayField) {
11912             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11913         } else {
11914             // this is an error condition!!!
11915             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11916         }
11917         
11918         if(!dv.length){
11919             return;
11920         }
11921         
11922         var choice = this.choices.createChild({
11923             tag: 'li',
11924             cls: 'select2-search-choice',
11925             cn: [
11926                 {
11927                     tag: 'div',
11928                     html: dv
11929                 },
11930                 {
11931                     tag: 'a',
11932                     href: '#',
11933                     cls: 'select2-search-choice-close',
11934                     tabindex: '-1'
11935                 }
11936             ]
11937             
11938         }, this.searchField);
11939         
11940         var close = choice.select('a.select2-search-choice-close', true).first()
11941         
11942         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11943         
11944         this.item.push(o);
11945         
11946         this.lastData = o;
11947         
11948         this.syncValue();
11949         
11950         this.inputEl().dom.value = '';
11951         
11952     },
11953     
11954     onRemoveItem : function(e, _self, o)
11955     {
11956         e.preventDefault();
11957         var index = this.item.indexOf(o.data) * 1;
11958         
11959         if( index < 0){
11960             Roo.log('not this item?!');
11961             return;
11962         }
11963         
11964         this.item.splice(index, 1);
11965         o.item.remove();
11966         
11967         this.syncValue();
11968         
11969         this.fireEvent('remove', this, e);
11970         
11971     },
11972     
11973     syncValue : function()
11974     {
11975         if(!this.item.length){
11976             this.clearValue();
11977             return;
11978         }
11979             
11980         var value = [];
11981         var _this = this;
11982         Roo.each(this.item, function(i){
11983             if(_this.valueField){
11984                 value.push(i[_this.valueField]);
11985                 return;
11986             }
11987
11988             value.push(i);
11989         });
11990
11991         this.value = value.join(',');
11992
11993         if(this.hiddenField){
11994             this.hiddenField.dom.value = this.value;
11995         }
11996     },
11997     
11998     clearItem : function()
11999     {
12000         if(!this.multiple){
12001             return;
12002         }
12003         
12004         this.item = [];
12005         
12006         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12007            c.remove();
12008         });
12009         
12010         this.syncValue();
12011     },
12012     
12013     inputEl: function ()
12014     {
12015         if(this.tickable){
12016             return this.searchField;
12017         }
12018         return this.el.select('input.form-control',true).first();
12019     },
12020     
12021     
12022     onTickableFooterButtonClick : function(e, btn, el)
12023     {
12024         e.preventDefault();
12025         
12026         if(btn && btn.name == 'cancel'){
12027             this.tickItems = Roo.apply([], this.item);
12028             this.collapse();
12029             return;
12030         }
12031         
12032         this.clearItem();
12033         
12034         var _this = this;
12035         
12036         Roo.each(this.tickItems, function(o){
12037             _this.addItem(o);
12038         });
12039         
12040         this.collapse();
12041         
12042     },
12043     
12044     validate : function()
12045     {
12046         var v = this.getRawValue();
12047         
12048         if(this.multiple){
12049             v = this.getValue();
12050         }
12051         
12052         if(this.disabled || this.validateValue(v)){
12053             this.clearInvalid();
12054             return true;
12055         }
12056         return false;
12057     }
12058     
12059     
12060
12061     /** 
12062     * @cfg {Boolean} grow 
12063     * @hide 
12064     */
12065     /** 
12066     * @cfg {Number} growMin 
12067     * @hide 
12068     */
12069     /** 
12070     * @cfg {Number} growMax 
12071     * @hide 
12072     */
12073     /**
12074      * @hide
12075      * @method autoSize
12076      */
12077 });
12078 /*
12079  * Based on:
12080  * Ext JS Library 1.1.1
12081  * Copyright(c) 2006-2007, Ext JS, LLC.
12082  *
12083  * Originally Released Under LGPL - original licence link has changed is not relivant.
12084  *
12085  * Fork - LGPL
12086  * <script type="text/javascript">
12087  */
12088
12089 /**
12090  * @class Roo.View
12091  * @extends Roo.util.Observable
12092  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12093  * This class also supports single and multi selection modes. <br>
12094  * Create a data model bound view:
12095  <pre><code>
12096  var store = new Roo.data.Store(...);
12097
12098  var view = new Roo.View({
12099     el : "my-element",
12100     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12101  
12102     singleSelect: true,
12103     selectedClass: "ydataview-selected",
12104     store: store
12105  });
12106
12107  // listen for node click?
12108  view.on("click", function(vw, index, node, e){
12109  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12110  });
12111
12112  // load XML data
12113  dataModel.load("foobar.xml");
12114  </code></pre>
12115  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12116  * <br><br>
12117  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12118  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12119  * 
12120  * Note: old style constructor is still suported (container, template, config)
12121  * 
12122  * @constructor
12123  * Create a new View
12124  * @param {Object} config The config object
12125  * 
12126  */
12127 Roo.View = function(config, depreciated_tpl, depreciated_config){
12128     
12129     this.parent = false;
12130     
12131     if (typeof(depreciated_tpl) == 'undefined') {
12132         // new way.. - universal constructor.
12133         Roo.apply(this, config);
12134         this.el  = Roo.get(this.el);
12135     } else {
12136         // old format..
12137         this.el  = Roo.get(config);
12138         this.tpl = depreciated_tpl;
12139         Roo.apply(this, depreciated_config);
12140     }
12141     this.wrapEl  = this.el.wrap().wrap();
12142     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12143     
12144     
12145     if(typeof(this.tpl) == "string"){
12146         this.tpl = new Roo.Template(this.tpl);
12147     } else {
12148         // support xtype ctors..
12149         this.tpl = new Roo.factory(this.tpl, Roo);
12150     }
12151     
12152     
12153     this.tpl.compile();
12154     
12155     /** @private */
12156     this.addEvents({
12157         /**
12158          * @event beforeclick
12159          * Fires before a click is processed. Returns false to cancel the default action.
12160          * @param {Roo.View} this
12161          * @param {Number} index The index of the target node
12162          * @param {HTMLElement} node The target node
12163          * @param {Roo.EventObject} e The raw event object
12164          */
12165             "beforeclick" : true,
12166         /**
12167          * @event click
12168          * Fires when a template node is clicked.
12169          * @param {Roo.View} this
12170          * @param {Number} index The index of the target node
12171          * @param {HTMLElement} node The target node
12172          * @param {Roo.EventObject} e The raw event object
12173          */
12174             "click" : true,
12175         /**
12176          * @event dblclick
12177          * Fires when a template node is double clicked.
12178          * @param {Roo.View} this
12179          * @param {Number} index The index of the target node
12180          * @param {HTMLElement} node The target node
12181          * @param {Roo.EventObject} e The raw event object
12182          */
12183             "dblclick" : true,
12184         /**
12185          * @event contextmenu
12186          * Fires when a template node is right clicked.
12187          * @param {Roo.View} this
12188          * @param {Number} index The index of the target node
12189          * @param {HTMLElement} node The target node
12190          * @param {Roo.EventObject} e The raw event object
12191          */
12192             "contextmenu" : true,
12193         /**
12194          * @event selectionchange
12195          * Fires when the selected nodes change.
12196          * @param {Roo.View} this
12197          * @param {Array} selections Array of the selected nodes
12198          */
12199             "selectionchange" : true,
12200     
12201         /**
12202          * @event beforeselect
12203          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12204          * @param {Roo.View} this
12205          * @param {HTMLElement} node The node to be selected
12206          * @param {Array} selections Array of currently selected nodes
12207          */
12208             "beforeselect" : true,
12209         /**
12210          * @event preparedata
12211          * Fires on every row to render, to allow you to change the data.
12212          * @param {Roo.View} this
12213          * @param {Object} data to be rendered (change this)
12214          */
12215           "preparedata" : true
12216           
12217           
12218         });
12219
12220
12221
12222     this.el.on({
12223         "click": this.onClick,
12224         "dblclick": this.onDblClick,
12225         "contextmenu": this.onContextMenu,
12226         scope:this
12227     });
12228
12229     this.selections = [];
12230     this.nodes = [];
12231     this.cmp = new Roo.CompositeElementLite([]);
12232     if(this.store){
12233         this.store = Roo.factory(this.store, Roo.data);
12234         this.setStore(this.store, true);
12235     }
12236     
12237     if ( this.footer && this.footer.xtype) {
12238            
12239          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12240         
12241         this.footer.dataSource = this.store
12242         this.footer.container = fctr;
12243         this.footer = Roo.factory(this.footer, Roo);
12244         fctr.insertFirst(this.el);
12245         
12246         // this is a bit insane - as the paging toolbar seems to detach the el..
12247 //        dom.parentNode.parentNode.parentNode
12248          // they get detached?
12249     }
12250     
12251     
12252     Roo.View.superclass.constructor.call(this);
12253     
12254     
12255 };
12256
12257 Roo.extend(Roo.View, Roo.util.Observable, {
12258     
12259      /**
12260      * @cfg {Roo.data.Store} store Data store to load data from.
12261      */
12262     store : false,
12263     
12264     /**
12265      * @cfg {String|Roo.Element} el The container element.
12266      */
12267     el : '',
12268     
12269     /**
12270      * @cfg {String|Roo.Template} tpl The template used by this View 
12271      */
12272     tpl : false,
12273     /**
12274      * @cfg {String} dataName the named area of the template to use as the data area
12275      *                          Works with domtemplates roo-name="name"
12276      */
12277     dataName: false,
12278     /**
12279      * @cfg {String} selectedClass The css class to add to selected nodes
12280      */
12281     selectedClass : "x-view-selected",
12282      /**
12283      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12284      */
12285     emptyText : "",
12286     
12287     /**
12288      * @cfg {String} text to display on mask (default Loading)
12289      */
12290     mask : false,
12291     /**
12292      * @cfg {Boolean} multiSelect Allow multiple selection
12293      */
12294     multiSelect : false,
12295     /**
12296      * @cfg {Boolean} singleSelect Allow single selection
12297      */
12298     singleSelect:  false,
12299     
12300     /**
12301      * @cfg {Boolean} toggleSelect - selecting 
12302      */
12303     toggleSelect : false,
12304     
12305     /**
12306      * @cfg {Boolean} tickable - selecting 
12307      */
12308     tickable : false,
12309     
12310     /**
12311      * Returns the element this view is bound to.
12312      * @return {Roo.Element}
12313      */
12314     getEl : function(){
12315         return this.wrapEl;
12316     },
12317     
12318     
12319
12320     /**
12321      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12322      */
12323     refresh : function(){
12324         Roo.log('refresh');
12325         var t = this.tpl;
12326         
12327         // if we are using something like 'domtemplate', then
12328         // the what gets used is:
12329         // t.applySubtemplate(NAME, data, wrapping data..)
12330         // the outer template then get' applied with
12331         //     the store 'extra data'
12332         // and the body get's added to the
12333         //      roo-name="data" node?
12334         //      <span class='roo-tpl-{name}'></span> ?????
12335         
12336         
12337         
12338         this.clearSelections();
12339         this.el.update("");
12340         var html = [];
12341         var records = this.store.getRange();
12342         if(records.length < 1) {
12343             
12344             // is this valid??  = should it render a template??
12345             
12346             this.el.update(this.emptyText);
12347             return;
12348         }
12349         var el = this.el;
12350         if (this.dataName) {
12351             this.el.update(t.apply(this.store.meta)); //????
12352             el = this.el.child('.roo-tpl-' + this.dataName);
12353         }
12354         
12355         for(var i = 0, len = records.length; i < len; i++){
12356             var data = this.prepareData(records[i].data, i, records[i]);
12357             this.fireEvent("preparedata", this, data, i, records[i]);
12358             
12359             var d = Roo.apply({}, data);
12360             
12361             if(this.tickable){
12362                 Roo.apply(d, {'roo-id' : Roo.id()});
12363                 
12364                 var _this = this;
12365             
12366                 Roo.each(this.parent.item, function(item){
12367                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12368                         return;
12369                     }
12370                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12371                 });
12372             }
12373             
12374             html[html.length] = Roo.util.Format.trim(
12375                 this.dataName ?
12376                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12377                     t.apply(d)
12378             );
12379         }
12380         
12381         
12382         
12383         el.update(html.join(""));
12384         this.nodes = el.dom.childNodes;
12385         this.updateIndexes(0);
12386     },
12387     
12388
12389     /**
12390      * Function to override to reformat the data that is sent to
12391      * the template for each node.
12392      * DEPRICATED - use the preparedata event handler.
12393      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12394      * a JSON object for an UpdateManager bound view).
12395      */
12396     prepareData : function(data, index, record)
12397     {
12398         this.fireEvent("preparedata", this, data, index, record);
12399         return data;
12400     },
12401
12402     onUpdate : function(ds, record){
12403          Roo.log('on update');   
12404         this.clearSelections();
12405         var index = this.store.indexOf(record);
12406         var n = this.nodes[index];
12407         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12408         n.parentNode.removeChild(n);
12409         this.updateIndexes(index, index);
12410     },
12411
12412     
12413     
12414 // --------- FIXME     
12415     onAdd : function(ds, records, index)
12416     {
12417         Roo.log(['on Add', ds, records, index] );        
12418         this.clearSelections();
12419         if(this.nodes.length == 0){
12420             this.refresh();
12421             return;
12422         }
12423         var n = this.nodes[index];
12424         for(var i = 0, len = records.length; i < len; i++){
12425             var d = this.prepareData(records[i].data, i, records[i]);
12426             if(n){
12427                 this.tpl.insertBefore(n, d);
12428             }else{
12429                 
12430                 this.tpl.append(this.el, d);
12431             }
12432         }
12433         this.updateIndexes(index);
12434     },
12435
12436     onRemove : function(ds, record, index){
12437         Roo.log('onRemove');
12438         this.clearSelections();
12439         var el = this.dataName  ?
12440             this.el.child('.roo-tpl-' + this.dataName) :
12441             this.el; 
12442         
12443         el.dom.removeChild(this.nodes[index]);
12444         this.updateIndexes(index);
12445     },
12446
12447     /**
12448      * Refresh an individual node.
12449      * @param {Number} index
12450      */
12451     refreshNode : function(index){
12452         this.onUpdate(this.store, this.store.getAt(index));
12453     },
12454
12455     updateIndexes : function(startIndex, endIndex){
12456         var ns = this.nodes;
12457         startIndex = startIndex || 0;
12458         endIndex = endIndex || ns.length - 1;
12459         for(var i = startIndex; i <= endIndex; i++){
12460             ns[i].nodeIndex = i;
12461         }
12462     },
12463
12464     /**
12465      * Changes the data store this view uses and refresh the view.
12466      * @param {Store} store
12467      */
12468     setStore : function(store, initial){
12469         if(!initial && this.store){
12470             this.store.un("datachanged", this.refresh);
12471             this.store.un("add", this.onAdd);
12472             this.store.un("remove", this.onRemove);
12473             this.store.un("update", this.onUpdate);
12474             this.store.un("clear", this.refresh);
12475             this.store.un("beforeload", this.onBeforeLoad);
12476             this.store.un("load", this.onLoad);
12477             this.store.un("loadexception", this.onLoad);
12478         }
12479         if(store){
12480           
12481             store.on("datachanged", this.refresh, this);
12482             store.on("add", this.onAdd, this);
12483             store.on("remove", this.onRemove, this);
12484             store.on("update", this.onUpdate, this);
12485             store.on("clear", this.refresh, this);
12486             store.on("beforeload", this.onBeforeLoad, this);
12487             store.on("load", this.onLoad, this);
12488             store.on("loadexception", this.onLoad, this);
12489         }
12490         
12491         if(store){
12492             this.refresh();
12493         }
12494     },
12495     /**
12496      * onbeforeLoad - masks the loading area.
12497      *
12498      */
12499     onBeforeLoad : function(store,opts)
12500     {
12501          Roo.log('onBeforeLoad');   
12502         if (!opts.add) {
12503             this.el.update("");
12504         }
12505         this.el.mask(this.mask ? this.mask : "Loading" ); 
12506     },
12507     onLoad : function ()
12508     {
12509         this.el.unmask();
12510     },
12511     
12512
12513     /**
12514      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12515      * @param {HTMLElement} node
12516      * @return {HTMLElement} The template node
12517      */
12518     findItemFromChild : function(node){
12519         var el = this.dataName  ?
12520             this.el.child('.roo-tpl-' + this.dataName,true) :
12521             this.el.dom; 
12522         
12523         if(!node || node.parentNode == el){
12524                     return node;
12525             }
12526             var p = node.parentNode;
12527             while(p && p != el){
12528             if(p.parentNode == el){
12529                 return p;
12530             }
12531             p = p.parentNode;
12532         }
12533             return null;
12534     },
12535
12536     /** @ignore */
12537     onClick : function(e){
12538         var item = this.findItemFromChild(e.getTarget());
12539         if(item){
12540             var index = this.indexOf(item);
12541             if(this.onItemClick(item, index, e) !== false){
12542                 this.fireEvent("click", this, index, item, e);
12543             }
12544         }else{
12545             this.clearSelections();
12546         }
12547     },
12548
12549     /** @ignore */
12550     onContextMenu : function(e){
12551         var item = this.findItemFromChild(e.getTarget());
12552         if(item){
12553             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12554         }
12555     },
12556
12557     /** @ignore */
12558     onDblClick : function(e){
12559         var item = this.findItemFromChild(e.getTarget());
12560         if(item){
12561             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12562         }
12563     },
12564
12565     onItemClick : function(item, index, e)
12566     {
12567         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12568             return false;
12569         }
12570         if (this.toggleSelect) {
12571             var m = this.isSelected(item) ? 'unselect' : 'select';
12572             Roo.log(m);
12573             var _t = this;
12574             _t[m](item, true, false);
12575             return true;
12576         }
12577         if(this.multiSelect || this.singleSelect){
12578             if(this.multiSelect && e.shiftKey && this.lastSelection){
12579                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12580             }else{
12581                 this.select(item, this.multiSelect && e.ctrlKey);
12582                 this.lastSelection = item;
12583             }
12584             
12585             if(!this.tickable){
12586                 e.preventDefault();
12587             }
12588             
12589         }
12590         return true;
12591     },
12592
12593     /**
12594      * Get the number of selected nodes.
12595      * @return {Number}
12596      */
12597     getSelectionCount : function(){
12598         return this.selections.length;
12599     },
12600
12601     /**
12602      * Get the currently selected nodes.
12603      * @return {Array} An array of HTMLElements
12604      */
12605     getSelectedNodes : function(){
12606         return this.selections;
12607     },
12608
12609     /**
12610      * Get the indexes of the selected nodes.
12611      * @return {Array}
12612      */
12613     getSelectedIndexes : function(){
12614         var indexes = [], s = this.selections;
12615         for(var i = 0, len = s.length; i < len; i++){
12616             indexes.push(s[i].nodeIndex);
12617         }
12618         return indexes;
12619     },
12620
12621     /**
12622      * Clear all selections
12623      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12624      */
12625     clearSelections : function(suppressEvent){
12626         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12627             this.cmp.elements = this.selections;
12628             this.cmp.removeClass(this.selectedClass);
12629             this.selections = [];
12630             if(!suppressEvent){
12631                 this.fireEvent("selectionchange", this, this.selections);
12632             }
12633         }
12634     },
12635
12636     /**
12637      * Returns true if the passed node is selected
12638      * @param {HTMLElement/Number} node The node or node index
12639      * @return {Boolean}
12640      */
12641     isSelected : function(node){
12642         var s = this.selections;
12643         if(s.length < 1){
12644             return false;
12645         }
12646         node = this.getNode(node);
12647         return s.indexOf(node) !== -1;
12648     },
12649
12650     /**
12651      * Selects nodes.
12652      * @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
12653      * @param {Boolean} keepExisting (optional) true to keep existing selections
12654      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12655      */
12656     select : function(nodeInfo, keepExisting, suppressEvent){
12657         if(nodeInfo instanceof Array){
12658             if(!keepExisting){
12659                 this.clearSelections(true);
12660             }
12661             for(var i = 0, len = nodeInfo.length; i < len; i++){
12662                 this.select(nodeInfo[i], true, true);
12663             }
12664             return;
12665         } 
12666         var node = this.getNode(nodeInfo);
12667         if(!node || this.isSelected(node)){
12668             return; // already selected.
12669         }
12670         if(!keepExisting){
12671             this.clearSelections(true);
12672         }
12673         
12674         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12675             Roo.fly(node).addClass(this.selectedClass);
12676             this.selections.push(node);
12677             if(!suppressEvent){
12678                 this.fireEvent("selectionchange", this, this.selections);
12679             }
12680         }
12681         
12682         
12683     },
12684       /**
12685      * Unselects nodes.
12686      * @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
12687      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12688      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12689      */
12690     unselect : function(nodeInfo, keepExisting, suppressEvent)
12691     {
12692         if(nodeInfo instanceof Array){
12693             Roo.each(this.selections, function(s) {
12694                 this.unselect(s, nodeInfo);
12695             }, this);
12696             return;
12697         }
12698         var node = this.getNode(nodeInfo);
12699         if(!node || !this.isSelected(node)){
12700             Roo.log("not selected");
12701             return; // not selected.
12702         }
12703         // fireevent???
12704         var ns = [];
12705         Roo.each(this.selections, function(s) {
12706             if (s == node ) {
12707                 Roo.fly(node).removeClass(this.selectedClass);
12708
12709                 return;
12710             }
12711             ns.push(s);
12712         },this);
12713         
12714         this.selections= ns;
12715         this.fireEvent("selectionchange", this, this.selections);
12716     },
12717
12718     /**
12719      * Gets a template node.
12720      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12721      * @return {HTMLElement} The node or null if it wasn't found
12722      */
12723     getNode : function(nodeInfo){
12724         if(typeof nodeInfo == "string"){
12725             return document.getElementById(nodeInfo);
12726         }else if(typeof nodeInfo == "number"){
12727             return this.nodes[nodeInfo];
12728         }
12729         return nodeInfo;
12730     },
12731
12732     /**
12733      * Gets a range template nodes.
12734      * @param {Number} startIndex
12735      * @param {Number} endIndex
12736      * @return {Array} An array of nodes
12737      */
12738     getNodes : function(start, end){
12739         var ns = this.nodes;
12740         start = start || 0;
12741         end = typeof end == "undefined" ? ns.length - 1 : end;
12742         var nodes = [];
12743         if(start <= end){
12744             for(var i = start; i <= end; i++){
12745                 nodes.push(ns[i]);
12746             }
12747         } else{
12748             for(var i = start; i >= end; i--){
12749                 nodes.push(ns[i]);
12750             }
12751         }
12752         return nodes;
12753     },
12754
12755     /**
12756      * Finds the index of the passed node
12757      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12758      * @return {Number} The index of the node or -1
12759      */
12760     indexOf : function(node){
12761         node = this.getNode(node);
12762         if(typeof node.nodeIndex == "number"){
12763             return node.nodeIndex;
12764         }
12765         var ns = this.nodes;
12766         for(var i = 0, len = ns.length; i < len; i++){
12767             if(ns[i] == node){
12768                 return i;
12769             }
12770         }
12771         return -1;
12772     }
12773 });
12774 /*
12775  * - LGPL
12776  *
12777  * based on jquery fullcalendar
12778  * 
12779  */
12780
12781 Roo.bootstrap = Roo.bootstrap || {};
12782 /**
12783  * @class Roo.bootstrap.Calendar
12784  * @extends Roo.bootstrap.Component
12785  * Bootstrap Calendar class
12786  * @cfg {Boolean} loadMask (true|false) default false
12787  * @cfg {Object} header generate the user specific header of the calendar, default false
12788
12789  * @constructor
12790  * Create a new Container
12791  * @param {Object} config The config object
12792  */
12793
12794
12795
12796 Roo.bootstrap.Calendar = function(config){
12797     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12798      this.addEvents({
12799         /**
12800              * @event select
12801              * Fires when a date is selected
12802              * @param {DatePicker} this
12803              * @param {Date} date The selected date
12804              */
12805         'select': true,
12806         /**
12807              * @event monthchange
12808              * Fires when the displayed month changes 
12809              * @param {DatePicker} this
12810              * @param {Date} date The selected month
12811              */
12812         'monthchange': true,
12813         /**
12814              * @event evententer
12815              * Fires when mouse over an event
12816              * @param {Calendar} this
12817              * @param {event} Event
12818              */
12819         'evententer': true,
12820         /**
12821              * @event eventleave
12822              * Fires when the mouse leaves an
12823              * @param {Calendar} this
12824              * @param {event}
12825              */
12826         'eventleave': true,
12827         /**
12828              * @event eventclick
12829              * Fires when the mouse click an
12830              * @param {Calendar} this
12831              * @param {event}
12832              */
12833         'eventclick': true
12834         
12835     });
12836
12837 };
12838
12839 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12840     
12841      /**
12842      * @cfg {Number} startDay
12843      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12844      */
12845     startDay : 0,
12846     
12847     loadMask : false,
12848     
12849     header : false,
12850       
12851     getAutoCreate : function(){
12852         
12853         
12854         var fc_button = function(name, corner, style, content ) {
12855             return Roo.apply({},{
12856                 tag : 'span',
12857                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12858                          (corner.length ?
12859                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12860                             ''
12861                         ),
12862                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12863                 unselectable: 'on'
12864             });
12865         };
12866         
12867         var header = {};
12868         
12869         if(!this.header){
12870             header = {
12871                 tag : 'table',
12872                 cls : 'fc-header',
12873                 style : 'width:100%',
12874                 cn : [
12875                     {
12876                         tag: 'tr',
12877                         cn : [
12878                             {
12879                                 tag : 'td',
12880                                 cls : 'fc-header-left',
12881                                 cn : [
12882                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12883                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12884                                     { tag: 'span', cls: 'fc-header-space' },
12885                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12886
12887
12888                                 ]
12889                             },
12890
12891                             {
12892                                 tag : 'td',
12893                                 cls : 'fc-header-center',
12894                                 cn : [
12895                                     {
12896                                         tag: 'span',
12897                                         cls: 'fc-header-title',
12898                                         cn : {
12899                                             tag: 'H2',
12900                                             html : 'month / year'
12901                                         }
12902                                     }
12903
12904                                 ]
12905                             },
12906                             {
12907                                 tag : 'td',
12908                                 cls : 'fc-header-right',
12909                                 cn : [
12910                               /*      fc_button('month', 'left', '', 'month' ),
12911                                     fc_button('week', '', '', 'week' ),
12912                                     fc_button('day', 'right', '', 'day' )
12913                                 */    
12914
12915                                 ]
12916                             }
12917
12918                         ]
12919                     }
12920                 ]
12921             };
12922         }
12923         
12924         header = this.header;
12925         
12926        
12927         var cal_heads = function() {
12928             var ret = [];
12929             // fixme - handle this.
12930             
12931             for (var i =0; i < Date.dayNames.length; i++) {
12932                 var d = Date.dayNames[i];
12933                 ret.push({
12934                     tag: 'th',
12935                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12936                     html : d.substring(0,3)
12937                 });
12938                 
12939             }
12940             ret[0].cls += ' fc-first';
12941             ret[6].cls += ' fc-last';
12942             return ret;
12943         };
12944         var cal_cell = function(n) {
12945             return  {
12946                 tag: 'td',
12947                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12948                 cn : [
12949                     {
12950                         cn : [
12951                             {
12952                                 cls: 'fc-day-number',
12953                                 html: 'D'
12954                             },
12955                             {
12956                                 cls: 'fc-day-content',
12957                              
12958                                 cn : [
12959                                      {
12960                                         style: 'position: relative;' // height: 17px;
12961                                     }
12962                                 ]
12963                             }
12964                             
12965                             
12966                         ]
12967                     }
12968                 ]
12969                 
12970             }
12971         };
12972         var cal_rows = function() {
12973             
12974             var ret = []
12975             for (var r = 0; r < 6; r++) {
12976                 var row= {
12977                     tag : 'tr',
12978                     cls : 'fc-week',
12979                     cn : []
12980                 };
12981                 
12982                 for (var i =0; i < Date.dayNames.length; i++) {
12983                     var d = Date.dayNames[i];
12984                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12985
12986                 }
12987                 row.cn[0].cls+=' fc-first';
12988                 row.cn[0].cn[0].style = 'min-height:90px';
12989                 row.cn[6].cls+=' fc-last';
12990                 ret.push(row);
12991                 
12992             }
12993             ret[0].cls += ' fc-first';
12994             ret[4].cls += ' fc-prev-last';
12995             ret[5].cls += ' fc-last';
12996             return ret;
12997             
12998         };
12999         
13000         var cal_table = {
13001             tag: 'table',
13002             cls: 'fc-border-separate',
13003             style : 'width:100%',
13004             cellspacing  : 0,
13005             cn : [
13006                 { 
13007                     tag: 'thead',
13008                     cn : [
13009                         { 
13010                             tag: 'tr',
13011                             cls : 'fc-first fc-last',
13012                             cn : cal_heads()
13013                         }
13014                     ]
13015                 },
13016                 { 
13017                     tag: 'tbody',
13018                     cn : cal_rows()
13019                 }
13020                   
13021             ]
13022         };
13023          
13024          var cfg = {
13025             cls : 'fc fc-ltr',
13026             cn : [
13027                 header,
13028                 {
13029                     cls : 'fc-content',
13030                     style : "position: relative;",
13031                     cn : [
13032                         {
13033                             cls : 'fc-view fc-view-month fc-grid',
13034                             style : 'position: relative',
13035                             unselectable : 'on',
13036                             cn : [
13037                                 {
13038                                     cls : 'fc-event-container',
13039                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13040                                 },
13041                                 cal_table
13042                             ]
13043                         }
13044                     ]
13045     
13046                 }
13047            ] 
13048             
13049         };
13050         
13051          
13052         
13053         return cfg;
13054     },
13055     
13056     
13057     initEvents : function()
13058     {
13059         if(!this.store){
13060             throw "can not find store for calendar";
13061         }
13062         
13063         var mark = {
13064             tag: "div",
13065             cls:"x-dlg-mask",
13066             style: "text-align:center",
13067             cn: [
13068                 {
13069                     tag: "div",
13070                     style: "background-color:white;width:50%;margin:250 auto",
13071                     cn: [
13072                         {
13073                             tag: "img",
13074                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13075                         },
13076                         {
13077                             tag: "span",
13078                             html: "Loading"
13079                         }
13080                         
13081                     ]
13082                 }
13083             ]
13084         }
13085         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13086         
13087         var size = this.el.select('.fc-content', true).first().getSize();
13088         this.maskEl.setSize(size.width, size.height);
13089         this.maskEl.enableDisplayMode("block");
13090         if(!this.loadMask){
13091             this.maskEl.hide();
13092         }
13093         
13094         this.store = Roo.factory(this.store, Roo.data);
13095         this.store.on('load', this.onLoad, this);
13096         this.store.on('beforeload', this.onBeforeLoad, this);
13097         
13098         this.resize();
13099         
13100         this.cells = this.el.select('.fc-day',true);
13101         //Roo.log(this.cells);
13102         this.textNodes = this.el.query('.fc-day-number');
13103         this.cells.addClassOnOver('fc-state-hover');
13104         
13105         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13106         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13107         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13108         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13109         
13110         this.on('monthchange', this.onMonthChange, this);
13111         
13112         this.update(new Date().clearTime());
13113     },
13114     
13115     resize : function() {
13116         var sz  = this.el.getSize();
13117         
13118         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13119         this.el.select('.fc-day-content div',true).setHeight(34);
13120     },
13121     
13122     
13123     // private
13124     showPrevMonth : function(e){
13125         this.update(this.activeDate.add("mo", -1));
13126     },
13127     showToday : function(e){
13128         this.update(new Date().clearTime());
13129     },
13130     // private
13131     showNextMonth : function(e){
13132         this.update(this.activeDate.add("mo", 1));
13133     },
13134
13135     // private
13136     showPrevYear : function(){
13137         this.update(this.activeDate.add("y", -1));
13138     },
13139
13140     // private
13141     showNextYear : function(){
13142         this.update(this.activeDate.add("y", 1));
13143     },
13144
13145     
13146    // private
13147     update : function(date)
13148     {
13149         var vd = this.activeDate;
13150         this.activeDate = date;
13151 //        if(vd && this.el){
13152 //            var t = date.getTime();
13153 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13154 //                Roo.log('using add remove');
13155 //                
13156 //                this.fireEvent('monthchange', this, date);
13157 //                
13158 //                this.cells.removeClass("fc-state-highlight");
13159 //                this.cells.each(function(c){
13160 //                   if(c.dateValue == t){
13161 //                       c.addClass("fc-state-highlight");
13162 //                       setTimeout(function(){
13163 //                            try{c.dom.firstChild.focus();}catch(e){}
13164 //                       }, 50);
13165 //                       return false;
13166 //                   }
13167 //                   return true;
13168 //                });
13169 //                return;
13170 //            }
13171 //        }
13172         
13173         var days = date.getDaysInMonth();
13174         
13175         var firstOfMonth = date.getFirstDateOfMonth();
13176         var startingPos = firstOfMonth.getDay()-this.startDay;
13177         
13178         if(startingPos < this.startDay){
13179             startingPos += 7;
13180         }
13181         
13182         var pm = date.add(Date.MONTH, -1);
13183         var prevStart = pm.getDaysInMonth()-startingPos;
13184 //        
13185         this.cells = this.el.select('.fc-day',true);
13186         this.textNodes = this.el.query('.fc-day-number');
13187         this.cells.addClassOnOver('fc-state-hover');
13188         
13189         var cells = this.cells.elements;
13190         var textEls = this.textNodes;
13191         
13192         Roo.each(cells, function(cell){
13193             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13194         });
13195         
13196         days += startingPos;
13197
13198         // convert everything to numbers so it's fast
13199         var day = 86400000;
13200         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13201         //Roo.log(d);
13202         //Roo.log(pm);
13203         //Roo.log(prevStart);
13204         
13205         var today = new Date().clearTime().getTime();
13206         var sel = date.clearTime().getTime();
13207         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13208         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13209         var ddMatch = this.disabledDatesRE;
13210         var ddText = this.disabledDatesText;
13211         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13212         var ddaysText = this.disabledDaysText;
13213         var format = this.format;
13214         
13215         var setCellClass = function(cal, cell){
13216             cell.row = 0;
13217             cell.events = [];
13218             cell.more = [];
13219             //Roo.log('set Cell Class');
13220             cell.title = "";
13221             var t = d.getTime();
13222             
13223             //Roo.log(d);
13224             
13225             cell.dateValue = t;
13226             if(t == today){
13227                 cell.className += " fc-today";
13228                 cell.className += " fc-state-highlight";
13229                 cell.title = cal.todayText;
13230             }
13231             if(t == sel){
13232                 // disable highlight in other month..
13233                 //cell.className += " fc-state-highlight";
13234                 
13235             }
13236             // disabling
13237             if(t < min) {
13238                 cell.className = " fc-state-disabled";
13239                 cell.title = cal.minText;
13240                 return;
13241             }
13242             if(t > max) {
13243                 cell.className = " fc-state-disabled";
13244                 cell.title = cal.maxText;
13245                 return;
13246             }
13247             if(ddays){
13248                 if(ddays.indexOf(d.getDay()) != -1){
13249                     cell.title = ddaysText;
13250                     cell.className = " fc-state-disabled";
13251                 }
13252             }
13253             if(ddMatch && format){
13254                 var fvalue = d.dateFormat(format);
13255                 if(ddMatch.test(fvalue)){
13256                     cell.title = ddText.replace("%0", fvalue);
13257                     cell.className = " fc-state-disabled";
13258                 }
13259             }
13260             
13261             if (!cell.initialClassName) {
13262                 cell.initialClassName = cell.dom.className;
13263             }
13264             
13265             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13266         };
13267
13268         var i = 0;
13269         
13270         for(; i < startingPos; i++) {
13271             textEls[i].innerHTML = (++prevStart);
13272             d.setDate(d.getDate()+1);
13273             
13274             cells[i].className = "fc-past fc-other-month";
13275             setCellClass(this, cells[i]);
13276         }
13277         
13278         var intDay = 0;
13279         
13280         for(; i < days; i++){
13281             intDay = i - startingPos + 1;
13282             textEls[i].innerHTML = (intDay);
13283             d.setDate(d.getDate()+1);
13284             
13285             cells[i].className = ''; // "x-date-active";
13286             setCellClass(this, cells[i]);
13287         }
13288         var extraDays = 0;
13289         
13290         for(; i < 42; i++) {
13291             textEls[i].innerHTML = (++extraDays);
13292             d.setDate(d.getDate()+1);
13293             
13294             cells[i].className = "fc-future fc-other-month";
13295             setCellClass(this, cells[i]);
13296         }
13297         
13298         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13299         
13300         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13301         
13302         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13303         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13304         
13305         if(totalRows != 6){
13306             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13307             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13308         }
13309         
13310         this.fireEvent('monthchange', this, date);
13311         
13312         
13313         /*
13314         if(!this.internalRender){
13315             var main = this.el.dom.firstChild;
13316             var w = main.offsetWidth;
13317             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13318             Roo.fly(main).setWidth(w);
13319             this.internalRender = true;
13320             // opera does not respect the auto grow header center column
13321             // then, after it gets a width opera refuses to recalculate
13322             // without a second pass
13323             if(Roo.isOpera && !this.secondPass){
13324                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13325                 this.secondPass = true;
13326                 this.update.defer(10, this, [date]);
13327             }
13328         }
13329         */
13330         
13331     },
13332     
13333     findCell : function(dt) {
13334         dt = dt.clearTime().getTime();
13335         var ret = false;
13336         this.cells.each(function(c){
13337             //Roo.log("check " +c.dateValue + '?=' + dt);
13338             if(c.dateValue == dt){
13339                 ret = c;
13340                 return false;
13341             }
13342             return true;
13343         });
13344         
13345         return ret;
13346     },
13347     
13348     findCells : function(ev) {
13349         var s = ev.start.clone().clearTime().getTime();
13350        // Roo.log(s);
13351         var e= ev.end.clone().clearTime().getTime();
13352        // Roo.log(e);
13353         var ret = [];
13354         this.cells.each(function(c){
13355              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13356             
13357             if(c.dateValue > e){
13358                 return ;
13359             }
13360             if(c.dateValue < s){
13361                 return ;
13362             }
13363             ret.push(c);
13364         });
13365         
13366         return ret;    
13367     },
13368     
13369 //    findBestRow: function(cells)
13370 //    {
13371 //        var ret = 0;
13372 //        
13373 //        for (var i =0 ; i < cells.length;i++) {
13374 //            ret  = Math.max(cells[i].rows || 0,ret);
13375 //        }
13376 //        return ret;
13377 //        
13378 //    },
13379     
13380     
13381     addItem : function(ev)
13382     {
13383         // look for vertical location slot in
13384         var cells = this.findCells(ev);
13385         
13386 //        ev.row = this.findBestRow(cells);
13387         
13388         // work out the location.
13389         
13390         var crow = false;
13391         var rows = [];
13392         for(var i =0; i < cells.length; i++) {
13393             
13394             cells[i].row = cells[0].row;
13395             
13396             if(i == 0){
13397                 cells[i].row = cells[i].row + 1;
13398             }
13399             
13400             if (!crow) {
13401                 crow = {
13402                     start : cells[i],
13403                     end :  cells[i]
13404                 };
13405                 continue;
13406             }
13407             if (crow.start.getY() == cells[i].getY()) {
13408                 // on same row.
13409                 crow.end = cells[i];
13410                 continue;
13411             }
13412             // different row.
13413             rows.push(crow);
13414             crow = {
13415                 start: cells[i],
13416                 end : cells[i]
13417             };
13418             
13419         }
13420         
13421         rows.push(crow);
13422         ev.els = [];
13423         ev.rows = rows;
13424         ev.cells = cells;
13425         
13426         cells[0].events.push(ev);
13427         
13428         this.calevents.push(ev);
13429     },
13430     
13431     clearEvents: function() {
13432         
13433         if(!this.calevents){
13434             return;
13435         }
13436         
13437         Roo.each(this.cells.elements, function(c){
13438             c.row = 0;
13439             c.events = [];
13440             c.more = [];
13441         });
13442         
13443         Roo.each(this.calevents, function(e) {
13444             Roo.each(e.els, function(el) {
13445                 el.un('mouseenter' ,this.onEventEnter, this);
13446                 el.un('mouseleave' ,this.onEventLeave, this);
13447                 el.remove();
13448             },this);
13449         },this);
13450         
13451         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13452             e.remove();
13453         });
13454         
13455     },
13456     
13457     renderEvents: function()
13458     {   
13459         var _this = this;
13460         
13461         this.cells.each(function(c) {
13462             
13463             if(c.row < 5){
13464                 return;
13465             }
13466             
13467             var ev = c.events;
13468             
13469             var r = 4;
13470             if(c.row != c.events.length){
13471                 r = 4 - (4 - (c.row - c.events.length));
13472             }
13473             
13474             c.events = ev.slice(0, r);
13475             c.more = ev.slice(r);
13476             
13477             if(c.more.length && c.more.length == 1){
13478                 c.events.push(c.more.pop());
13479             }
13480             
13481             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13482             
13483         });
13484             
13485         this.cells.each(function(c) {
13486             
13487             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13488             
13489             
13490             for (var e = 0; e < c.events.length; e++){
13491                 var ev = c.events[e];
13492                 var rows = ev.rows;
13493                 
13494                 for(var i = 0; i < rows.length; i++) {
13495                 
13496                     // how many rows should it span..
13497
13498                     var  cfg = {
13499                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13500                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13501
13502                         unselectable : "on",
13503                         cn : [
13504                             {
13505                                 cls: 'fc-event-inner',
13506                                 cn : [
13507     //                                {
13508     //                                  tag:'span',
13509     //                                  cls: 'fc-event-time',
13510     //                                  html : cells.length > 1 ? '' : ev.time
13511     //                                },
13512                                     {
13513                                       tag:'span',
13514                                       cls: 'fc-event-title',
13515                                       html : String.format('{0}', ev.title)
13516                                     }
13517
13518
13519                                 ]
13520                             },
13521                             {
13522                                 cls: 'ui-resizable-handle ui-resizable-e',
13523                                 html : '&nbsp;&nbsp;&nbsp'
13524                             }
13525
13526                         ]
13527                     };
13528
13529                     if (i == 0) {
13530                         cfg.cls += ' fc-event-start';
13531                     }
13532                     if ((i+1) == rows.length) {
13533                         cfg.cls += ' fc-event-end';
13534                     }
13535
13536                     var ctr = _this.el.select('.fc-event-container',true).first();
13537                     var cg = ctr.createChild(cfg);
13538
13539                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13540                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13541
13542                     var r = (c.more.length) ? 1 : 0;
13543                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13544                     cg.setWidth(ebox.right - sbox.x -2);
13545
13546                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13547                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13548                     cg.on('click', _this.onEventClick, _this, ev);
13549
13550                     ev.els.push(cg);
13551                     
13552                 }
13553                 
13554             }
13555             
13556             
13557             if(c.more.length){
13558                 var  cfg = {
13559                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13560                     style : 'position: absolute',
13561                     unselectable : "on",
13562                     cn : [
13563                         {
13564                             cls: 'fc-event-inner',
13565                             cn : [
13566                                 {
13567                                   tag:'span',
13568                                   cls: 'fc-event-title',
13569                                   html : 'More'
13570                                 }
13571
13572
13573                             ]
13574                         },
13575                         {
13576                             cls: 'ui-resizable-handle ui-resizable-e',
13577                             html : '&nbsp;&nbsp;&nbsp'
13578                         }
13579
13580                     ]
13581                 };
13582
13583                 var ctr = _this.el.select('.fc-event-container',true).first();
13584                 var cg = ctr.createChild(cfg);
13585
13586                 var sbox = c.select('.fc-day-content',true).first().getBox();
13587                 var ebox = c.select('.fc-day-content',true).first().getBox();
13588                 //Roo.log(cg);
13589                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13590                 cg.setWidth(ebox.right - sbox.x -2);
13591
13592                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13593                 
13594             }
13595             
13596         });
13597         
13598         
13599         
13600     },
13601     
13602     onEventEnter: function (e, el,event,d) {
13603         this.fireEvent('evententer', this, el, event);
13604     },
13605     
13606     onEventLeave: function (e, el,event,d) {
13607         this.fireEvent('eventleave', this, el, event);
13608     },
13609     
13610     onEventClick: function (e, el,event,d) {
13611         this.fireEvent('eventclick', this, el, event);
13612     },
13613     
13614     onMonthChange: function () {
13615         this.store.load();
13616     },
13617     
13618     onMoreEventClick: function(e, el, more)
13619     {
13620         var _this = this;
13621         
13622         this.calpopover.placement = 'right';
13623         this.calpopover.setTitle('More');
13624         
13625         this.calpopover.setContent('');
13626         
13627         var ctr = this.calpopover.el.select('.popover-content', true).first();
13628         
13629         Roo.each(more, function(m){
13630             var cfg = {
13631                 cls : 'fc-event-hori fc-event-draggable',
13632                 html : m.title
13633             }
13634             var cg = ctr.createChild(cfg);
13635             
13636             cg.on('click', _this.onEventClick, _this, m);
13637         });
13638         
13639         this.calpopover.show(el);
13640         
13641         
13642     },
13643     
13644     onLoad: function () 
13645     {   
13646         this.calevents = [];
13647         var cal = this;
13648         
13649         if(this.store.getCount() > 0){
13650             this.store.data.each(function(d){
13651                cal.addItem({
13652                     id : d.data.id,
13653                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13654                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13655                     time : d.data.start_time,
13656                     title : d.data.title,
13657                     description : d.data.description,
13658                     venue : d.data.venue
13659                 });
13660             });
13661         }
13662         
13663         this.renderEvents();
13664         
13665         if(this.calevents.length && this.loadMask){
13666             this.maskEl.hide();
13667         }
13668     },
13669     
13670     onBeforeLoad: function()
13671     {
13672         this.clearEvents();
13673         if(this.loadMask){
13674             this.maskEl.show();
13675         }
13676     }
13677 });
13678
13679  
13680  /*
13681  * - LGPL
13682  *
13683  * element
13684  * 
13685  */
13686
13687 /**
13688  * @class Roo.bootstrap.Popover
13689  * @extends Roo.bootstrap.Component
13690  * Bootstrap Popover class
13691  * @cfg {String} html contents of the popover   (or false to use children..)
13692  * @cfg {String} title of popover (or false to hide)
13693  * @cfg {String} placement how it is placed
13694  * @cfg {String} trigger click || hover (or false to trigger manually)
13695  * @cfg {String} over what (parent or false to trigger manually.)
13696  * @cfg {Number} delay - delay before showing
13697  
13698  * @constructor
13699  * Create a new Popover
13700  * @param {Object} config The config object
13701  */
13702
13703 Roo.bootstrap.Popover = function(config){
13704     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13705 };
13706
13707 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13708     
13709     title: 'Fill in a title',
13710     html: false,
13711     
13712     placement : 'right',
13713     trigger : 'hover', // hover
13714     
13715     delay : 0,
13716     
13717     over: 'parent',
13718     
13719     can_build_overlaid : false,
13720     
13721     getChildContainer : function()
13722     {
13723         return this.el.select('.popover-content',true).first();
13724     },
13725     
13726     getAutoCreate : function(){
13727          Roo.log('make popover?');
13728         var cfg = {
13729            cls : 'popover roo-dynamic',
13730            style: 'display:block',
13731            cn : [
13732                 {
13733                     cls : 'arrow'
13734                 },
13735                 {
13736                     cls : 'popover-inner',
13737                     cn : [
13738                         {
13739                             tag: 'h3',
13740                             cls: 'popover-title',
13741                             html : this.title
13742                         },
13743                         {
13744                             cls : 'popover-content',
13745                             html : this.html
13746                         }
13747                     ]
13748                     
13749                 }
13750            ]
13751         };
13752         
13753         return cfg;
13754     },
13755     setTitle: function(str)
13756     {
13757         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13758     },
13759     setContent: function(str)
13760     {
13761         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13762     },
13763     // as it get's added to the bottom of the page.
13764     onRender : function(ct, position)
13765     {
13766         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13767         if(!this.el){
13768             var cfg = Roo.apply({},  this.getAutoCreate());
13769             cfg.id = Roo.id();
13770             
13771             if (this.cls) {
13772                 cfg.cls += ' ' + this.cls;
13773             }
13774             if (this.style) {
13775                 cfg.style = this.style;
13776             }
13777             Roo.log("adding to ")
13778             this.el = Roo.get(document.body).createChild(cfg, position);
13779             Roo.log(this.el);
13780         }
13781         this.initEvents();
13782     },
13783     
13784     initEvents : function()
13785     {
13786         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13787         this.el.enableDisplayMode('block');
13788         this.el.hide();
13789         if (this.over === false) {
13790             return; 
13791         }
13792         if (this.triggers === false) {
13793             return;
13794         }
13795         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13796         var triggers = this.trigger ? this.trigger.split(' ') : [];
13797         Roo.each(triggers, function(trigger) {
13798         
13799             if (trigger == 'click') {
13800                 on_el.on('click', this.toggle, this);
13801             } else if (trigger != 'manual') {
13802                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13803                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13804       
13805                 on_el.on(eventIn  ,this.enter, this);
13806                 on_el.on(eventOut, this.leave, this);
13807             }
13808         }, this);
13809         
13810     },
13811     
13812     
13813     // private
13814     timeout : null,
13815     hoverState : null,
13816     
13817     toggle : function () {
13818         this.hoverState == 'in' ? this.leave() : this.enter();
13819     },
13820     
13821     enter : function () {
13822        
13823     
13824         clearTimeout(this.timeout);
13825     
13826         this.hoverState = 'in'
13827     
13828         if (!this.delay || !this.delay.show) {
13829             this.show();
13830             return 
13831         }
13832         var _t = this;
13833         this.timeout = setTimeout(function () {
13834             if (_t.hoverState == 'in') {
13835                 _t.show();
13836             }
13837         }, this.delay.show)
13838     },
13839     leave : function() {
13840         clearTimeout(this.timeout);
13841     
13842         this.hoverState = 'out'
13843     
13844         if (!this.delay || !this.delay.hide) {
13845             this.hide();
13846             return 
13847         }
13848         var _t = this;
13849         this.timeout = setTimeout(function () {
13850             if (_t.hoverState == 'out') {
13851                 _t.hide();
13852             }
13853         }, this.delay.hide)
13854     },
13855     
13856     show : function (on_el)
13857     {
13858         if (!on_el) {
13859             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13860         }
13861         // set content.
13862         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13863         if (this.html !== false) {
13864             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13865         }
13866         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13867         if (!this.title.length) {
13868             this.el.select('.popover-title',true).hide();
13869         }
13870         
13871         var placement = typeof this.placement == 'function' ?
13872             this.placement.call(this, this.el, on_el) :
13873             this.placement;
13874             
13875         var autoToken = /\s?auto?\s?/i;
13876         var autoPlace = autoToken.test(placement);
13877         if (autoPlace) {
13878             placement = placement.replace(autoToken, '') || 'top';
13879         }
13880         
13881         //this.el.detach()
13882         //this.el.setXY([0,0]);
13883         this.el.show();
13884         this.el.dom.style.display='block';
13885         this.el.addClass(placement);
13886         
13887         //this.el.appendTo(on_el);
13888         
13889         var p = this.getPosition();
13890         var box = this.el.getBox();
13891         
13892         if (autoPlace) {
13893             // fixme..
13894         }
13895         var align = Roo.bootstrap.Popover.alignment[placement]
13896         this.el.alignTo(on_el, align[0],align[1]);
13897         //var arrow = this.el.select('.arrow',true).first();
13898         //arrow.set(align[2], 
13899         
13900         this.el.addClass('in');
13901         this.hoverState = null;
13902         
13903         if (this.el.hasClass('fade')) {
13904             // fade it?
13905         }
13906         
13907     },
13908     hide : function()
13909     {
13910         this.el.setXY([0,0]);
13911         this.el.removeClass('in');
13912         this.el.hide();
13913         
13914     }
13915     
13916 });
13917
13918 Roo.bootstrap.Popover.alignment = {
13919     'left' : ['r-l', [-10,0], 'right'],
13920     'right' : ['l-r', [10,0], 'left'],
13921     'bottom' : ['t-b', [0,10], 'top'],
13922     'top' : [ 'b-t', [0,-10], 'bottom']
13923 };
13924
13925  /*
13926  * - LGPL
13927  *
13928  * Progress
13929  * 
13930  */
13931
13932 /**
13933  * @class Roo.bootstrap.Progress
13934  * @extends Roo.bootstrap.Component
13935  * Bootstrap Progress class
13936  * @cfg {Boolean} striped striped of the progress bar
13937  * @cfg {Boolean} active animated of the progress bar
13938  * 
13939  * 
13940  * @constructor
13941  * Create a new Progress
13942  * @param {Object} config The config object
13943  */
13944
13945 Roo.bootstrap.Progress = function(config){
13946     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13947 };
13948
13949 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13950     
13951     striped : false,
13952     active: false,
13953     
13954     getAutoCreate : function(){
13955         var cfg = {
13956             tag: 'div',
13957             cls: 'progress'
13958         };
13959         
13960         
13961         if(this.striped){
13962             cfg.cls += ' progress-striped';
13963         }
13964       
13965         if(this.active){
13966             cfg.cls += ' active';
13967         }
13968         
13969         
13970         return cfg;
13971     }
13972    
13973 });
13974
13975  
13976
13977  /*
13978  * - LGPL
13979  *
13980  * ProgressBar
13981  * 
13982  */
13983
13984 /**
13985  * @class Roo.bootstrap.ProgressBar
13986  * @extends Roo.bootstrap.Component
13987  * Bootstrap ProgressBar class
13988  * @cfg {Number} aria_valuenow aria-value now
13989  * @cfg {Number} aria_valuemin aria-value min
13990  * @cfg {Number} aria_valuemax aria-value max
13991  * @cfg {String} label label for the progress bar
13992  * @cfg {String} panel (success | info | warning | danger )
13993  * @cfg {String} role role of the progress bar
13994  * @cfg {String} sr_only text
13995  * 
13996  * 
13997  * @constructor
13998  * Create a new ProgressBar
13999  * @param {Object} config The config object
14000  */
14001
14002 Roo.bootstrap.ProgressBar = function(config){
14003     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14004 };
14005
14006 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14007     
14008     aria_valuenow : 0,
14009     aria_valuemin : 0,
14010     aria_valuemax : 100,
14011     label : false,
14012     panel : false,
14013     role : false,
14014     sr_only: false,
14015     
14016     getAutoCreate : function()
14017     {
14018         
14019         var cfg = {
14020             tag: 'div',
14021             cls: 'progress-bar',
14022             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14023         };
14024         
14025         if(this.sr_only){
14026             cfg.cn = {
14027                 tag: 'span',
14028                 cls: 'sr-only',
14029                 html: this.sr_only
14030             }
14031         }
14032         
14033         if(this.role){
14034             cfg.role = this.role;
14035         }
14036         
14037         if(this.aria_valuenow){
14038             cfg['aria-valuenow'] = this.aria_valuenow;
14039         }
14040         
14041         if(this.aria_valuemin){
14042             cfg['aria-valuemin'] = this.aria_valuemin;
14043         }
14044         
14045         if(this.aria_valuemax){
14046             cfg['aria-valuemax'] = this.aria_valuemax;
14047         }
14048         
14049         if(this.label && !this.sr_only){
14050             cfg.html = this.label;
14051         }
14052         
14053         if(this.panel){
14054             cfg.cls += ' progress-bar-' + this.panel;
14055         }
14056         
14057         return cfg;
14058     },
14059     
14060     update : function(aria_valuenow)
14061     {
14062         this.aria_valuenow = aria_valuenow;
14063         
14064         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14065     }
14066    
14067 });
14068
14069  
14070
14071  /*
14072  * - LGPL
14073  *
14074  * column
14075  * 
14076  */
14077
14078 /**
14079  * @class Roo.bootstrap.TabGroup
14080  * @extends Roo.bootstrap.Column
14081  * Bootstrap Column class
14082  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14083  * @cfg {Boolean} carousel true to make the group behave like a carousel
14084  * 
14085  * @constructor
14086  * Create a new TabGroup
14087  * @param {Object} config The config object
14088  */
14089
14090 Roo.bootstrap.TabGroup = function(config){
14091     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14092     if (!this.navId) {
14093         this.navId = Roo.id();
14094     }
14095     this.tabs = [];
14096     Roo.bootstrap.TabGroup.register(this);
14097     
14098 };
14099
14100 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14101     
14102     carousel : false,
14103     transition : false,
14104      
14105     getAutoCreate : function()
14106     {
14107         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14108         
14109         cfg.cls += ' tab-content';
14110         
14111         if (this.carousel) {
14112             cfg.cls += ' carousel slide';
14113             cfg.cn = [{
14114                cls : 'carousel-inner'
14115             }]
14116         }
14117         
14118         
14119         return cfg;
14120     },
14121     getChildContainer : function()
14122     {
14123         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14124     },
14125     
14126     /**
14127     * register a Navigation item
14128     * @param {Roo.bootstrap.NavItem} the navitem to add
14129     */
14130     register : function(item)
14131     {
14132         this.tabs.push( item);
14133         item.navId = this.navId; // not really needed..
14134     
14135     },
14136     
14137     getActivePanel : function()
14138     {
14139         var r = false;
14140         Roo.each(this.tabs, function(t) {
14141             if (t.active) {
14142                 r = t;
14143                 return false;
14144             }
14145             return null;
14146         });
14147         return r;
14148         
14149     },
14150     getPanelByName : function(n)
14151     {
14152         var r = false;
14153         Roo.each(this.tabs, function(t) {
14154             if (t.tabId == n) {
14155                 r = t;
14156                 return false;
14157             }
14158             return null;
14159         });
14160         return r;
14161     },
14162     indexOfPanel : function(p)
14163     {
14164         var r = false;
14165         Roo.each(this.tabs, function(t,i) {
14166             if (t.tabId == p.tabId) {
14167                 r = i;
14168                 return false;
14169             }
14170             return null;
14171         });
14172         return r;
14173     },
14174     /**
14175      * show a specific panel
14176      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14177      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14178      */
14179     showPanel : function (pan)
14180     {
14181         
14182         if (typeof(pan) == 'number') {
14183             pan = this.tabs[pan];
14184         }
14185         if (typeof(pan) == 'string') {
14186             pan = this.getPanelByName(pan);
14187         }
14188         if (pan.tabId == this.getActivePanel().tabId) {
14189             return true;
14190         }
14191         var cur = this.getActivePanel();
14192         
14193         if (false === cur.fireEvent('beforedeactivate')) {
14194             return false;
14195         }
14196         
14197         if (this.carousel) {
14198             this.transition = true;
14199             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14200             var lr = dir == 'next' ? 'left' : 'right';
14201             pan.el.addClass(dir); // or prev
14202             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14203             cur.el.addClass(lr); // or right
14204             pan.el.addClass(lr);
14205             
14206             var _this = this;
14207             cur.el.on('transitionend', function() {
14208                 Roo.log("trans end?");
14209                 
14210                 pan.el.removeClass([lr,dir]);
14211                 pan.setActive(true);
14212                 
14213                 cur.el.removeClass([lr]);
14214                 cur.setActive(false);
14215                 
14216                 _this.transition = false;
14217                 
14218             }, this, { single:  true } );
14219             return true;
14220         }
14221         
14222         cur.setActive(false);
14223         pan.setActive(true);
14224         return true;
14225         
14226     },
14227     showPanelNext : function()
14228     {
14229         var i = this.indexOfPanel(this.getActivePanel());
14230         if (i > this.tabs.length) {
14231             return;
14232         }
14233         this.showPanel(this.tabs[i+1]);
14234     },
14235     showPanelPrev : function()
14236     {
14237         var i = this.indexOfPanel(this.getActivePanel());
14238         if (i  < 1) {
14239             return;
14240         }
14241         this.showPanel(this.tabs[i-1]);
14242     }
14243     
14244     
14245   
14246 });
14247
14248  
14249
14250  
14251  
14252 Roo.apply(Roo.bootstrap.TabGroup, {
14253     
14254     groups: {},
14255      /**
14256     * register a Navigation Group
14257     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14258     */
14259     register : function(navgrp)
14260     {
14261         this.groups[navgrp.navId] = navgrp;
14262         
14263     },
14264     /**
14265     * fetch a Navigation Group based on the navigation ID
14266     * if one does not exist , it will get created.
14267     * @param {string} the navgroup to add
14268     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14269     */
14270     get: function(navId) {
14271         if (typeof(this.groups[navId]) == 'undefined') {
14272             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14273         }
14274         return this.groups[navId] ;
14275     }
14276     
14277     
14278     
14279 });
14280
14281  /*
14282  * - LGPL
14283  *
14284  * TabPanel
14285  * 
14286  */
14287
14288 /**
14289  * @class Roo.bootstrap.TabPanel
14290  * @extends Roo.bootstrap.Component
14291  * Bootstrap TabPanel class
14292  * @cfg {Boolean} active panel active
14293  * @cfg {String} html panel content
14294  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14295  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14296  * 
14297  * 
14298  * @constructor
14299  * Create a new TabPanel
14300  * @param {Object} config The config object
14301  */
14302
14303 Roo.bootstrap.TabPanel = function(config){
14304     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14305     this.addEvents({
14306         /**
14307              * @event changed
14308              * Fires when the active status changes
14309              * @param {Roo.bootstrap.TabPanel} this
14310              * @param {Boolean} state the new state
14311             
14312          */
14313         'changed': true,
14314         /**
14315              * @event beforedeactivate
14316              * Fires before a tab is de-activated - can be used to do validation on a form.
14317              * @param {Roo.bootstrap.TabPanel} this
14318              * @return {Boolean} false if there is an error
14319             
14320          */
14321         'beforedeactivate': true
14322      });
14323     
14324     this.tabId = this.tabId || Roo.id();
14325   
14326 };
14327
14328 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14329     
14330     active: false,
14331     html: false,
14332     tabId: false,
14333     navId : false,
14334     
14335     getAutoCreate : function(){
14336         var cfg = {
14337             tag: 'div',
14338             // item is needed for carousel - not sure if it has any effect otherwise
14339             cls: 'tab-pane item',
14340             html: this.html || ''
14341         };
14342         
14343         if(this.active){
14344             cfg.cls += ' active';
14345         }
14346         
14347         if(this.tabId){
14348             cfg.tabId = this.tabId;
14349         }
14350         
14351         
14352         return cfg;
14353     },
14354     
14355     initEvents:  function()
14356     {
14357         Roo.log('-------- init events on tab panel ---------');
14358         
14359         var p = this.parent();
14360         this.navId = this.navId || p.navId;
14361         
14362         if (typeof(this.navId) != 'undefined') {
14363             // not really needed.. but just in case.. parent should be a NavGroup.
14364             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14365             Roo.log(['register', tg, this]);
14366             tg.register(this);
14367         }
14368     },
14369     
14370     
14371     onRender : function(ct, position)
14372     {
14373        // Roo.log("Call onRender: " + this.xtype);
14374         
14375         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14376         
14377         
14378         
14379         
14380         
14381     },
14382     
14383     setActive: function(state)
14384     {
14385         Roo.log("panel - set active " + this.tabId + "=" + state);
14386         
14387         this.active = state;
14388         if (!state) {
14389             this.el.removeClass('active');
14390             
14391         } else  if (!this.el.hasClass('active')) {
14392             this.el.addClass('active');
14393         }
14394         this.fireEvent('changed', this, state);
14395     }
14396     
14397     
14398 });
14399  
14400
14401  
14402
14403  /*
14404  * - LGPL
14405  *
14406  * DateField
14407  * 
14408  */
14409
14410 /**
14411  * @class Roo.bootstrap.DateField
14412  * @extends Roo.bootstrap.Input
14413  * Bootstrap DateField class
14414  * @cfg {Number} weekStart default 0
14415  * @cfg {String} viewMode default empty, (months|years)
14416  * @cfg {String} minViewMode default empty, (months|years)
14417  * @cfg {Number} startDate default -Infinity
14418  * @cfg {Number} endDate default Infinity
14419  * @cfg {Boolean} todayHighlight default false
14420  * @cfg {Boolean} todayBtn default false
14421  * @cfg {Boolean} calendarWeeks default false
14422  * @cfg {Object} daysOfWeekDisabled default empty
14423  * @cfg {Boolean} singleMode default false (true | false)
14424  * 
14425  * @cfg {Boolean} keyboardNavigation default true
14426  * @cfg {String} language default en
14427  * 
14428  * @constructor
14429  * Create a new DateField
14430  * @param {Object} config The config object
14431  */
14432
14433 Roo.bootstrap.DateField = function(config){
14434     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14435      this.addEvents({
14436             /**
14437              * @event show
14438              * Fires when this field show.
14439              * @param {Roo.bootstrap.DateField} this
14440              * @param {Mixed} date The date value
14441              */
14442             show : true,
14443             /**
14444              * @event show
14445              * Fires when this field hide.
14446              * @param {Roo.bootstrap.DateField} this
14447              * @param {Mixed} date The date value
14448              */
14449             hide : true,
14450             /**
14451              * @event select
14452              * Fires when select a date.
14453              * @param {Roo.bootstrap.DateField} this
14454              * @param {Mixed} date The date value
14455              */
14456             select : true
14457         });
14458 };
14459
14460 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14461     
14462     /**
14463      * @cfg {String} format
14464      * The default date format string which can be overriden for localization support.  The format must be
14465      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14466      */
14467     format : "m/d/y",
14468     /**
14469      * @cfg {String} altFormats
14470      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14471      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14472      */
14473     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14474     
14475     weekStart : 0,
14476     
14477     viewMode : '',
14478     
14479     minViewMode : '',
14480     
14481     todayHighlight : false,
14482     
14483     todayBtn: false,
14484     
14485     language: 'en',
14486     
14487     keyboardNavigation: true,
14488     
14489     calendarWeeks: false,
14490     
14491     startDate: -Infinity,
14492     
14493     endDate: Infinity,
14494     
14495     daysOfWeekDisabled: [],
14496     
14497     _events: [],
14498     
14499     singleMode : false,
14500     
14501     UTCDate: function()
14502     {
14503         return new Date(Date.UTC.apply(Date, arguments));
14504     },
14505     
14506     UTCToday: function()
14507     {
14508         var today = new Date();
14509         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14510     },
14511     
14512     getDate: function() {
14513             var d = this.getUTCDate();
14514             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14515     },
14516     
14517     getUTCDate: function() {
14518             return this.date;
14519     },
14520     
14521     setDate: function(d) {
14522             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14523     },
14524     
14525     setUTCDate: function(d) {
14526             this.date = d;
14527             this.setValue(this.formatDate(this.date));
14528     },
14529         
14530     onRender: function(ct, position)
14531     {
14532         
14533         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14534         
14535         this.language = this.language || 'en';
14536         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14537         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14538         
14539         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14540         this.format = this.format || 'm/d/y';
14541         this.isInline = false;
14542         this.isInput = true;
14543         this.component = this.el.select('.add-on', true).first() || false;
14544         this.component = (this.component && this.component.length === 0) ? false : this.component;
14545         this.hasInput = this.component && this.inputEL().length;
14546         
14547         if (typeof(this.minViewMode === 'string')) {
14548             switch (this.minViewMode) {
14549                 case 'months':
14550                     this.minViewMode = 1;
14551                     break;
14552                 case 'years':
14553                     this.minViewMode = 2;
14554                     break;
14555                 default:
14556                     this.minViewMode = 0;
14557                     break;
14558             }
14559         }
14560         
14561         if (typeof(this.viewMode === 'string')) {
14562             switch (this.viewMode) {
14563                 case 'months':
14564                     this.viewMode = 1;
14565                     break;
14566                 case 'years':
14567                     this.viewMode = 2;
14568                     break;
14569                 default:
14570                     this.viewMode = 0;
14571                     break;
14572             }
14573         }
14574                 
14575         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14576         
14577 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14578         
14579         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14580         
14581         this.picker().on('mousedown', this.onMousedown, this);
14582         this.picker().on('click', this.onClick, this);
14583         
14584         this.picker().addClass('datepicker-dropdown');
14585         
14586         this.startViewMode = this.viewMode;
14587         
14588         if(this.singleMode){
14589             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14590                 v.setVisibilityMode(Roo.Element.DISPLAY)
14591                 v.hide();
14592             })
14593             
14594             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14595                 v.setStyle('width', '189px');
14596             });
14597         }
14598         
14599         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14600             if(!this.calendarWeeks){
14601                 v.remove();
14602                 return;
14603             };
14604             
14605             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14606             v.attr('colspan', function(i, val){
14607                 return parseInt(val) + 1;
14608             });
14609         })
14610                         
14611         
14612         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14613         
14614         this.setStartDate(this.startDate);
14615         this.setEndDate(this.endDate);
14616         
14617         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14618         
14619         this.fillDow();
14620         this.fillMonths();
14621         this.update();
14622         this.showMode();
14623         
14624         if(this.isInline) {
14625             this.show();
14626         }
14627     },
14628     
14629     picker : function()
14630     {
14631         return this.pickerEl;
14632 //        return this.el.select('.datepicker', true).first();
14633     },
14634     
14635     fillDow: function()
14636     {
14637         var dowCnt = this.weekStart;
14638         
14639         var dow = {
14640             tag: 'tr',
14641             cn: [
14642                 
14643             ]
14644         };
14645         
14646         if(this.calendarWeeks){
14647             dow.cn.push({
14648                 tag: 'th',
14649                 cls: 'cw',
14650                 html: '&nbsp;'
14651             })
14652         }
14653         
14654         while (dowCnt < this.weekStart + 7) {
14655             dow.cn.push({
14656                 tag: 'th',
14657                 cls: 'dow',
14658                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14659             });
14660         }
14661         
14662         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14663     },
14664     
14665     fillMonths: function()
14666     {    
14667         var i = 0
14668         var months = this.picker().select('>.datepicker-months td', true).first();
14669         
14670         months.dom.innerHTML = '';
14671         
14672         while (i < 12) {
14673             var month = {
14674                 tag: 'span',
14675                 cls: 'month',
14676                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14677             }
14678             
14679             months.createChild(month);
14680         }
14681         
14682     },
14683     
14684     update: function()
14685     {
14686         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;
14687         
14688         if (this.date < this.startDate) {
14689             this.viewDate = new Date(this.startDate);
14690         } else if (this.date > this.endDate) {
14691             this.viewDate = new Date(this.endDate);
14692         } else {
14693             this.viewDate = new Date(this.date);
14694         }
14695         
14696         this.fill();
14697     },
14698     
14699     fill: function() 
14700     {
14701         var d = new Date(this.viewDate),
14702                 year = d.getUTCFullYear(),
14703                 month = d.getUTCMonth(),
14704                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14705                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14706                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14707                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14708                 currentDate = this.date && this.date.valueOf(),
14709                 today = this.UTCToday();
14710         
14711         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14712         
14713 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14714         
14715 //        this.picker.select('>tfoot th.today').
14716 //                                              .text(dates[this.language].today)
14717 //                                              .toggle(this.todayBtn !== false);
14718     
14719         this.updateNavArrows();
14720         this.fillMonths();
14721                                                 
14722         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14723         
14724         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14725          
14726         prevMonth.setUTCDate(day);
14727         
14728         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14729         
14730         var nextMonth = new Date(prevMonth);
14731         
14732         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14733         
14734         nextMonth = nextMonth.valueOf();
14735         
14736         var fillMonths = false;
14737         
14738         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14739         
14740         while(prevMonth.valueOf() < nextMonth) {
14741             var clsName = '';
14742             
14743             if (prevMonth.getUTCDay() === this.weekStart) {
14744                 if(fillMonths){
14745                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14746                 }
14747                     
14748                 fillMonths = {
14749                     tag: 'tr',
14750                     cn: []
14751                 };
14752                 
14753                 if(this.calendarWeeks){
14754                     // ISO 8601: First week contains first thursday.
14755                     // ISO also states week starts on Monday, but we can be more abstract here.
14756                     var
14757                     // Start of current week: based on weekstart/current date
14758                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14759                     // Thursday of this week
14760                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14761                     // First Thursday of year, year from thursday
14762                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14763                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14764                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14765                     
14766                     fillMonths.cn.push({
14767                         tag: 'td',
14768                         cls: 'cw',
14769                         html: calWeek
14770                     });
14771                 }
14772             }
14773             
14774             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14775                 clsName += ' old';
14776             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14777                 clsName += ' new';
14778             }
14779             if (this.todayHighlight &&
14780                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14781                 prevMonth.getUTCMonth() == today.getMonth() &&
14782                 prevMonth.getUTCDate() == today.getDate()) {
14783                 clsName += ' today';
14784             }
14785             
14786             if (currentDate && prevMonth.valueOf() === currentDate) {
14787                 clsName += ' active';
14788             }
14789             
14790             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14791                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14792                     clsName += ' disabled';
14793             }
14794             
14795             fillMonths.cn.push({
14796                 tag: 'td',
14797                 cls: 'day ' + clsName,
14798                 html: prevMonth.getDate()
14799             })
14800             
14801             prevMonth.setDate(prevMonth.getDate()+1);
14802         }
14803           
14804         var currentYear = this.date && this.date.getUTCFullYear();
14805         var currentMonth = this.date && this.date.getUTCMonth();
14806         
14807         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14808         
14809         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14810             v.removeClass('active');
14811             
14812             if(currentYear === year && k === currentMonth){
14813                 v.addClass('active');
14814             }
14815             
14816             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14817                 v.addClass('disabled');
14818             }
14819             
14820         });
14821         
14822         
14823         year = parseInt(year/10, 10) * 10;
14824         
14825         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14826         
14827         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14828         
14829         year -= 1;
14830         for (var i = -1; i < 11; i++) {
14831             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14832                 tag: 'span',
14833                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14834                 html: year
14835             })
14836             
14837             year += 1;
14838         }
14839     },
14840     
14841     showMode: function(dir) 
14842     {
14843         if (dir) {
14844             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14845         }
14846         
14847         Roo.each(this.picker().select('>div',true).elements, function(v){
14848             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14849             v.hide();
14850         });
14851         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14852     },
14853     
14854     place: function()
14855     {
14856         if(this.isInline) return;
14857         
14858         this.picker().removeClass(['bottom', 'top']);
14859         
14860         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14861             /*
14862              * place to the top of element!
14863              *
14864              */
14865             
14866             this.picker().addClass('top');
14867             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14868             
14869             return;
14870         }
14871         
14872         this.picker().addClass('bottom');
14873         
14874         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14875     },
14876     
14877     parseDate : function(value)
14878     {
14879         if(!value || value instanceof Date){
14880             return value;
14881         }
14882         var v = Date.parseDate(value, this.format);
14883         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14884             v = Date.parseDate(value, 'Y-m-d');
14885         }
14886         if(!v && this.altFormats){
14887             if(!this.altFormatsArray){
14888                 this.altFormatsArray = this.altFormats.split("|");
14889             }
14890             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14891                 v = Date.parseDate(value, this.altFormatsArray[i]);
14892             }
14893         }
14894         return v;
14895     },
14896     
14897     formatDate : function(date, fmt)
14898     {   
14899         return (!date || !(date instanceof Date)) ?
14900         date : date.dateFormat(fmt || this.format);
14901     },
14902     
14903     onFocus : function()
14904     {
14905         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14906         this.show();
14907     },
14908     
14909     onBlur : function()
14910     {
14911         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14912         
14913         var d = this.inputEl().getValue();
14914         
14915         this.setValue(d);
14916                 
14917         this.hide();
14918     },
14919     
14920     show : function()
14921     {
14922         this.picker().show();
14923         this.update();
14924         this.place();
14925         
14926         this.fireEvent('show', this, this.date);
14927     },
14928     
14929     hide : function()
14930     {
14931         if(this.isInline) return;
14932         this.picker().hide();
14933         this.viewMode = this.startViewMode;
14934         this.showMode();
14935         
14936         this.fireEvent('hide', this, this.date);
14937         
14938     },
14939     
14940     onMousedown: function(e)
14941     {
14942         e.stopPropagation();
14943         e.preventDefault();
14944     },
14945     
14946     keyup: function(e)
14947     {
14948         Roo.bootstrap.DateField.superclass.keyup.call(this);
14949         this.update();
14950     },
14951
14952     setValue: function(v)
14953     {
14954         
14955         // v can be a string or a date..
14956         
14957         
14958         var d = new Date(this.parseDate(v) ).clearTime();
14959         
14960         if(isNaN(d.getTime())){
14961             this.date = this.viewDate = '';
14962             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14963             return;
14964         }
14965         
14966         v = this.formatDate(d);
14967         
14968         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14969         
14970         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14971      
14972         this.update();
14973
14974         this.fireEvent('select', this, this.date);
14975         
14976     },
14977     
14978     getValue: function()
14979     {
14980         return this.formatDate(this.date);
14981     },
14982     
14983     fireKey: function(e)
14984     {
14985         if (!this.picker().isVisible()){
14986             if (e.keyCode == 27) // allow escape to hide and re-show picker
14987                 this.show();
14988             return;
14989         }
14990         
14991         var dateChanged = false,
14992         dir, day, month,
14993         newDate, newViewDate;
14994         
14995         switch(e.keyCode){
14996             case 27: // escape
14997                 this.hide();
14998                 e.preventDefault();
14999                 break;
15000             case 37: // left
15001             case 39: // right
15002                 if (!this.keyboardNavigation) break;
15003                 dir = e.keyCode == 37 ? -1 : 1;
15004                 
15005                 if (e.ctrlKey){
15006                     newDate = this.moveYear(this.date, dir);
15007                     newViewDate = this.moveYear(this.viewDate, dir);
15008                 } else if (e.shiftKey){
15009                     newDate = this.moveMonth(this.date, dir);
15010                     newViewDate = this.moveMonth(this.viewDate, dir);
15011                 } else {
15012                     newDate = new Date(this.date);
15013                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15014                     newViewDate = new Date(this.viewDate);
15015                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15016                 }
15017                 if (this.dateWithinRange(newDate)){
15018                     this.date = newDate;
15019                     this.viewDate = newViewDate;
15020                     this.setValue(this.formatDate(this.date));
15021 //                    this.update();
15022                     e.preventDefault();
15023                     dateChanged = true;
15024                 }
15025                 break;
15026             case 38: // up
15027             case 40: // down
15028                 if (!this.keyboardNavigation) break;
15029                 dir = e.keyCode == 38 ? -1 : 1;
15030                 if (e.ctrlKey){
15031                     newDate = this.moveYear(this.date, dir);
15032                     newViewDate = this.moveYear(this.viewDate, dir);
15033                 } else if (e.shiftKey){
15034                     newDate = this.moveMonth(this.date, dir);
15035                     newViewDate = this.moveMonth(this.viewDate, dir);
15036                 } else {
15037                     newDate = new Date(this.date);
15038                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15039                     newViewDate = new Date(this.viewDate);
15040                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15041                 }
15042                 if (this.dateWithinRange(newDate)){
15043                     this.date = newDate;
15044                     this.viewDate = newViewDate;
15045                     this.setValue(this.formatDate(this.date));
15046 //                    this.update();
15047                     e.preventDefault();
15048                     dateChanged = true;
15049                 }
15050                 break;
15051             case 13: // enter
15052                 this.setValue(this.formatDate(this.date));
15053                 this.hide();
15054                 e.preventDefault();
15055                 break;
15056             case 9: // tab
15057                 this.setValue(this.formatDate(this.date));
15058                 this.hide();
15059                 break;
15060             case 16: // shift
15061             case 17: // ctrl
15062             case 18: // alt
15063                 break;
15064             default :
15065                 this.hide();
15066                 
15067         }
15068     },
15069     
15070     
15071     onClick: function(e) 
15072     {
15073         e.stopPropagation();
15074         e.preventDefault();
15075         
15076         var target = e.getTarget();
15077         
15078         if(target.nodeName.toLowerCase() === 'i'){
15079             target = Roo.get(target).dom.parentNode;
15080         }
15081         
15082         var nodeName = target.nodeName;
15083         var className = target.className;
15084         var html = target.innerHTML;
15085         //Roo.log(nodeName);
15086         
15087         switch(nodeName.toLowerCase()) {
15088             case 'th':
15089                 switch(className) {
15090                     case 'switch':
15091                         this.showMode(1);
15092                         break;
15093                     case 'prev':
15094                     case 'next':
15095                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15096                         switch(this.viewMode){
15097                                 case 0:
15098                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15099                                         break;
15100                                 case 1:
15101                                 case 2:
15102                                         this.viewDate = this.moveYear(this.viewDate, dir);
15103                                         break;
15104                         }
15105                         this.fill();
15106                         break;
15107                     case 'today':
15108                         var date = new Date();
15109                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15110 //                        this.fill()
15111                         this.setValue(this.formatDate(this.date));
15112                         
15113                         this.hide();
15114                         break;
15115                 }
15116                 break;
15117             case 'span':
15118                 if (className.indexOf('disabled') < 0) {
15119                     this.viewDate.setUTCDate(1);
15120                     if (className.indexOf('month') > -1) {
15121                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15122                     } else {
15123                         var year = parseInt(html, 10) || 0;
15124                         this.viewDate.setUTCFullYear(year);
15125                         
15126                     }
15127                     
15128                     if(this.singleMode){
15129                         this.setValue(this.formatDate(this.viewDate));
15130                         this.hide();
15131                         return;
15132                     }
15133                     
15134                     this.showMode(-1);
15135                     this.fill();
15136                 }
15137                 break;
15138                 
15139             case 'td':
15140                 //Roo.log(className);
15141                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15142                     var day = parseInt(html, 10) || 1;
15143                     var year = this.viewDate.getUTCFullYear(),
15144                         month = this.viewDate.getUTCMonth();
15145
15146                     if (className.indexOf('old') > -1) {
15147                         if(month === 0 ){
15148                             month = 11;
15149                             year -= 1;
15150                         }else{
15151                             month -= 1;
15152                         }
15153                     } else if (className.indexOf('new') > -1) {
15154                         if (month == 11) {
15155                             month = 0;
15156                             year += 1;
15157                         } else {
15158                             month += 1;
15159                         }
15160                     }
15161                     //Roo.log([year,month,day]);
15162                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15163                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15164 //                    this.fill();
15165                     //Roo.log(this.formatDate(this.date));
15166                     this.setValue(this.formatDate(this.date));
15167                     this.hide();
15168                 }
15169                 break;
15170         }
15171     },
15172     
15173     setStartDate: function(startDate)
15174     {
15175         this.startDate = startDate || -Infinity;
15176         if (this.startDate !== -Infinity) {
15177             this.startDate = this.parseDate(this.startDate);
15178         }
15179         this.update();
15180         this.updateNavArrows();
15181     },
15182
15183     setEndDate: function(endDate)
15184     {
15185         this.endDate = endDate || Infinity;
15186         if (this.endDate !== Infinity) {
15187             this.endDate = this.parseDate(this.endDate);
15188         }
15189         this.update();
15190         this.updateNavArrows();
15191     },
15192     
15193     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15194     {
15195         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15196         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15197             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15198         }
15199         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15200             return parseInt(d, 10);
15201         });
15202         this.update();
15203         this.updateNavArrows();
15204     },
15205     
15206     updateNavArrows: function() 
15207     {
15208         if(this.singleMode){
15209             return;
15210         }
15211         
15212         var d = new Date(this.viewDate),
15213         year = d.getUTCFullYear(),
15214         month = d.getUTCMonth();
15215         
15216         Roo.each(this.picker().select('.prev', true).elements, function(v){
15217             v.show();
15218             switch (this.viewMode) {
15219                 case 0:
15220
15221                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15222                         v.hide();
15223                     }
15224                     break;
15225                 case 1:
15226                 case 2:
15227                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15228                         v.hide();
15229                     }
15230                     break;
15231             }
15232         });
15233         
15234         Roo.each(this.picker().select('.next', true).elements, function(v){
15235             v.show();
15236             switch (this.viewMode) {
15237                 case 0:
15238
15239                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15240                         v.hide();
15241                     }
15242                     break;
15243                 case 1:
15244                 case 2:
15245                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15246                         v.hide();
15247                     }
15248                     break;
15249             }
15250         })
15251     },
15252     
15253     moveMonth: function(date, dir)
15254     {
15255         if (!dir) return date;
15256         var new_date = new Date(date.valueOf()),
15257         day = new_date.getUTCDate(),
15258         month = new_date.getUTCMonth(),
15259         mag = Math.abs(dir),
15260         new_month, test;
15261         dir = dir > 0 ? 1 : -1;
15262         if (mag == 1){
15263             test = dir == -1
15264             // If going back one month, make sure month is not current month
15265             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15266             ? function(){
15267                 return new_date.getUTCMonth() == month;
15268             }
15269             // If going forward one month, make sure month is as expected
15270             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15271             : function(){
15272                 return new_date.getUTCMonth() != new_month;
15273             };
15274             new_month = month + dir;
15275             new_date.setUTCMonth(new_month);
15276             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15277             if (new_month < 0 || new_month > 11)
15278                 new_month = (new_month + 12) % 12;
15279         } else {
15280             // For magnitudes >1, move one month at a time...
15281             for (var i=0; i<mag; i++)
15282                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15283                 new_date = this.moveMonth(new_date, dir);
15284             // ...then reset the day, keeping it in the new month
15285             new_month = new_date.getUTCMonth();
15286             new_date.setUTCDate(day);
15287             test = function(){
15288                 return new_month != new_date.getUTCMonth();
15289             };
15290         }
15291         // Common date-resetting loop -- if date is beyond end of month, make it
15292         // end of month
15293         while (test()){
15294             new_date.setUTCDate(--day);
15295             new_date.setUTCMonth(new_month);
15296         }
15297         return new_date;
15298     },
15299
15300     moveYear: function(date, dir)
15301     {
15302         return this.moveMonth(date, dir*12);
15303     },
15304
15305     dateWithinRange: function(date)
15306     {
15307         return date >= this.startDate && date <= this.endDate;
15308     },
15309
15310     
15311     remove: function() 
15312     {
15313         this.picker().remove();
15314     }
15315    
15316 });
15317
15318 Roo.apply(Roo.bootstrap.DateField,  {
15319     
15320     head : {
15321         tag: 'thead',
15322         cn: [
15323         {
15324             tag: 'tr',
15325             cn: [
15326             {
15327                 tag: 'th',
15328                 cls: 'prev',
15329                 html: '<i class="fa fa-arrow-left"/>'
15330             },
15331             {
15332                 tag: 'th',
15333                 cls: 'switch',
15334                 colspan: '5'
15335             },
15336             {
15337                 tag: 'th',
15338                 cls: 'next',
15339                 html: '<i class="fa fa-arrow-right"/>'
15340             }
15341
15342             ]
15343         }
15344         ]
15345     },
15346     
15347     content : {
15348         tag: 'tbody',
15349         cn: [
15350         {
15351             tag: 'tr',
15352             cn: [
15353             {
15354                 tag: 'td',
15355                 colspan: '7'
15356             }
15357             ]
15358         }
15359         ]
15360     },
15361     
15362     footer : {
15363         tag: 'tfoot',
15364         cn: [
15365         {
15366             tag: 'tr',
15367             cn: [
15368             {
15369                 tag: 'th',
15370                 colspan: '7',
15371                 cls: 'today'
15372             }
15373                     
15374             ]
15375         }
15376         ]
15377     },
15378     
15379     dates:{
15380         en: {
15381             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15382             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15383             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15384             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15385             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15386             today: "Today"
15387         }
15388     },
15389     
15390     modes: [
15391     {
15392         clsName: 'days',
15393         navFnc: 'Month',
15394         navStep: 1
15395     },
15396     {
15397         clsName: 'months',
15398         navFnc: 'FullYear',
15399         navStep: 1
15400     },
15401     {
15402         clsName: 'years',
15403         navFnc: 'FullYear',
15404         navStep: 10
15405     }]
15406 });
15407
15408 Roo.apply(Roo.bootstrap.DateField,  {
15409   
15410     template : {
15411         tag: 'div',
15412         cls: 'datepicker dropdown-menu roo-dynamic',
15413         cn: [
15414         {
15415             tag: 'div',
15416             cls: 'datepicker-days',
15417             cn: [
15418             {
15419                 tag: 'table',
15420                 cls: 'table-condensed',
15421                 cn:[
15422                 Roo.bootstrap.DateField.head,
15423                 {
15424                     tag: 'tbody'
15425                 },
15426                 Roo.bootstrap.DateField.footer
15427                 ]
15428             }
15429             ]
15430         },
15431         {
15432             tag: 'div',
15433             cls: 'datepicker-months',
15434             cn: [
15435             {
15436                 tag: 'table',
15437                 cls: 'table-condensed',
15438                 cn:[
15439                 Roo.bootstrap.DateField.head,
15440                 Roo.bootstrap.DateField.content,
15441                 Roo.bootstrap.DateField.footer
15442                 ]
15443             }
15444             ]
15445         },
15446         {
15447             tag: 'div',
15448             cls: 'datepicker-years',
15449             cn: [
15450             {
15451                 tag: 'table',
15452                 cls: 'table-condensed',
15453                 cn:[
15454                 Roo.bootstrap.DateField.head,
15455                 Roo.bootstrap.DateField.content,
15456                 Roo.bootstrap.DateField.footer
15457                 ]
15458             }
15459             ]
15460         }
15461         ]
15462     }
15463 });
15464
15465  
15466
15467  /*
15468  * - LGPL
15469  *
15470  * TimeField
15471  * 
15472  */
15473
15474 /**
15475  * @class Roo.bootstrap.TimeField
15476  * @extends Roo.bootstrap.Input
15477  * Bootstrap DateField class
15478  * 
15479  * 
15480  * @constructor
15481  * Create a new TimeField
15482  * @param {Object} config The config object
15483  */
15484
15485 Roo.bootstrap.TimeField = function(config){
15486     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15487     this.addEvents({
15488             /**
15489              * @event show
15490              * Fires when this field show.
15491              * @param {Roo.bootstrap.DateField} this
15492              * @param {Mixed} date The date value
15493              */
15494             show : true,
15495             /**
15496              * @event show
15497              * Fires when this field hide.
15498              * @param {Roo.bootstrap.DateField} this
15499              * @param {Mixed} date The date value
15500              */
15501             hide : true,
15502             /**
15503              * @event select
15504              * Fires when select a date.
15505              * @param {Roo.bootstrap.DateField} this
15506              * @param {Mixed} date The date value
15507              */
15508             select : true
15509         });
15510 };
15511
15512 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15513     
15514     /**
15515      * @cfg {String} format
15516      * The default time format string which can be overriden for localization support.  The format must be
15517      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15518      */
15519     format : "H:i",
15520        
15521     onRender: function(ct, position)
15522     {
15523         
15524         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15525                 
15526         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15527         
15528         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15529         
15530         this.pop = this.picker().select('>.datepicker-time',true).first();
15531         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15532         
15533         this.picker().on('mousedown', this.onMousedown, this);
15534         this.picker().on('click', this.onClick, this);
15535         
15536         this.picker().addClass('datepicker-dropdown');
15537     
15538         this.fillTime();
15539         this.update();
15540             
15541         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15542         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15543         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15544         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15545         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15546         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15547
15548     },
15549     
15550     fireKey: function(e){
15551         if (!this.picker().isVisible()){
15552             if (e.keyCode == 27) // allow escape to hide and re-show picker
15553                 this.show();
15554             return;
15555         }
15556
15557         e.preventDefault();
15558         
15559         switch(e.keyCode){
15560             case 27: // escape
15561                 this.hide();
15562                 break;
15563             case 37: // left
15564             case 39: // right
15565                 this.onTogglePeriod();
15566                 break;
15567             case 38: // up
15568                 this.onIncrementMinutes();
15569                 break;
15570             case 40: // down
15571                 this.onDecrementMinutes();
15572                 break;
15573             case 13: // enter
15574             case 9: // tab
15575                 this.setTime();
15576                 break;
15577         }
15578     },
15579     
15580     onClick: function(e) {
15581         e.stopPropagation();
15582         e.preventDefault();
15583     },
15584     
15585     picker : function()
15586     {
15587         return this.el.select('.datepicker', true).first();
15588     },
15589     
15590     fillTime: function()
15591     {    
15592         var time = this.pop.select('tbody', true).first();
15593         
15594         time.dom.innerHTML = '';
15595         
15596         time.createChild({
15597             tag: 'tr',
15598             cn: [
15599                 {
15600                     tag: 'td',
15601                     cn: [
15602                         {
15603                             tag: 'a',
15604                             href: '#',
15605                             cls: 'btn',
15606                             cn: [
15607                                 {
15608                                     tag: 'span',
15609                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15610                                 }
15611                             ]
15612                         } 
15613                     ]
15614                 },
15615                 {
15616                     tag: 'td',
15617                     cls: 'separator'
15618                 },
15619                 {
15620                     tag: 'td',
15621                     cn: [
15622                         {
15623                             tag: 'a',
15624                             href: '#',
15625                             cls: 'btn',
15626                             cn: [
15627                                 {
15628                                     tag: 'span',
15629                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15630                                 }
15631                             ]
15632                         }
15633                     ]
15634                 },
15635                 {
15636                     tag: 'td',
15637                     cls: 'separator'
15638                 }
15639             ]
15640         });
15641         
15642         time.createChild({
15643             tag: 'tr',
15644             cn: [
15645                 {
15646                     tag: 'td',
15647                     cn: [
15648                         {
15649                             tag: 'span',
15650                             cls: 'timepicker-hour',
15651                             html: '00'
15652                         }  
15653                     ]
15654                 },
15655                 {
15656                     tag: 'td',
15657                     cls: 'separator',
15658                     html: ':'
15659                 },
15660                 {
15661                     tag: 'td',
15662                     cn: [
15663                         {
15664                             tag: 'span',
15665                             cls: 'timepicker-minute',
15666                             html: '00'
15667                         }  
15668                     ]
15669                 },
15670                 {
15671                     tag: 'td',
15672                     cls: 'separator'
15673                 },
15674                 {
15675                     tag: 'td',
15676                     cn: [
15677                         {
15678                             tag: 'button',
15679                             type: 'button',
15680                             cls: 'btn btn-primary period',
15681                             html: 'AM'
15682                             
15683                         }
15684                     ]
15685                 }
15686             ]
15687         });
15688         
15689         time.createChild({
15690             tag: 'tr',
15691             cn: [
15692                 {
15693                     tag: 'td',
15694                     cn: [
15695                         {
15696                             tag: 'a',
15697                             href: '#',
15698                             cls: 'btn',
15699                             cn: [
15700                                 {
15701                                     tag: 'span',
15702                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15703                                 }
15704                             ]
15705                         }
15706                     ]
15707                 },
15708                 {
15709                     tag: 'td',
15710                     cls: 'separator'
15711                 },
15712                 {
15713                     tag: 'td',
15714                     cn: [
15715                         {
15716                             tag: 'a',
15717                             href: '#',
15718                             cls: 'btn',
15719                             cn: [
15720                                 {
15721                                     tag: 'span',
15722                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15723                                 }
15724                             ]
15725                         }
15726                     ]
15727                 },
15728                 {
15729                     tag: 'td',
15730                     cls: 'separator'
15731                 }
15732             ]
15733         });
15734         
15735     },
15736     
15737     update: function()
15738     {
15739         
15740         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15741         
15742         this.fill();
15743     },
15744     
15745     fill: function() 
15746     {
15747         var hours = this.time.getHours();
15748         var minutes = this.time.getMinutes();
15749         var period = 'AM';
15750         
15751         if(hours > 11){
15752             period = 'PM';
15753         }
15754         
15755         if(hours == 0){
15756             hours = 12;
15757         }
15758         
15759         
15760         if(hours > 12){
15761             hours = hours - 12;
15762         }
15763         
15764         if(hours < 10){
15765             hours = '0' + hours;
15766         }
15767         
15768         if(minutes < 10){
15769             minutes = '0' + minutes;
15770         }
15771         
15772         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15773         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15774         this.pop.select('button', true).first().dom.innerHTML = period;
15775         
15776     },
15777     
15778     place: function()
15779     {   
15780         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15781         
15782         var cls = ['bottom'];
15783         
15784         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15785             cls.pop();
15786             cls.push('top');
15787         }
15788         
15789         cls.push('right');
15790         
15791         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15792             cls.pop();
15793             cls.push('left');
15794         }
15795         
15796         this.picker().addClass(cls.join('-'));
15797         
15798         var _this = this;
15799         
15800         Roo.each(cls, function(c){
15801             if(c == 'bottom'){
15802                 _this.picker().setTop(_this.inputEl().getHeight());
15803                 return;
15804             }
15805             if(c == 'top'){
15806                 _this.picker().setTop(0 - _this.picker().getHeight());
15807                 return;
15808             }
15809             
15810             if(c == 'left'){
15811                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15812                 return;
15813             }
15814             if(c == 'right'){
15815                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15816                 return;
15817             }
15818         });
15819         
15820     },
15821   
15822     onFocus : function()
15823     {
15824         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15825         this.show();
15826     },
15827     
15828     onBlur : function()
15829     {
15830         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15831         this.hide();
15832     },
15833     
15834     show : function()
15835     {
15836         this.picker().show();
15837         this.pop.show();
15838         this.update();
15839         this.place();
15840         
15841         this.fireEvent('show', this, this.date);
15842     },
15843     
15844     hide : function()
15845     {
15846         this.picker().hide();
15847         this.pop.hide();
15848         
15849         this.fireEvent('hide', this, this.date);
15850     },
15851     
15852     setTime : function()
15853     {
15854         this.hide();
15855         this.setValue(this.time.format(this.format));
15856         
15857         this.fireEvent('select', this, this.date);
15858         
15859         
15860     },
15861     
15862     onMousedown: function(e){
15863         e.stopPropagation();
15864         e.preventDefault();
15865     },
15866     
15867     onIncrementHours: function()
15868     {
15869         Roo.log('onIncrementHours');
15870         this.time = this.time.add(Date.HOUR, 1);
15871         this.update();
15872         
15873     },
15874     
15875     onDecrementHours: function()
15876     {
15877         Roo.log('onDecrementHours');
15878         this.time = this.time.add(Date.HOUR, -1);
15879         this.update();
15880     },
15881     
15882     onIncrementMinutes: function()
15883     {
15884         Roo.log('onIncrementMinutes');
15885         this.time = this.time.add(Date.MINUTE, 1);
15886         this.update();
15887     },
15888     
15889     onDecrementMinutes: function()
15890     {
15891         Roo.log('onDecrementMinutes');
15892         this.time = this.time.add(Date.MINUTE, -1);
15893         this.update();
15894     },
15895     
15896     onTogglePeriod: function()
15897     {
15898         Roo.log('onTogglePeriod');
15899         this.time = this.time.add(Date.HOUR, 12);
15900         this.update();
15901     }
15902     
15903    
15904 });
15905
15906 Roo.apply(Roo.bootstrap.TimeField,  {
15907     
15908     content : {
15909         tag: 'tbody',
15910         cn: [
15911             {
15912                 tag: 'tr',
15913                 cn: [
15914                 {
15915                     tag: 'td',
15916                     colspan: '7'
15917                 }
15918                 ]
15919             }
15920         ]
15921     },
15922     
15923     footer : {
15924         tag: 'tfoot',
15925         cn: [
15926             {
15927                 tag: 'tr',
15928                 cn: [
15929                 {
15930                     tag: 'th',
15931                     colspan: '7',
15932                     cls: '',
15933                     cn: [
15934                         {
15935                             tag: 'button',
15936                             cls: 'btn btn-info ok',
15937                             html: 'OK'
15938                         }
15939                     ]
15940                 }
15941
15942                 ]
15943             }
15944         ]
15945     }
15946 });
15947
15948 Roo.apply(Roo.bootstrap.TimeField,  {
15949   
15950     template : {
15951         tag: 'div',
15952         cls: 'datepicker dropdown-menu',
15953         cn: [
15954             {
15955                 tag: 'div',
15956                 cls: 'datepicker-time',
15957                 cn: [
15958                 {
15959                     tag: 'table',
15960                     cls: 'table-condensed',
15961                     cn:[
15962                     Roo.bootstrap.TimeField.content,
15963                     Roo.bootstrap.TimeField.footer
15964                     ]
15965                 }
15966                 ]
15967             }
15968         ]
15969     }
15970 });
15971
15972  
15973
15974  /*
15975  * - LGPL
15976  *
15977  * CheckBox
15978  * 
15979  */
15980
15981 /**
15982  * @class Roo.bootstrap.CheckBox
15983  * @extends Roo.bootstrap.Input
15984  * Bootstrap CheckBox class
15985  * 
15986  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15987  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15988  * @cfg {String} boxLabel The text that appears beside the checkbox
15989  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15990  * @cfg {Boolean} checked initnal the element
15991  * 
15992  * 
15993  * @constructor
15994  * Create a new CheckBox
15995  * @param {Object} config The config object
15996  */
15997
15998 Roo.bootstrap.CheckBox = function(config){
15999     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16000    
16001         this.addEvents({
16002             /**
16003             * @event check
16004             * Fires when the element is checked or unchecked.
16005             * @param {Roo.bootstrap.CheckBox} this This input
16006             * @param {Boolean} checked The new checked value
16007             */
16008            check : true
16009         });
16010 };
16011
16012 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16013     
16014     inputType: 'checkbox',
16015     inputValue: 1,
16016     valueOff: 0,
16017     boxLabel: false,
16018     checked: false,
16019     weight : false,
16020     
16021     getAutoCreate : function()
16022     {
16023         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16024         
16025         var id = Roo.id();
16026         
16027         var cfg = {};
16028         
16029         cfg.cls = 'form-group checkbox' //input-group
16030         
16031         
16032         
16033         
16034         var input =  {
16035             tag: 'input',
16036             id : id,
16037             type : this.inputType,
16038             value : (!this.checked) ? this.valueOff : this.inputValue,
16039             cls : 'roo-checkbox', //'form-box',
16040             placeholder : this.placeholder || ''
16041             
16042         };
16043         
16044         if (this.weight) { // Validity check?
16045             cfg.cls += " checkbox-" + this.weight;
16046         }
16047         
16048         if (this.disabled) {
16049             input.disabled=true;
16050         }
16051         
16052         if(this.checked){
16053             input.checked = this.checked;
16054         }
16055         
16056         if (this.name) {
16057             input.name = this.name;
16058         }
16059         
16060         if (this.size) {
16061             input.cls += ' input-' + this.size;
16062         }
16063         
16064         var settings=this;
16065         ['xs','sm','md','lg'].map(function(size){
16066             if (settings[size]) {
16067                 cfg.cls += ' col-' + size + '-' + settings[size];
16068             }
16069         });
16070         
16071        
16072         
16073         var inputblock = input;
16074         
16075         
16076         
16077         
16078         if (this.before || this.after) {
16079             
16080             inputblock = {
16081                 cls : 'input-group',
16082                 cn :  [] 
16083             };
16084             if (this.before) {
16085                 inputblock.cn.push({
16086                     tag :'span',
16087                     cls : 'input-group-addon',
16088                     html : this.before
16089                 });
16090             }
16091             inputblock.cn.push(input);
16092             if (this.after) {
16093                 inputblock.cn.push({
16094                     tag :'span',
16095                     cls : 'input-group-addon',
16096                     html : this.after
16097                 });
16098             }
16099             
16100         };
16101         
16102         if (align ==='left' && this.fieldLabel.length) {
16103                 Roo.log("left and has label");
16104                 cfg.cn = [
16105                     
16106                     {
16107                         tag: 'label',
16108                         'for' :  id,
16109                         cls : 'control-label col-md-' + this.labelWidth,
16110                         html : this.fieldLabel
16111                         
16112                     },
16113                     {
16114                         cls : "col-md-" + (12 - this.labelWidth), 
16115                         cn: [
16116                             inputblock
16117                         ]
16118                     }
16119                     
16120                 ];
16121         } else if ( this.fieldLabel.length) {
16122                 Roo.log(" label");
16123                 cfg.cn = [
16124                    
16125                     {
16126                         tag: this.boxLabel ? 'span' : 'label',
16127                         'for': id,
16128                         cls: 'control-label box-input-label',
16129                         //cls : 'input-group-addon',
16130                         html : this.fieldLabel
16131                         
16132                     },
16133                     
16134                     inputblock
16135                     
16136                 ];
16137
16138         } else {
16139             
16140                 Roo.log(" no label && no align");
16141                 cfg.cn = [  inputblock ] ;
16142                 
16143                 
16144         };
16145          if(this.boxLabel){
16146             cfg.cn.push( {
16147                 tag: 'label',
16148                 'for': id,
16149                 cls: 'box-label',
16150                 html: this.boxLabel
16151                 
16152             });
16153         }
16154         
16155         
16156        
16157         return cfg;
16158         
16159     },
16160     
16161     /**
16162      * return the real input element.
16163      */
16164     inputEl: function ()
16165     {
16166         return this.el.select('input.roo-checkbox',true).first();
16167     },
16168     
16169     label: function()
16170     {
16171         return this.el.select('label.control-label',true).first();
16172     },
16173     
16174     initEvents : function()
16175     {
16176 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16177         
16178         this.inputEl().on('click', this.onClick,  this);
16179         
16180     },
16181     
16182     onClick : function()
16183     {   
16184         this.setChecked(!this.checked);
16185     },
16186     
16187     setChecked : function(state,suppressEvent)
16188     {
16189         this.checked = state;
16190         
16191         this.inputEl().dom.checked = state;
16192         
16193         if(suppressEvent !== true){
16194             this.fireEvent('check', this, state);
16195         }
16196         
16197         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16198         
16199     },
16200     
16201     setValue : function(v,suppressEvent)
16202     {
16203         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16204     }
16205     
16206 });
16207
16208  
16209 /*
16210  * - LGPL
16211  *
16212  * Radio
16213  * 
16214  */
16215
16216 /**
16217  * @class Roo.bootstrap.Radio
16218  * @extends Roo.bootstrap.CheckBox
16219  * Bootstrap Radio class
16220
16221  * @constructor
16222  * Create a new Radio
16223  * @param {Object} config The config object
16224  */
16225
16226 Roo.bootstrap.Radio = function(config){
16227     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16228    
16229 };
16230
16231 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16232     
16233     inputType: 'radio',
16234     inputValue: '',
16235     valueOff: '',
16236     
16237     getAutoCreate : function()
16238     {
16239         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16240         
16241         var id = Roo.id();
16242         
16243         var cfg = {};
16244         
16245         cfg.cls = 'form-group radio' //input-group
16246         
16247         var input =  {
16248             tag: 'input',
16249             id : id,
16250             type : this.inputType,
16251             value : (!this.checked) ? this.valueOff : this.inputValue,
16252             cls : 'roo-radio',
16253             placeholder : this.placeholder || ''
16254             
16255         };
16256           if (this.weight) { // Validity check?
16257             cfg.cls += " radio-" + this.weight;
16258         }
16259         if (this.disabled) {
16260             input.disabled=true;
16261         }
16262         
16263         if(this.checked){
16264             input.checked = this.checked;
16265         }
16266         
16267         if (this.name) {
16268             input.name = this.name;
16269         }
16270         
16271         if (this.size) {
16272             input.cls += ' input-' + this.size;
16273         }
16274         
16275         var settings=this;
16276         ['xs','sm','md','lg'].map(function(size){
16277             if (settings[size]) {
16278                 cfg.cls += ' col-' + size + '-' + settings[size];
16279             }
16280         });
16281         
16282         var inputblock = input;
16283         
16284         if (this.before || this.after) {
16285             
16286             inputblock = {
16287                 cls : 'input-group',
16288                 cn :  [] 
16289             };
16290             if (this.before) {
16291                 inputblock.cn.push({
16292                     tag :'span',
16293                     cls : 'input-group-addon',
16294                     html : this.before
16295                 });
16296             }
16297             inputblock.cn.push(input);
16298             if (this.after) {
16299                 inputblock.cn.push({
16300                     tag :'span',
16301                     cls : 'input-group-addon',
16302                     html : this.after
16303                 });
16304             }
16305             
16306         };
16307         
16308         if (align ==='left' && this.fieldLabel.length) {
16309                 Roo.log("left and has label");
16310                 cfg.cn = [
16311                     
16312                     {
16313                         tag: 'label',
16314                         'for' :  id,
16315                         cls : 'control-label col-md-' + this.labelWidth,
16316                         html : this.fieldLabel
16317                         
16318                     },
16319                     {
16320                         cls : "col-md-" + (12 - this.labelWidth), 
16321                         cn: [
16322                             inputblock
16323                         ]
16324                     }
16325                     
16326                 ];
16327         } else if ( this.fieldLabel.length) {
16328                 Roo.log(" label");
16329                  cfg.cn = [
16330                    
16331                     {
16332                         tag: 'label',
16333                         'for': id,
16334                         cls: 'control-label box-input-label',
16335                         //cls : 'input-group-addon',
16336                         html : this.fieldLabel
16337                         
16338                     },
16339                     
16340                     inputblock
16341                     
16342                 ];
16343
16344         } else {
16345             
16346                    Roo.log(" no label && no align");
16347                 cfg.cn = [
16348                     
16349                         inputblock
16350                     
16351                 ];
16352                 
16353                 
16354         };
16355         
16356         if(this.boxLabel){
16357             cfg.cn.push({
16358                 tag: 'label',
16359                 'for': id,
16360                 cls: 'box-label',
16361                 html: this.boxLabel
16362             })
16363         }
16364         
16365         return cfg;
16366         
16367     },
16368     inputEl: function ()
16369     {
16370         return this.el.select('input.roo-radio',true).first();
16371     },
16372     onClick : function()
16373     {   
16374         this.setChecked(true);
16375     },
16376     
16377     setChecked : function(state,suppressEvent)
16378     {
16379         if(state){
16380             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16381                 v.dom.checked = false;
16382             });
16383         }
16384         
16385         this.checked = state;
16386         this.inputEl().dom.checked = state;
16387         
16388         if(suppressEvent !== true){
16389             this.fireEvent('check', this, state);
16390         }
16391         
16392         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16393         
16394     },
16395     
16396     getGroupValue : function()
16397     {
16398         var value = ''
16399         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16400             if(v.dom.checked == true){
16401                 value = v.dom.value;
16402             }
16403         });
16404         
16405         return value;
16406     },
16407     
16408     /**
16409      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16410      * @return {Mixed} value The field value
16411      */
16412     getValue : function(){
16413         return this.getGroupValue();
16414     }
16415     
16416 });
16417
16418  
16419 //<script type="text/javascript">
16420
16421 /*
16422  * Based  Ext JS Library 1.1.1
16423  * Copyright(c) 2006-2007, Ext JS, LLC.
16424  * LGPL
16425  *
16426  */
16427  
16428 /**
16429  * @class Roo.HtmlEditorCore
16430  * @extends Roo.Component
16431  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16432  *
16433  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16434  */
16435
16436 Roo.HtmlEditorCore = function(config){
16437     
16438     
16439     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16440     
16441     
16442     this.addEvents({
16443         /**
16444          * @event initialize
16445          * Fires when the editor is fully initialized (including the iframe)
16446          * @param {Roo.HtmlEditorCore} this
16447          */
16448         initialize: true,
16449         /**
16450          * @event activate
16451          * Fires when the editor is first receives the focus. Any insertion must wait
16452          * until after this event.
16453          * @param {Roo.HtmlEditorCore} this
16454          */
16455         activate: true,
16456          /**
16457          * @event beforesync
16458          * Fires before the textarea is updated with content from the editor iframe. Return false
16459          * to cancel the sync.
16460          * @param {Roo.HtmlEditorCore} this
16461          * @param {String} html
16462          */
16463         beforesync: true,
16464          /**
16465          * @event beforepush
16466          * Fires before the iframe editor is updated with content from the textarea. Return false
16467          * to cancel the push.
16468          * @param {Roo.HtmlEditorCore} this
16469          * @param {String} html
16470          */
16471         beforepush: true,
16472          /**
16473          * @event sync
16474          * Fires when the textarea is updated with content from the editor iframe.
16475          * @param {Roo.HtmlEditorCore} this
16476          * @param {String} html
16477          */
16478         sync: true,
16479          /**
16480          * @event push
16481          * Fires when the iframe editor is updated with content from the textarea.
16482          * @param {Roo.HtmlEditorCore} this
16483          * @param {String} html
16484          */
16485         push: true,
16486         
16487         /**
16488          * @event editorevent
16489          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16490          * @param {Roo.HtmlEditorCore} this
16491          */
16492         editorevent: true
16493     });
16494     
16495     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16496     
16497     // defaults : white / black...
16498     this.applyBlacklists();
16499     
16500     
16501     
16502 };
16503
16504
16505 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16506
16507
16508      /**
16509      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16510      */
16511     
16512     owner : false,
16513     
16514      /**
16515      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16516      *                        Roo.resizable.
16517      */
16518     resizable : false,
16519      /**
16520      * @cfg {Number} height (in pixels)
16521      */   
16522     height: 300,
16523    /**
16524      * @cfg {Number} width (in pixels)
16525      */   
16526     width: 500,
16527     
16528     /**
16529      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16530      * 
16531      */
16532     stylesheets: false,
16533     
16534     // id of frame..
16535     frameId: false,
16536     
16537     // private properties
16538     validationEvent : false,
16539     deferHeight: true,
16540     initialized : false,
16541     activated : false,
16542     sourceEditMode : false,
16543     onFocus : Roo.emptyFn,
16544     iframePad:3,
16545     hideMode:'offsets',
16546     
16547     clearUp: true,
16548     
16549     // blacklist + whitelisted elements..
16550     black: false,
16551     white: false,
16552      
16553     
16554
16555     /**
16556      * Protected method that will not generally be called directly. It
16557      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16558      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16559      */
16560     getDocMarkup : function(){
16561         // body styles..
16562         var st = '';
16563         Roo.log(this.stylesheets);
16564         
16565         // inherit styels from page...?? 
16566         if (this.stylesheets === false) {
16567             
16568             Roo.get(document.head).select('style').each(function(node) {
16569                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16570             });
16571             
16572             Roo.get(document.head).select('link').each(function(node) { 
16573                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16574             });
16575             
16576         } else if (!this.stylesheets.length) {
16577                 // simple..
16578                 st = '<style type="text/css">' +
16579                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16580                    '</style>';
16581         } else {
16582             Roo.each(this.stylesheets, function(s) {
16583                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16584             });
16585             
16586         }
16587         
16588         st +=  '<style type="text/css">' +
16589             'IMG { cursor: pointer } ' +
16590         '</style>';
16591
16592         
16593         return '<html><head>' + st  +
16594             //<style type="text/css">' +
16595             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16596             //'</style>' +
16597             ' </head><body class="roo-htmleditor-body"></body></html>';
16598     },
16599
16600     // private
16601     onRender : function(ct, position)
16602     {
16603         var _t = this;
16604         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16605         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16606         
16607         
16608         this.el.dom.style.border = '0 none';
16609         this.el.dom.setAttribute('tabIndex', -1);
16610         this.el.addClass('x-hidden hide');
16611         
16612         
16613         
16614         if(Roo.isIE){ // fix IE 1px bogus margin
16615             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16616         }
16617        
16618         
16619         this.frameId = Roo.id();
16620         
16621          
16622         
16623         var iframe = this.owner.wrap.createChild({
16624             tag: 'iframe',
16625             cls: 'form-control', // bootstrap..
16626             id: this.frameId,
16627             name: this.frameId,
16628             frameBorder : 'no',
16629             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16630         }, this.el
16631         );
16632         
16633         
16634         this.iframe = iframe.dom;
16635
16636          this.assignDocWin();
16637         
16638         this.doc.designMode = 'on';
16639        
16640         this.doc.open();
16641         this.doc.write(this.getDocMarkup());
16642         this.doc.close();
16643
16644         
16645         var task = { // must defer to wait for browser to be ready
16646             run : function(){
16647                 //console.log("run task?" + this.doc.readyState);
16648                 this.assignDocWin();
16649                 if(this.doc.body || this.doc.readyState == 'complete'){
16650                     try {
16651                         this.doc.designMode="on";
16652                     } catch (e) {
16653                         return;
16654                     }
16655                     Roo.TaskMgr.stop(task);
16656                     this.initEditor.defer(10, this);
16657                 }
16658             },
16659             interval : 10,
16660             duration: 10000,
16661             scope: this
16662         };
16663         Roo.TaskMgr.start(task);
16664
16665         
16666          
16667     },
16668
16669     // private
16670     onResize : function(w, h)
16671     {
16672          Roo.log('resize: ' +w + ',' + h );
16673         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16674         if(!this.iframe){
16675             return;
16676         }
16677         if(typeof w == 'number'){
16678             
16679             this.iframe.style.width = w + 'px';
16680         }
16681         if(typeof h == 'number'){
16682             
16683             this.iframe.style.height = h + 'px';
16684             if(this.doc){
16685                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16686             }
16687         }
16688         
16689     },
16690
16691     /**
16692      * Toggles the editor between standard and source edit mode.
16693      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16694      */
16695     toggleSourceEdit : function(sourceEditMode){
16696         
16697         this.sourceEditMode = sourceEditMode === true;
16698         
16699         if(this.sourceEditMode){
16700  
16701             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16702             
16703         }else{
16704             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16705             //this.iframe.className = '';
16706             this.deferFocus();
16707         }
16708         //this.setSize(this.owner.wrap.getSize());
16709         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16710     },
16711
16712     
16713   
16714
16715     /**
16716      * Protected method that will not generally be called directly. If you need/want
16717      * custom HTML cleanup, this is the method you should override.
16718      * @param {String} html The HTML to be cleaned
16719      * return {String} The cleaned HTML
16720      */
16721     cleanHtml : function(html){
16722         html = String(html);
16723         if(html.length > 5){
16724             if(Roo.isSafari){ // strip safari nonsense
16725                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16726             }
16727         }
16728         if(html == '&nbsp;'){
16729             html = '';
16730         }
16731         return html;
16732     },
16733
16734     /**
16735      * HTML Editor -> Textarea
16736      * Protected method that will not generally be called directly. Syncs the contents
16737      * of the editor iframe with the textarea.
16738      */
16739     syncValue : function(){
16740         if(this.initialized){
16741             var bd = (this.doc.body || this.doc.documentElement);
16742             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16743             var html = bd.innerHTML;
16744             if(Roo.isSafari){
16745                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16746                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16747                 if(m && m[1]){
16748                     html = '<div style="'+m[0]+'">' + html + '</div>';
16749                 }
16750             }
16751             html = this.cleanHtml(html);
16752             // fix up the special chars.. normaly like back quotes in word...
16753             // however we do not want to do this with chinese..
16754             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16755                 var cc = b.charCodeAt();
16756                 if (
16757                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16758                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16759                     (cc >= 0xf900 && cc < 0xfb00 )
16760                 ) {
16761                         return b;
16762                 }
16763                 return "&#"+cc+";" 
16764             });
16765             if(this.owner.fireEvent('beforesync', this, html) !== false){
16766                 this.el.dom.value = html;
16767                 this.owner.fireEvent('sync', this, html);
16768             }
16769         }
16770     },
16771
16772     /**
16773      * Protected method that will not generally be called directly. Pushes the value of the textarea
16774      * into the iframe editor.
16775      */
16776     pushValue : function(){
16777         if(this.initialized){
16778             var v = this.el.dom.value.trim();
16779             
16780 //            if(v.length < 1){
16781 //                v = '&#160;';
16782 //            }
16783             
16784             if(this.owner.fireEvent('beforepush', this, v) !== false){
16785                 var d = (this.doc.body || this.doc.documentElement);
16786                 d.innerHTML = v;
16787                 this.cleanUpPaste();
16788                 this.el.dom.value = d.innerHTML;
16789                 this.owner.fireEvent('push', this, v);
16790             }
16791         }
16792     },
16793
16794     // private
16795     deferFocus : function(){
16796         this.focus.defer(10, this);
16797     },
16798
16799     // doc'ed in Field
16800     focus : function(){
16801         if(this.win && !this.sourceEditMode){
16802             this.win.focus();
16803         }else{
16804             this.el.focus();
16805         }
16806     },
16807     
16808     assignDocWin: function()
16809     {
16810         var iframe = this.iframe;
16811         
16812          if(Roo.isIE){
16813             this.doc = iframe.contentWindow.document;
16814             this.win = iframe.contentWindow;
16815         } else {
16816 //            if (!Roo.get(this.frameId)) {
16817 //                return;
16818 //            }
16819 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16820 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16821             
16822             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16823                 return;
16824             }
16825             
16826             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16827             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16828         }
16829     },
16830     
16831     // private
16832     initEditor : function(){
16833         //console.log("INIT EDITOR");
16834         this.assignDocWin();
16835         
16836         
16837         
16838         this.doc.designMode="on";
16839         this.doc.open();
16840         this.doc.write(this.getDocMarkup());
16841         this.doc.close();
16842         
16843         var dbody = (this.doc.body || this.doc.documentElement);
16844         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16845         // this copies styles from the containing element into thsi one..
16846         // not sure why we need all of this..
16847         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16848         
16849         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16850         //ss['background-attachment'] = 'fixed'; // w3c
16851         dbody.bgProperties = 'fixed'; // ie
16852         //Roo.DomHelper.applyStyles(dbody, ss);
16853         Roo.EventManager.on(this.doc, {
16854             //'mousedown': this.onEditorEvent,
16855             'mouseup': this.onEditorEvent,
16856             'dblclick': this.onEditorEvent,
16857             'click': this.onEditorEvent,
16858             'keyup': this.onEditorEvent,
16859             buffer:100,
16860             scope: this
16861         });
16862         if(Roo.isGecko){
16863             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16864         }
16865         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16866             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16867         }
16868         this.initialized = true;
16869
16870         this.owner.fireEvent('initialize', this);
16871         this.pushValue();
16872     },
16873
16874     // private
16875     onDestroy : function(){
16876         
16877         
16878         
16879         if(this.rendered){
16880             
16881             //for (var i =0; i < this.toolbars.length;i++) {
16882             //    // fixme - ask toolbars for heights?
16883             //    this.toolbars[i].onDestroy();
16884            // }
16885             
16886             //this.wrap.dom.innerHTML = '';
16887             //this.wrap.remove();
16888         }
16889     },
16890
16891     // private
16892     onFirstFocus : function(){
16893         
16894         this.assignDocWin();
16895         
16896         
16897         this.activated = true;
16898          
16899     
16900         if(Roo.isGecko){ // prevent silly gecko errors
16901             this.win.focus();
16902             var s = this.win.getSelection();
16903             if(!s.focusNode || s.focusNode.nodeType != 3){
16904                 var r = s.getRangeAt(0);
16905                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16906                 r.collapse(true);
16907                 this.deferFocus();
16908             }
16909             try{
16910                 this.execCmd('useCSS', true);
16911                 this.execCmd('styleWithCSS', false);
16912             }catch(e){}
16913         }
16914         this.owner.fireEvent('activate', this);
16915     },
16916
16917     // private
16918     adjustFont: function(btn){
16919         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16920         //if(Roo.isSafari){ // safari
16921         //    adjust *= 2;
16922        // }
16923         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16924         if(Roo.isSafari){ // safari
16925             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16926             v =  (v < 10) ? 10 : v;
16927             v =  (v > 48) ? 48 : v;
16928             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16929             
16930         }
16931         
16932         
16933         v = Math.max(1, v+adjust);
16934         
16935         this.execCmd('FontSize', v  );
16936     },
16937
16938     onEditorEvent : function(e){
16939         this.owner.fireEvent('editorevent', this, e);
16940       //  this.updateToolbar();
16941         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16942     },
16943
16944     insertTag : function(tg)
16945     {
16946         // could be a bit smarter... -> wrap the current selected tRoo..
16947         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16948             
16949             range = this.createRange(this.getSelection());
16950             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16951             wrappingNode.appendChild(range.extractContents());
16952             range.insertNode(wrappingNode);
16953
16954             return;
16955             
16956             
16957             
16958         }
16959         this.execCmd("formatblock",   tg);
16960         
16961     },
16962     
16963     insertText : function(txt)
16964     {
16965         
16966         
16967         var range = this.createRange();
16968         range.deleteContents();
16969                //alert(Sender.getAttribute('label'));
16970                
16971         range.insertNode(this.doc.createTextNode(txt));
16972     } ,
16973     
16974      
16975
16976     /**
16977      * Executes a Midas editor command on the editor document and performs necessary focus and
16978      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16979      * @param {String} cmd The Midas command
16980      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16981      */
16982     relayCmd : function(cmd, value){
16983         this.win.focus();
16984         this.execCmd(cmd, value);
16985         this.owner.fireEvent('editorevent', this);
16986         //this.updateToolbar();
16987         this.owner.deferFocus();
16988     },
16989
16990     /**
16991      * Executes a Midas editor command directly on the editor document.
16992      * For visual commands, you should use {@link #relayCmd} instead.
16993      * <b>This should only be called after the editor is initialized.</b>
16994      * @param {String} cmd The Midas command
16995      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16996      */
16997     execCmd : function(cmd, value){
16998         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16999         this.syncValue();
17000     },
17001  
17002  
17003    
17004     /**
17005      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
17006      * to insert tRoo.
17007      * @param {String} text | dom node.. 
17008      */
17009     insertAtCursor : function(text)
17010     {
17011         
17012         
17013         
17014         if(!this.activated){
17015             return;
17016         }
17017         /*
17018         if(Roo.isIE){
17019             this.win.focus();
17020             var r = this.doc.selection.createRange();
17021             if(r){
17022                 r.collapse(true);
17023                 r.pasteHTML(text);
17024                 this.syncValue();
17025                 this.deferFocus();
17026             
17027             }
17028             return;
17029         }
17030         */
17031         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17032             this.win.focus();
17033             
17034             
17035             // from jquery ui (MIT licenced)
17036             var range, node;
17037             var win = this.win;
17038             
17039             if (win.getSelection && win.getSelection().getRangeAt) {
17040                 range = win.getSelection().getRangeAt(0);
17041                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17042                 range.insertNode(node);
17043             } else if (win.document.selection && win.document.selection.createRange) {
17044                 // no firefox support
17045                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17046                 win.document.selection.createRange().pasteHTML(txt);
17047             } else {
17048                 // no firefox support
17049                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17050                 this.execCmd('InsertHTML', txt);
17051             } 
17052             
17053             this.syncValue();
17054             
17055             this.deferFocus();
17056         }
17057     },
17058  // private
17059     mozKeyPress : function(e){
17060         if(e.ctrlKey){
17061             var c = e.getCharCode(), cmd;
17062           
17063             if(c > 0){
17064                 c = String.fromCharCode(c).toLowerCase();
17065                 switch(c){
17066                     case 'b':
17067                         cmd = 'bold';
17068                         break;
17069                     case 'i':
17070                         cmd = 'italic';
17071                         break;
17072                     
17073                     case 'u':
17074                         cmd = 'underline';
17075                         break;
17076                     
17077                     case 'v':
17078                         this.cleanUpPaste.defer(100, this);
17079                         return;
17080                         
17081                 }
17082                 if(cmd){
17083                     this.win.focus();
17084                     this.execCmd(cmd);
17085                     this.deferFocus();
17086                     e.preventDefault();
17087                 }
17088                 
17089             }
17090         }
17091     },
17092
17093     // private
17094     fixKeys : function(){ // load time branching for fastest keydown performance
17095         if(Roo.isIE){
17096             return function(e){
17097                 var k = e.getKey(), r;
17098                 if(k == e.TAB){
17099                     e.stopEvent();
17100                     r = this.doc.selection.createRange();
17101                     if(r){
17102                         r.collapse(true);
17103                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17104                         this.deferFocus();
17105                     }
17106                     return;
17107                 }
17108                 
17109                 if(k == e.ENTER){
17110                     r = this.doc.selection.createRange();
17111                     if(r){
17112                         var target = r.parentElement();
17113                         if(!target || target.tagName.toLowerCase() != 'li'){
17114                             e.stopEvent();
17115                             r.pasteHTML('<br />');
17116                             r.collapse(false);
17117                             r.select();
17118                         }
17119                     }
17120                 }
17121                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17122                     this.cleanUpPaste.defer(100, this);
17123                     return;
17124                 }
17125                 
17126                 
17127             };
17128         }else if(Roo.isOpera){
17129             return function(e){
17130                 var k = e.getKey();
17131                 if(k == e.TAB){
17132                     e.stopEvent();
17133                     this.win.focus();
17134                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17135                     this.deferFocus();
17136                 }
17137                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17138                     this.cleanUpPaste.defer(100, this);
17139                     return;
17140                 }
17141                 
17142             };
17143         }else if(Roo.isSafari){
17144             return function(e){
17145                 var k = e.getKey();
17146                 
17147                 if(k == e.TAB){
17148                     e.stopEvent();
17149                     this.execCmd('InsertText','\t');
17150                     this.deferFocus();
17151                     return;
17152                 }
17153                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17154                     this.cleanUpPaste.defer(100, this);
17155                     return;
17156                 }
17157                 
17158              };
17159         }
17160     }(),
17161     
17162     getAllAncestors: function()
17163     {
17164         var p = this.getSelectedNode();
17165         var a = [];
17166         if (!p) {
17167             a.push(p); // push blank onto stack..
17168             p = this.getParentElement();
17169         }
17170         
17171         
17172         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17173             a.push(p);
17174             p = p.parentNode;
17175         }
17176         a.push(this.doc.body);
17177         return a;
17178     },
17179     lastSel : false,
17180     lastSelNode : false,
17181     
17182     
17183     getSelection : function() 
17184     {
17185         this.assignDocWin();
17186         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17187     },
17188     
17189     getSelectedNode: function() 
17190     {
17191         // this may only work on Gecko!!!
17192         
17193         // should we cache this!!!!
17194         
17195         
17196         
17197          
17198         var range = this.createRange(this.getSelection()).cloneRange();
17199         
17200         if (Roo.isIE) {
17201             var parent = range.parentElement();
17202             while (true) {
17203                 var testRange = range.duplicate();
17204                 testRange.moveToElementText(parent);
17205                 if (testRange.inRange(range)) {
17206                     break;
17207                 }
17208                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17209                     break;
17210                 }
17211                 parent = parent.parentElement;
17212             }
17213             return parent;
17214         }
17215         
17216         // is ancestor a text element.
17217         var ac =  range.commonAncestorContainer;
17218         if (ac.nodeType == 3) {
17219             ac = ac.parentNode;
17220         }
17221         
17222         var ar = ac.childNodes;
17223          
17224         var nodes = [];
17225         var other_nodes = [];
17226         var has_other_nodes = false;
17227         for (var i=0;i<ar.length;i++) {
17228             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17229                 continue;
17230             }
17231             // fullly contained node.
17232             
17233             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17234                 nodes.push(ar[i]);
17235                 continue;
17236             }
17237             
17238             // probably selected..
17239             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17240                 other_nodes.push(ar[i]);
17241                 continue;
17242             }
17243             // outer..
17244             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17245                 continue;
17246             }
17247             
17248             
17249             has_other_nodes = true;
17250         }
17251         if (!nodes.length && other_nodes.length) {
17252             nodes= other_nodes;
17253         }
17254         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17255             return false;
17256         }
17257         
17258         return nodes[0];
17259     },
17260     createRange: function(sel)
17261     {
17262         // this has strange effects when using with 
17263         // top toolbar - not sure if it's a great idea.
17264         //this.editor.contentWindow.focus();
17265         if (typeof sel != "undefined") {
17266             try {
17267                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17268             } catch(e) {
17269                 return this.doc.createRange();
17270             }
17271         } else {
17272             return this.doc.createRange();
17273         }
17274     },
17275     getParentElement: function()
17276     {
17277         
17278         this.assignDocWin();
17279         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17280         
17281         var range = this.createRange(sel);
17282          
17283         try {
17284             var p = range.commonAncestorContainer;
17285             while (p.nodeType == 3) { // text node
17286                 p = p.parentNode;
17287             }
17288             return p;
17289         } catch (e) {
17290             return null;
17291         }
17292     
17293     },
17294     /***
17295      *
17296      * Range intersection.. the hard stuff...
17297      *  '-1' = before
17298      *  '0' = hits..
17299      *  '1' = after.
17300      *         [ -- selected range --- ]
17301      *   [fail]                        [fail]
17302      *
17303      *    basically..
17304      *      if end is before start or  hits it. fail.
17305      *      if start is after end or hits it fail.
17306      *
17307      *   if either hits (but other is outside. - then it's not 
17308      *   
17309      *    
17310      **/
17311     
17312     
17313     // @see http://www.thismuchiknow.co.uk/?p=64.
17314     rangeIntersectsNode : function(range, node)
17315     {
17316         var nodeRange = node.ownerDocument.createRange();
17317         try {
17318             nodeRange.selectNode(node);
17319         } catch (e) {
17320             nodeRange.selectNodeContents(node);
17321         }
17322     
17323         var rangeStartRange = range.cloneRange();
17324         rangeStartRange.collapse(true);
17325     
17326         var rangeEndRange = range.cloneRange();
17327         rangeEndRange.collapse(false);
17328     
17329         var nodeStartRange = nodeRange.cloneRange();
17330         nodeStartRange.collapse(true);
17331     
17332         var nodeEndRange = nodeRange.cloneRange();
17333         nodeEndRange.collapse(false);
17334     
17335         return rangeStartRange.compareBoundaryPoints(
17336                  Range.START_TO_START, nodeEndRange) == -1 &&
17337                rangeEndRange.compareBoundaryPoints(
17338                  Range.START_TO_START, nodeStartRange) == 1;
17339         
17340          
17341     },
17342     rangeCompareNode : function(range, node)
17343     {
17344         var nodeRange = node.ownerDocument.createRange();
17345         try {
17346             nodeRange.selectNode(node);
17347         } catch (e) {
17348             nodeRange.selectNodeContents(node);
17349         }
17350         
17351         
17352         range.collapse(true);
17353     
17354         nodeRange.collapse(true);
17355      
17356         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17357         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17358          
17359         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17360         
17361         var nodeIsBefore   =  ss == 1;
17362         var nodeIsAfter    = ee == -1;
17363         
17364         if (nodeIsBefore && nodeIsAfter)
17365             return 0; // outer
17366         if (!nodeIsBefore && nodeIsAfter)
17367             return 1; //right trailed.
17368         
17369         if (nodeIsBefore && !nodeIsAfter)
17370             return 2;  // left trailed.
17371         // fully contined.
17372         return 3;
17373     },
17374
17375     // private? - in a new class?
17376     cleanUpPaste :  function()
17377     {
17378         // cleans up the whole document..
17379         Roo.log('cleanuppaste');
17380         
17381         this.cleanUpChildren(this.doc.body);
17382         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17383         if (clean != this.doc.body.innerHTML) {
17384             this.doc.body.innerHTML = clean;
17385         }
17386         
17387     },
17388     
17389     cleanWordChars : function(input) {// change the chars to hex code
17390         var he = Roo.HtmlEditorCore;
17391         
17392         var output = input;
17393         Roo.each(he.swapCodes, function(sw) { 
17394             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17395             
17396             output = output.replace(swapper, sw[1]);
17397         });
17398         
17399         return output;
17400     },
17401     
17402     
17403     cleanUpChildren : function (n)
17404     {
17405         if (!n.childNodes.length) {
17406             return;
17407         }
17408         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17409            this.cleanUpChild(n.childNodes[i]);
17410         }
17411     },
17412     
17413     
17414         
17415     
17416     cleanUpChild : function (node)
17417     {
17418         var ed = this;
17419         //console.log(node);
17420         if (node.nodeName == "#text") {
17421             // clean up silly Windows -- stuff?
17422             return; 
17423         }
17424         if (node.nodeName == "#comment") {
17425             node.parentNode.removeChild(node);
17426             // clean up silly Windows -- stuff?
17427             return; 
17428         }
17429         var lcname = node.tagName.toLowerCase();
17430         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17431         // whitelist of tags..
17432         
17433         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17434             // remove node.
17435             node.parentNode.removeChild(node);
17436             return;
17437             
17438         }
17439         
17440         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17441         
17442         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17443         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17444         
17445         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17446         //    remove_keep_children = true;
17447         //}
17448         
17449         if (remove_keep_children) {
17450             this.cleanUpChildren(node);
17451             // inserts everything just before this node...
17452             while (node.childNodes.length) {
17453                 var cn = node.childNodes[0];
17454                 node.removeChild(cn);
17455                 node.parentNode.insertBefore(cn, node);
17456             }
17457             node.parentNode.removeChild(node);
17458             return;
17459         }
17460         
17461         if (!node.attributes || !node.attributes.length) {
17462             this.cleanUpChildren(node);
17463             return;
17464         }
17465         
17466         function cleanAttr(n,v)
17467         {
17468             
17469             if (v.match(/^\./) || v.match(/^\//)) {
17470                 return;
17471             }
17472             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17473                 return;
17474             }
17475             if (v.match(/^#/)) {
17476                 return;
17477             }
17478 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17479             node.removeAttribute(n);
17480             
17481         }
17482         
17483         var cwhite = this.cwhite;
17484         var cblack = this.cblack;
17485             
17486         function cleanStyle(n,v)
17487         {
17488             if (v.match(/expression/)) { //XSS?? should we even bother..
17489                 node.removeAttribute(n);
17490                 return;
17491             }
17492             
17493             var parts = v.split(/;/);
17494             var clean = [];
17495             
17496             Roo.each(parts, function(p) {
17497                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17498                 if (!p.length) {
17499                     return true;
17500                 }
17501                 var l = p.split(':').shift().replace(/\s+/g,'');
17502                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17503                 
17504                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17505 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17506                     //node.removeAttribute(n);
17507                     return true;
17508                 }
17509                 //Roo.log()
17510                 // only allow 'c whitelisted system attributes'
17511                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17512 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17513                     //node.removeAttribute(n);
17514                     return true;
17515                 }
17516                 
17517                 
17518                  
17519                 
17520                 clean.push(p);
17521                 return true;
17522             });
17523             if (clean.length) { 
17524                 node.setAttribute(n, clean.join(';'));
17525             } else {
17526                 node.removeAttribute(n);
17527             }
17528             
17529         }
17530         
17531         
17532         for (var i = node.attributes.length-1; i > -1 ; i--) {
17533             var a = node.attributes[i];
17534             //console.log(a);
17535             
17536             if (a.name.toLowerCase().substr(0,2)=='on')  {
17537                 node.removeAttribute(a.name);
17538                 continue;
17539             }
17540             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17541                 node.removeAttribute(a.name);
17542                 continue;
17543             }
17544             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17545                 cleanAttr(a.name,a.value); // fixme..
17546                 continue;
17547             }
17548             if (a.name == 'style') {
17549                 cleanStyle(a.name,a.value);
17550                 continue;
17551             }
17552             /// clean up MS crap..
17553             // tecnically this should be a list of valid class'es..
17554             
17555             
17556             if (a.name == 'class') {
17557                 if (a.value.match(/^Mso/)) {
17558                     node.className = '';
17559                 }
17560                 
17561                 if (a.value.match(/body/)) {
17562                     node.className = '';
17563                 }
17564                 continue;
17565             }
17566             
17567             // style cleanup!?
17568             // class cleanup?
17569             
17570         }
17571         
17572         
17573         this.cleanUpChildren(node);
17574         
17575         
17576     },
17577     /**
17578      * Clean up MS wordisms...
17579      */
17580     cleanWord : function(node)
17581     {
17582         var _t = this;
17583         var cleanWordChildren = function()
17584         {
17585             if (!node.childNodes.length) {
17586                 return;
17587             }
17588             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17589                _t.cleanWord(node.childNodes[i]);
17590             }
17591         }
17592         
17593         
17594         if (!node) {
17595             this.cleanWord(this.doc.body);
17596             return;
17597         }
17598         if (node.nodeName == "#text") {
17599             // clean up silly Windows -- stuff?
17600             return; 
17601         }
17602         if (node.nodeName == "#comment") {
17603             node.parentNode.removeChild(node);
17604             // clean up silly Windows -- stuff?
17605             return; 
17606         }
17607         
17608         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17609             node.parentNode.removeChild(node);
17610             return;
17611         }
17612         
17613         // remove - but keep children..
17614         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17615             while (node.childNodes.length) {
17616                 var cn = node.childNodes[0];
17617                 node.removeChild(cn);
17618                 node.parentNode.insertBefore(cn, node);
17619             }
17620             node.parentNode.removeChild(node);
17621             cleanWordChildren();
17622             return;
17623         }
17624         // clean styles
17625         if (node.className.length) {
17626             
17627             var cn = node.className.split(/\W+/);
17628             var cna = [];
17629             Roo.each(cn, function(cls) {
17630                 if (cls.match(/Mso[a-zA-Z]+/)) {
17631                     return;
17632                 }
17633                 cna.push(cls);
17634             });
17635             node.className = cna.length ? cna.join(' ') : '';
17636             if (!cna.length) {
17637                 node.removeAttribute("class");
17638             }
17639         }
17640         
17641         if (node.hasAttribute("lang")) {
17642             node.removeAttribute("lang");
17643         }
17644         
17645         if (node.hasAttribute("style")) {
17646             
17647             var styles = node.getAttribute("style").split(";");
17648             var nstyle = [];
17649             Roo.each(styles, function(s) {
17650                 if (!s.match(/:/)) {
17651                     return;
17652                 }
17653                 var kv = s.split(":");
17654                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17655                     return;
17656                 }
17657                 // what ever is left... we allow.
17658                 nstyle.push(s);
17659             });
17660             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17661             if (!nstyle.length) {
17662                 node.removeAttribute('style');
17663             }
17664         }
17665         
17666         cleanWordChildren();
17667         
17668         
17669     },
17670     domToHTML : function(currentElement, depth, nopadtext) {
17671         
17672         depth = depth || 0;
17673         nopadtext = nopadtext || false;
17674     
17675         if (!currentElement) {
17676             return this.domToHTML(this.doc.body);
17677         }
17678         
17679         //Roo.log(currentElement);
17680         var j;
17681         var allText = false;
17682         var nodeName = currentElement.nodeName;
17683         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17684         
17685         if  (nodeName == '#text') {
17686             return currentElement.nodeValue;
17687         }
17688         
17689         
17690         var ret = '';
17691         if (nodeName != 'BODY') {
17692              
17693             var i = 0;
17694             // Prints the node tagName, such as <A>, <IMG>, etc
17695             if (tagName) {
17696                 var attr = [];
17697                 for(i = 0; i < currentElement.attributes.length;i++) {
17698                     // quoting?
17699                     var aname = currentElement.attributes.item(i).name;
17700                     if (!currentElement.attributes.item(i).value.length) {
17701                         continue;
17702                     }
17703                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17704                 }
17705                 
17706                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17707             } 
17708             else {
17709                 
17710                 // eack
17711             }
17712         } else {
17713             tagName = false;
17714         }
17715         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17716             return ret;
17717         }
17718         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17719             nopadtext = true;
17720         }
17721         
17722         
17723         // Traverse the tree
17724         i = 0;
17725         var currentElementChild = currentElement.childNodes.item(i);
17726         var allText = true;
17727         var innerHTML  = '';
17728         lastnode = '';
17729         while (currentElementChild) {
17730             // Formatting code (indent the tree so it looks nice on the screen)
17731             var nopad = nopadtext;
17732             if (lastnode == 'SPAN') {
17733                 nopad  = true;
17734             }
17735             // text
17736             if  (currentElementChild.nodeName == '#text') {
17737                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17738                 if (!nopad && toadd.length > 80) {
17739                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17740                 }
17741                 innerHTML  += toadd;
17742                 
17743                 i++;
17744                 currentElementChild = currentElement.childNodes.item(i);
17745                 lastNode = '';
17746                 continue;
17747             }
17748             allText = false;
17749             
17750             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17751                 
17752             // Recursively traverse the tree structure of the child node
17753             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17754             lastnode = currentElementChild.nodeName;
17755             i++;
17756             currentElementChild=currentElement.childNodes.item(i);
17757         }
17758         
17759         ret += innerHTML;
17760         
17761         if (!allText) {
17762                 // The remaining code is mostly for formatting the tree
17763             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17764         }
17765         
17766         
17767         if (tagName) {
17768             ret+= "</"+tagName+">";
17769         }
17770         return ret;
17771         
17772     },
17773         
17774     applyBlacklists : function()
17775     {
17776         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17777         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17778         
17779         this.white = [];
17780         this.black = [];
17781         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17782             if (b.indexOf(tag) > -1) {
17783                 return;
17784             }
17785             this.white.push(tag);
17786             
17787         }, this);
17788         
17789         Roo.each(w, function(tag) {
17790             if (b.indexOf(tag) > -1) {
17791                 return;
17792             }
17793             if (this.white.indexOf(tag) > -1) {
17794                 return;
17795             }
17796             this.white.push(tag);
17797             
17798         }, this);
17799         
17800         
17801         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17802             if (w.indexOf(tag) > -1) {
17803                 return;
17804             }
17805             this.black.push(tag);
17806             
17807         }, this);
17808         
17809         Roo.each(b, function(tag) {
17810             if (w.indexOf(tag) > -1) {
17811                 return;
17812             }
17813             if (this.black.indexOf(tag) > -1) {
17814                 return;
17815             }
17816             this.black.push(tag);
17817             
17818         }, this);
17819         
17820         
17821         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17822         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17823         
17824         this.cwhite = [];
17825         this.cblack = [];
17826         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17827             if (b.indexOf(tag) > -1) {
17828                 return;
17829             }
17830             this.cwhite.push(tag);
17831             
17832         }, this);
17833         
17834         Roo.each(w, function(tag) {
17835             if (b.indexOf(tag) > -1) {
17836                 return;
17837             }
17838             if (this.cwhite.indexOf(tag) > -1) {
17839                 return;
17840             }
17841             this.cwhite.push(tag);
17842             
17843         }, this);
17844         
17845         
17846         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17847             if (w.indexOf(tag) > -1) {
17848                 return;
17849             }
17850             this.cblack.push(tag);
17851             
17852         }, this);
17853         
17854         Roo.each(b, function(tag) {
17855             if (w.indexOf(tag) > -1) {
17856                 return;
17857             }
17858             if (this.cblack.indexOf(tag) > -1) {
17859                 return;
17860             }
17861             this.cblack.push(tag);
17862             
17863         }, this);
17864     }
17865     
17866     // hide stuff that is not compatible
17867     /**
17868      * @event blur
17869      * @hide
17870      */
17871     /**
17872      * @event change
17873      * @hide
17874      */
17875     /**
17876      * @event focus
17877      * @hide
17878      */
17879     /**
17880      * @event specialkey
17881      * @hide
17882      */
17883     /**
17884      * @cfg {String} fieldClass @hide
17885      */
17886     /**
17887      * @cfg {String} focusClass @hide
17888      */
17889     /**
17890      * @cfg {String} autoCreate @hide
17891      */
17892     /**
17893      * @cfg {String} inputType @hide
17894      */
17895     /**
17896      * @cfg {String} invalidClass @hide
17897      */
17898     /**
17899      * @cfg {String} invalidText @hide
17900      */
17901     /**
17902      * @cfg {String} msgFx @hide
17903      */
17904     /**
17905      * @cfg {String} validateOnBlur @hide
17906      */
17907 });
17908
17909 Roo.HtmlEditorCore.white = [
17910         'area', 'br', 'img', 'input', 'hr', 'wbr',
17911         
17912        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17913        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17914        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17915        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17916        'table',   'ul',         'xmp', 
17917        
17918        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17919       'thead',   'tr', 
17920      
17921       'dir', 'menu', 'ol', 'ul', 'dl',
17922        
17923       'embed',  'object'
17924 ];
17925
17926
17927 Roo.HtmlEditorCore.black = [
17928     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17929         'applet', // 
17930         'base',   'basefont', 'bgsound', 'blink',  'body', 
17931         'frame',  'frameset', 'head',    'html',   'ilayer', 
17932         'iframe', 'layer',  'link',     'meta',    'object',   
17933         'script', 'style' ,'title',  'xml' // clean later..
17934 ];
17935 Roo.HtmlEditorCore.clean = [
17936     'script', 'style', 'title', 'xml'
17937 ];
17938 Roo.HtmlEditorCore.remove = [
17939     'font'
17940 ];
17941 // attributes..
17942
17943 Roo.HtmlEditorCore.ablack = [
17944     'on'
17945 ];
17946     
17947 Roo.HtmlEditorCore.aclean = [ 
17948     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17949 ];
17950
17951 // protocols..
17952 Roo.HtmlEditorCore.pwhite= [
17953         'http',  'https',  'mailto'
17954 ];
17955
17956 // white listed style attributes.
17957 Roo.HtmlEditorCore.cwhite= [
17958       //  'text-align', /// default is to allow most things..
17959       
17960          
17961 //        'font-size'//??
17962 ];
17963
17964 // black listed style attributes.
17965 Roo.HtmlEditorCore.cblack= [
17966       //  'font-size' -- this can be set by the project 
17967 ];
17968
17969
17970 Roo.HtmlEditorCore.swapCodes   =[ 
17971     [    8211, "--" ], 
17972     [    8212, "--" ], 
17973     [    8216,  "'" ],  
17974     [    8217, "'" ],  
17975     [    8220, '"' ],  
17976     [    8221, '"' ],  
17977     [    8226, "*" ],  
17978     [    8230, "..." ]
17979 ]; 
17980
17981     /*
17982  * - LGPL
17983  *
17984  * HtmlEditor
17985  * 
17986  */
17987
17988 /**
17989  * @class Roo.bootstrap.HtmlEditor
17990  * @extends Roo.bootstrap.TextArea
17991  * Bootstrap HtmlEditor class
17992
17993  * @constructor
17994  * Create a new HtmlEditor
17995  * @param {Object} config The config object
17996  */
17997
17998 Roo.bootstrap.HtmlEditor = function(config){
17999     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
18000     if (!this.toolbars) {
18001         this.toolbars = [];
18002     }
18003     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
18004     this.addEvents({
18005             /**
18006              * @event initialize
18007              * Fires when the editor is fully initialized (including the iframe)
18008              * @param {HtmlEditor} this
18009              */
18010             initialize: true,
18011             /**
18012              * @event activate
18013              * Fires when the editor is first receives the focus. Any insertion must wait
18014              * until after this event.
18015              * @param {HtmlEditor} this
18016              */
18017             activate: true,
18018              /**
18019              * @event beforesync
18020              * Fires before the textarea is updated with content from the editor iframe. Return false
18021              * to cancel the sync.
18022              * @param {HtmlEditor} this
18023              * @param {String} html
18024              */
18025             beforesync: true,
18026              /**
18027              * @event beforepush
18028              * Fires before the iframe editor is updated with content from the textarea. Return false
18029              * to cancel the push.
18030              * @param {HtmlEditor} this
18031              * @param {String} html
18032              */
18033             beforepush: true,
18034              /**
18035              * @event sync
18036              * Fires when the textarea is updated with content from the editor iframe.
18037              * @param {HtmlEditor} this
18038              * @param {String} html
18039              */
18040             sync: true,
18041              /**
18042              * @event push
18043              * Fires when the iframe editor is updated with content from the textarea.
18044              * @param {HtmlEditor} this
18045              * @param {String} html
18046              */
18047             push: true,
18048              /**
18049              * @event editmodechange
18050              * Fires when the editor switches edit modes
18051              * @param {HtmlEditor} this
18052              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18053              */
18054             editmodechange: true,
18055             /**
18056              * @event editorevent
18057              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18058              * @param {HtmlEditor} this
18059              */
18060             editorevent: true,
18061             /**
18062              * @event firstfocus
18063              * Fires when on first focus - needed by toolbars..
18064              * @param {HtmlEditor} this
18065              */
18066             firstfocus: true,
18067             /**
18068              * @event autosave
18069              * Auto save the htmlEditor value as a file into Events
18070              * @param {HtmlEditor} this
18071              */
18072             autosave: true,
18073             /**
18074              * @event savedpreview
18075              * preview the saved version of htmlEditor
18076              * @param {HtmlEditor} this
18077              */
18078             savedpreview: true
18079         });
18080 };
18081
18082
18083 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18084     
18085     
18086       /**
18087      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18088      */
18089     toolbars : false,
18090    
18091      /**
18092      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18093      *                        Roo.resizable.
18094      */
18095     resizable : false,
18096      /**
18097      * @cfg {Number} height (in pixels)
18098      */   
18099     height: 300,
18100    /**
18101      * @cfg {Number} width (in pixels)
18102      */   
18103     width: false,
18104     
18105     /**
18106      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18107      * 
18108      */
18109     stylesheets: false,
18110     
18111     // id of frame..
18112     frameId: false,
18113     
18114     // private properties
18115     validationEvent : false,
18116     deferHeight: true,
18117     initialized : false,
18118     activated : false,
18119     
18120     onFocus : Roo.emptyFn,
18121     iframePad:3,
18122     hideMode:'offsets',
18123     
18124     
18125     tbContainer : false,
18126     
18127     toolbarContainer :function() {
18128         return this.wrap.select('.x-html-editor-tb',true).first();
18129     },
18130
18131     /**
18132      * Protected method that will not generally be called directly. It
18133      * is called when the editor creates its toolbar. Override this method if you need to
18134      * add custom toolbar buttons.
18135      * @param {HtmlEditor} editor
18136      */
18137     createToolbar : function(){
18138         
18139         Roo.log("create toolbars");
18140         
18141         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18142         this.toolbars[0].render(this.toolbarContainer());
18143         
18144         return;
18145         
18146 //        if (!editor.toolbars || !editor.toolbars.length) {
18147 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18148 //        }
18149 //        
18150 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18151 //            editor.toolbars[i] = Roo.factory(
18152 //                    typeof(editor.toolbars[i]) == 'string' ?
18153 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18154 //                Roo.bootstrap.HtmlEditor);
18155 //            editor.toolbars[i].init(editor);
18156 //        }
18157     },
18158
18159      
18160     // private
18161     onRender : function(ct, position)
18162     {
18163        // Roo.log("Call onRender: " + this.xtype);
18164         var _t = this;
18165         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18166       
18167         this.wrap = this.inputEl().wrap({
18168             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18169         });
18170         
18171         this.editorcore.onRender(ct, position);
18172          
18173         if (this.resizable) {
18174             this.resizeEl = new Roo.Resizable(this.wrap, {
18175                 pinned : true,
18176                 wrap: true,
18177                 dynamic : true,
18178                 minHeight : this.height,
18179                 height: this.height,
18180                 handles : this.resizable,
18181                 width: this.width,
18182                 listeners : {
18183                     resize : function(r, w, h) {
18184                         _t.onResize(w,h); // -something
18185                     }
18186                 }
18187             });
18188             
18189         }
18190         this.createToolbar(this);
18191        
18192         
18193         if(!this.width && this.resizable){
18194             this.setSize(this.wrap.getSize());
18195         }
18196         if (this.resizeEl) {
18197             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18198             // should trigger onReize..
18199         }
18200         
18201     },
18202
18203     // private
18204     onResize : function(w, h)
18205     {
18206         Roo.log('resize: ' +w + ',' + h );
18207         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18208         var ew = false;
18209         var eh = false;
18210         
18211         if(this.inputEl() ){
18212             if(typeof w == 'number'){
18213                 var aw = w - this.wrap.getFrameWidth('lr');
18214                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18215                 ew = aw;
18216             }
18217             if(typeof h == 'number'){
18218                  var tbh = -11;  // fixme it needs to tool bar size!
18219                 for (var i =0; i < this.toolbars.length;i++) {
18220                     // fixme - ask toolbars for heights?
18221                     tbh += this.toolbars[i].el.getHeight();
18222                     //if (this.toolbars[i].footer) {
18223                     //    tbh += this.toolbars[i].footer.el.getHeight();
18224                     //}
18225                 }
18226               
18227                 
18228                 
18229                 
18230                 
18231                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18232                 ah -= 5; // knock a few pixes off for look..
18233                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18234                 var eh = ah;
18235             }
18236         }
18237         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18238         this.editorcore.onResize(ew,eh);
18239         
18240     },
18241
18242     /**
18243      * Toggles the editor between standard and source edit mode.
18244      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18245      */
18246     toggleSourceEdit : function(sourceEditMode)
18247     {
18248         this.editorcore.toggleSourceEdit(sourceEditMode);
18249         
18250         if(this.editorcore.sourceEditMode){
18251             Roo.log('editor - showing textarea');
18252             
18253 //            Roo.log('in');
18254 //            Roo.log(this.syncValue());
18255             this.syncValue();
18256             this.inputEl().removeClass(['hide', 'x-hidden']);
18257             this.inputEl().dom.removeAttribute('tabIndex');
18258             this.inputEl().focus();
18259         }else{
18260             Roo.log('editor - hiding textarea');
18261 //            Roo.log('out')
18262 //            Roo.log(this.pushValue()); 
18263             this.pushValue();
18264             
18265             this.inputEl().addClass(['hide', 'x-hidden']);
18266             this.inputEl().dom.setAttribute('tabIndex', -1);
18267             //this.deferFocus();
18268         }
18269          
18270         if(this.resizable){
18271             this.setSize(this.wrap.getSize());
18272         }
18273         
18274         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18275     },
18276  
18277     // private (for BoxComponent)
18278     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18279
18280     // private (for BoxComponent)
18281     getResizeEl : function(){
18282         return this.wrap;
18283     },
18284
18285     // private (for BoxComponent)
18286     getPositionEl : function(){
18287         return this.wrap;
18288     },
18289
18290     // private
18291     initEvents : function(){
18292         this.originalValue = this.getValue();
18293     },
18294
18295 //    /**
18296 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18297 //     * @method
18298 //     */
18299 //    markInvalid : Roo.emptyFn,
18300 //    /**
18301 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18302 //     * @method
18303 //     */
18304 //    clearInvalid : Roo.emptyFn,
18305
18306     setValue : function(v){
18307         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18308         this.editorcore.pushValue();
18309     },
18310
18311      
18312     // private
18313     deferFocus : function(){
18314         this.focus.defer(10, this);
18315     },
18316
18317     // doc'ed in Field
18318     focus : function(){
18319         this.editorcore.focus();
18320         
18321     },
18322       
18323
18324     // private
18325     onDestroy : function(){
18326         
18327         
18328         
18329         if(this.rendered){
18330             
18331             for (var i =0; i < this.toolbars.length;i++) {
18332                 // fixme - ask toolbars for heights?
18333                 this.toolbars[i].onDestroy();
18334             }
18335             
18336             this.wrap.dom.innerHTML = '';
18337             this.wrap.remove();
18338         }
18339     },
18340
18341     // private
18342     onFirstFocus : function(){
18343         //Roo.log("onFirstFocus");
18344         this.editorcore.onFirstFocus();
18345          for (var i =0; i < this.toolbars.length;i++) {
18346             this.toolbars[i].onFirstFocus();
18347         }
18348         
18349     },
18350     
18351     // private
18352     syncValue : function()
18353     {   
18354         this.editorcore.syncValue();
18355     },
18356     
18357     pushValue : function()
18358     {   
18359         this.editorcore.pushValue();
18360     }
18361      
18362     
18363     // hide stuff that is not compatible
18364     /**
18365      * @event blur
18366      * @hide
18367      */
18368     /**
18369      * @event change
18370      * @hide
18371      */
18372     /**
18373      * @event focus
18374      * @hide
18375      */
18376     /**
18377      * @event specialkey
18378      * @hide
18379      */
18380     /**
18381      * @cfg {String} fieldClass @hide
18382      */
18383     /**
18384      * @cfg {String} focusClass @hide
18385      */
18386     /**
18387      * @cfg {String} autoCreate @hide
18388      */
18389     /**
18390      * @cfg {String} inputType @hide
18391      */
18392     /**
18393      * @cfg {String} invalidClass @hide
18394      */
18395     /**
18396      * @cfg {String} invalidText @hide
18397      */
18398     /**
18399      * @cfg {String} msgFx @hide
18400      */
18401     /**
18402      * @cfg {String} validateOnBlur @hide
18403      */
18404 });
18405  
18406     
18407    
18408    
18409    
18410       
18411 Roo.namespace('Roo.bootstrap.htmleditor');
18412 /**
18413  * @class Roo.bootstrap.HtmlEditorToolbar1
18414  * Basic Toolbar
18415  * 
18416  * Usage:
18417  *
18418  new Roo.bootstrap.HtmlEditor({
18419     ....
18420     toolbars : [
18421         new Roo.bootstrap.HtmlEditorToolbar1({
18422             disable : { fonts: 1 , format: 1, ..., ... , ...],
18423             btns : [ .... ]
18424         })
18425     }
18426      
18427  * 
18428  * @cfg {Object} disable List of elements to disable..
18429  * @cfg {Array} btns List of additional buttons.
18430  * 
18431  * 
18432  * NEEDS Extra CSS? 
18433  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18434  */
18435  
18436 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18437 {
18438     
18439     Roo.apply(this, config);
18440     
18441     // default disabled, based on 'good practice'..
18442     this.disable = this.disable || {};
18443     Roo.applyIf(this.disable, {
18444         fontSize : true,
18445         colors : true,
18446         specialElements : true
18447     });
18448     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18449     
18450     this.editor = config.editor;
18451     this.editorcore = config.editor.editorcore;
18452     
18453     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18454     
18455     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18456     // dont call parent... till later.
18457 }
18458 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18459      
18460     bar : true,
18461     
18462     editor : false,
18463     editorcore : false,
18464     
18465     
18466     formats : [
18467         "p" ,  
18468         "h1","h2","h3","h4","h5","h6", 
18469         "pre", "code", 
18470         "abbr", "acronym", "address", "cite", "samp", "var",
18471         'div','span'
18472     ],
18473     
18474     onRender : function(ct, position)
18475     {
18476        // Roo.log("Call onRender: " + this.xtype);
18477         
18478        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18479        Roo.log(this.el);
18480        this.el.dom.style.marginBottom = '0';
18481        var _this = this;
18482        var editorcore = this.editorcore;
18483        var editor= this.editor;
18484        
18485        var children = [];
18486        var btn = function(id,cmd , toggle, handler){
18487        
18488             var  event = toggle ? 'toggle' : 'click';
18489        
18490             var a = {
18491                 size : 'sm',
18492                 xtype: 'Button',
18493                 xns: Roo.bootstrap,
18494                 glyphicon : id,
18495                 cmd : id || cmd,
18496                 enableToggle:toggle !== false,
18497                 //html : 'submit'
18498                 pressed : toggle ? false : null,
18499                 listeners : {}
18500             }
18501             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18502                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18503             }
18504             children.push(a);
18505             return a;
18506        }
18507         
18508         var style = {
18509                 xtype: 'Button',
18510                 size : 'sm',
18511                 xns: Roo.bootstrap,
18512                 glyphicon : 'font',
18513                 //html : 'submit'
18514                 menu : {
18515                     xtype: 'Menu',
18516                     xns: Roo.bootstrap,
18517                     items:  []
18518                 }
18519         };
18520         Roo.each(this.formats, function(f) {
18521             style.menu.items.push({
18522                 xtype :'MenuItem',
18523                 xns: Roo.bootstrap,
18524                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18525                 tagname : f,
18526                 listeners : {
18527                     click : function()
18528                     {
18529                         editorcore.insertTag(this.tagname);
18530                         editor.focus();
18531                     }
18532                 }
18533                 
18534             });
18535         });
18536          children.push(style);   
18537             
18538             
18539         btn('bold',false,true);
18540         btn('italic',false,true);
18541         btn('align-left', 'justifyleft',true);
18542         btn('align-center', 'justifycenter',true);
18543         btn('align-right' , 'justifyright',true);
18544         btn('link', false, false, function(btn) {
18545             //Roo.log("create link?");
18546             var url = prompt(this.createLinkText, this.defaultLinkValue);
18547             if(url && url != 'http:/'+'/'){
18548                 this.editorcore.relayCmd('createlink', url);
18549             }
18550         }),
18551         btn('list','insertunorderedlist',true);
18552         btn('pencil', false,true, function(btn){
18553                 Roo.log(this);
18554                 
18555                 this.toggleSourceEdit(btn.pressed);
18556         });
18557         /*
18558         var cog = {
18559                 xtype: 'Button',
18560                 size : 'sm',
18561                 xns: Roo.bootstrap,
18562                 glyphicon : 'cog',
18563                 //html : 'submit'
18564                 menu : {
18565                     xtype: 'Menu',
18566                     xns: Roo.bootstrap,
18567                     items:  []
18568                 }
18569         };
18570         
18571         cog.menu.items.push({
18572             xtype :'MenuItem',
18573             xns: Roo.bootstrap,
18574             html : Clean styles,
18575             tagname : f,
18576             listeners : {
18577                 click : function()
18578                 {
18579                     editorcore.insertTag(this.tagname);
18580                     editor.focus();
18581                 }
18582             }
18583             
18584         });
18585        */
18586         
18587          
18588        this.xtype = 'NavSimplebar';
18589         
18590         for(var i=0;i< children.length;i++) {
18591             
18592             this.buttons.add(this.addxtypeChild(children[i]));
18593             
18594         }
18595         
18596         editor.on('editorevent', this.updateToolbar, this);
18597     },
18598     onBtnClick : function(id)
18599     {
18600        this.editorcore.relayCmd(id);
18601        this.editorcore.focus();
18602     },
18603     
18604     /**
18605      * Protected method that will not generally be called directly. It triggers
18606      * a toolbar update by reading the markup state of the current selection in the editor.
18607      */
18608     updateToolbar: function(){
18609
18610         if(!this.editorcore.activated){
18611             this.editor.onFirstFocus(); // is this neeed?
18612             return;
18613         }
18614
18615         var btns = this.buttons; 
18616         var doc = this.editorcore.doc;
18617         btns.get('bold').setActive(doc.queryCommandState('bold'));
18618         btns.get('italic').setActive(doc.queryCommandState('italic'));
18619         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18620         
18621         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18622         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18623         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18624         
18625         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18626         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18627          /*
18628         
18629         var ans = this.editorcore.getAllAncestors();
18630         if (this.formatCombo) {
18631             
18632             
18633             var store = this.formatCombo.store;
18634             this.formatCombo.setValue("");
18635             for (var i =0; i < ans.length;i++) {
18636                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18637                     // select it..
18638                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18639                     break;
18640                 }
18641             }
18642         }
18643         
18644         
18645         
18646         // hides menus... - so this cant be on a menu...
18647         Roo.bootstrap.MenuMgr.hideAll();
18648         */
18649         Roo.bootstrap.MenuMgr.hideAll();
18650         //this.editorsyncValue();
18651     },
18652     onFirstFocus: function() {
18653         this.buttons.each(function(item){
18654            item.enable();
18655         });
18656     },
18657     toggleSourceEdit : function(sourceEditMode){
18658         
18659           
18660         if(sourceEditMode){
18661             Roo.log("disabling buttons");
18662            this.buttons.each( function(item){
18663                 if(item.cmd != 'pencil'){
18664                     item.disable();
18665                 }
18666             });
18667           
18668         }else{
18669             Roo.log("enabling buttons");
18670             if(this.editorcore.initialized){
18671                 this.buttons.each( function(item){
18672                     item.enable();
18673                 });
18674             }
18675             
18676         }
18677         Roo.log("calling toggole on editor");
18678         // tell the editor that it's been pressed..
18679         this.editor.toggleSourceEdit(sourceEditMode);
18680        
18681     }
18682 });
18683
18684
18685
18686
18687
18688 /**
18689  * @class Roo.bootstrap.Table.AbstractSelectionModel
18690  * @extends Roo.util.Observable
18691  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18692  * implemented by descendant classes.  This class should not be directly instantiated.
18693  * @constructor
18694  */
18695 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18696     this.locked = false;
18697     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18698 };
18699
18700
18701 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18702     /** @ignore Called by the grid automatically. Do not call directly. */
18703     init : function(grid){
18704         this.grid = grid;
18705         this.initEvents();
18706     },
18707
18708     /**
18709      * Locks the selections.
18710      */
18711     lock : function(){
18712         this.locked = true;
18713     },
18714
18715     /**
18716      * Unlocks the selections.
18717      */
18718     unlock : function(){
18719         this.locked = false;
18720     },
18721
18722     /**
18723      * Returns true if the selections are locked.
18724      * @return {Boolean}
18725      */
18726     isLocked : function(){
18727         return this.locked;
18728     }
18729 });
18730 /**
18731  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18732  * @class Roo.bootstrap.Table.RowSelectionModel
18733  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18734  * It supports multiple selections and keyboard selection/navigation. 
18735  * @constructor
18736  * @param {Object} config
18737  */
18738
18739 Roo.bootstrap.Table.RowSelectionModel = function(config){
18740     Roo.apply(this, config);
18741     this.selections = new Roo.util.MixedCollection(false, function(o){
18742         return o.id;
18743     });
18744
18745     this.last = false;
18746     this.lastActive = false;
18747
18748     this.addEvents({
18749         /**
18750              * @event selectionchange
18751              * Fires when the selection changes
18752              * @param {SelectionModel} this
18753              */
18754             "selectionchange" : true,
18755         /**
18756              * @event afterselectionchange
18757              * Fires after the selection changes (eg. by key press or clicking)
18758              * @param {SelectionModel} this
18759              */
18760             "afterselectionchange" : true,
18761         /**
18762              * @event beforerowselect
18763              * Fires when a row is selected being selected, return false to cancel.
18764              * @param {SelectionModel} this
18765              * @param {Number} rowIndex The selected index
18766              * @param {Boolean} keepExisting False if other selections will be cleared
18767              */
18768             "beforerowselect" : true,
18769         /**
18770              * @event rowselect
18771              * Fires when a row is selected.
18772              * @param {SelectionModel} this
18773              * @param {Number} rowIndex The selected index
18774              * @param {Roo.data.Record} r The record
18775              */
18776             "rowselect" : true,
18777         /**
18778              * @event rowdeselect
18779              * Fires when a row is deselected.
18780              * @param {SelectionModel} this
18781              * @param {Number} rowIndex The selected index
18782              */
18783         "rowdeselect" : true
18784     });
18785     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18786     this.locked = false;
18787 };
18788
18789 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18790     /**
18791      * @cfg {Boolean} singleSelect
18792      * True to allow selection of only one row at a time (defaults to false)
18793      */
18794     singleSelect : false,
18795
18796     // private
18797     initEvents : function(){
18798
18799         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18800             this.grid.on("mousedown", this.handleMouseDown, this);
18801         }else{ // allow click to work like normal
18802             this.grid.on("rowclick", this.handleDragableRowClick, this);
18803         }
18804
18805         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18806             "up" : function(e){
18807                 if(!e.shiftKey){
18808                     this.selectPrevious(e.shiftKey);
18809                 }else if(this.last !== false && this.lastActive !== false){
18810                     var last = this.last;
18811                     this.selectRange(this.last,  this.lastActive-1);
18812                     this.grid.getView().focusRow(this.lastActive);
18813                     if(last !== false){
18814                         this.last = last;
18815                     }
18816                 }else{
18817                     this.selectFirstRow();
18818                 }
18819                 this.fireEvent("afterselectionchange", this);
18820             },
18821             "down" : function(e){
18822                 if(!e.shiftKey){
18823                     this.selectNext(e.shiftKey);
18824                 }else if(this.last !== false && this.lastActive !== false){
18825                     var last = this.last;
18826                     this.selectRange(this.last,  this.lastActive+1);
18827                     this.grid.getView().focusRow(this.lastActive);
18828                     if(last !== false){
18829                         this.last = last;
18830                     }
18831                 }else{
18832                     this.selectFirstRow();
18833                 }
18834                 this.fireEvent("afterselectionchange", this);
18835             },
18836             scope: this
18837         });
18838
18839         var view = this.grid.view;
18840         view.on("refresh", this.onRefresh, this);
18841         view.on("rowupdated", this.onRowUpdated, this);
18842         view.on("rowremoved", this.onRemove, this);
18843     },
18844
18845     // private
18846     onRefresh : function(){
18847         var ds = this.grid.dataSource, i, v = this.grid.view;
18848         var s = this.selections;
18849         s.each(function(r){
18850             if((i = ds.indexOfId(r.id)) != -1){
18851                 v.onRowSelect(i);
18852             }else{
18853                 s.remove(r);
18854             }
18855         });
18856     },
18857
18858     // private
18859     onRemove : function(v, index, r){
18860         this.selections.remove(r);
18861     },
18862
18863     // private
18864     onRowUpdated : function(v, index, r){
18865         if(this.isSelected(r)){
18866             v.onRowSelect(index);
18867         }
18868     },
18869
18870     /**
18871      * Select records.
18872      * @param {Array} records The records to select
18873      * @param {Boolean} keepExisting (optional) True to keep existing selections
18874      */
18875     selectRecords : function(records, keepExisting){
18876         if(!keepExisting){
18877             this.clearSelections();
18878         }
18879         var ds = this.grid.dataSource;
18880         for(var i = 0, len = records.length; i < len; i++){
18881             this.selectRow(ds.indexOf(records[i]), true);
18882         }
18883     },
18884
18885     /**
18886      * Gets the number of selected rows.
18887      * @return {Number}
18888      */
18889     getCount : function(){
18890         return this.selections.length;
18891     },
18892
18893     /**
18894      * Selects the first row in the grid.
18895      */
18896     selectFirstRow : function(){
18897         this.selectRow(0);
18898     },
18899
18900     /**
18901      * Select the last row.
18902      * @param {Boolean} keepExisting (optional) True to keep existing selections
18903      */
18904     selectLastRow : function(keepExisting){
18905         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18906     },
18907
18908     /**
18909      * Selects the row immediately following the last selected row.
18910      * @param {Boolean} keepExisting (optional) True to keep existing selections
18911      */
18912     selectNext : function(keepExisting){
18913         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18914             this.selectRow(this.last+1, keepExisting);
18915             this.grid.getView().focusRow(this.last);
18916         }
18917     },
18918
18919     /**
18920      * Selects the row that precedes the last selected row.
18921      * @param {Boolean} keepExisting (optional) True to keep existing selections
18922      */
18923     selectPrevious : function(keepExisting){
18924         if(this.last){
18925             this.selectRow(this.last-1, keepExisting);
18926             this.grid.getView().focusRow(this.last);
18927         }
18928     },
18929
18930     /**
18931      * Returns the selected records
18932      * @return {Array} Array of selected records
18933      */
18934     getSelections : function(){
18935         return [].concat(this.selections.items);
18936     },
18937
18938     /**
18939      * Returns the first selected record.
18940      * @return {Record}
18941      */
18942     getSelected : function(){
18943         return this.selections.itemAt(0);
18944     },
18945
18946
18947     /**
18948      * Clears all selections.
18949      */
18950     clearSelections : function(fast){
18951         if(this.locked) return;
18952         if(fast !== true){
18953             var ds = this.grid.dataSource;
18954             var s = this.selections;
18955             s.each(function(r){
18956                 this.deselectRow(ds.indexOfId(r.id));
18957             }, this);
18958             s.clear();
18959         }else{
18960             this.selections.clear();
18961         }
18962         this.last = false;
18963     },
18964
18965
18966     /**
18967      * Selects all rows.
18968      */
18969     selectAll : function(){
18970         if(this.locked) return;
18971         this.selections.clear();
18972         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18973             this.selectRow(i, true);
18974         }
18975     },
18976
18977     /**
18978      * Returns True if there is a selection.
18979      * @return {Boolean}
18980      */
18981     hasSelection : function(){
18982         return this.selections.length > 0;
18983     },
18984
18985     /**
18986      * Returns True if the specified row is selected.
18987      * @param {Number/Record} record The record or index of the record to check
18988      * @return {Boolean}
18989      */
18990     isSelected : function(index){
18991         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18992         return (r && this.selections.key(r.id) ? true : false);
18993     },
18994
18995     /**
18996      * Returns True if the specified record id is selected.
18997      * @param {String} id The id of record to check
18998      * @return {Boolean}
18999      */
19000     isIdSelected : function(id){
19001         return (this.selections.key(id) ? true : false);
19002     },
19003
19004     // private
19005     handleMouseDown : function(e, t){
19006         var view = this.grid.getView(), rowIndex;
19007         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
19008             return;
19009         };
19010         if(e.shiftKey && this.last !== false){
19011             var last = this.last;
19012             this.selectRange(last, rowIndex, e.ctrlKey);
19013             this.last = last; // reset the last
19014             view.focusRow(rowIndex);
19015         }else{
19016             var isSelected = this.isSelected(rowIndex);
19017             if(e.button !== 0 && isSelected){
19018                 view.focusRow(rowIndex);
19019             }else if(e.ctrlKey && isSelected){
19020                 this.deselectRow(rowIndex);
19021             }else if(!isSelected){
19022                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19023                 view.focusRow(rowIndex);
19024             }
19025         }
19026         this.fireEvent("afterselectionchange", this);
19027     },
19028     // private
19029     handleDragableRowClick :  function(grid, rowIndex, e) 
19030     {
19031         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19032             this.selectRow(rowIndex, false);
19033             grid.view.focusRow(rowIndex);
19034              this.fireEvent("afterselectionchange", this);
19035         }
19036     },
19037     
19038     /**
19039      * Selects multiple rows.
19040      * @param {Array} rows Array of the indexes of the row to select
19041      * @param {Boolean} keepExisting (optional) True to keep existing selections
19042      */
19043     selectRows : function(rows, keepExisting){
19044         if(!keepExisting){
19045             this.clearSelections();
19046         }
19047         for(var i = 0, len = rows.length; i < len; i++){
19048             this.selectRow(rows[i], true);
19049         }
19050     },
19051
19052     /**
19053      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19054      * @param {Number} startRow The index of the first row in the range
19055      * @param {Number} endRow The index of the last row in the range
19056      * @param {Boolean} keepExisting (optional) True to retain existing selections
19057      */
19058     selectRange : function(startRow, endRow, keepExisting){
19059         if(this.locked) return;
19060         if(!keepExisting){
19061             this.clearSelections();
19062         }
19063         if(startRow <= endRow){
19064             for(var i = startRow; i <= endRow; i++){
19065                 this.selectRow(i, true);
19066             }
19067         }else{
19068             for(var i = startRow; i >= endRow; i--){
19069                 this.selectRow(i, true);
19070             }
19071         }
19072     },
19073
19074     /**
19075      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19076      * @param {Number} startRow The index of the first row in the range
19077      * @param {Number} endRow The index of the last row in the range
19078      */
19079     deselectRange : function(startRow, endRow, preventViewNotify){
19080         if(this.locked) return;
19081         for(var i = startRow; i <= endRow; i++){
19082             this.deselectRow(i, preventViewNotify);
19083         }
19084     },
19085
19086     /**
19087      * Selects a row.
19088      * @param {Number} row The index of the row to select
19089      * @param {Boolean} keepExisting (optional) True to keep existing selections
19090      */
19091     selectRow : function(index, keepExisting, preventViewNotify){
19092         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19093         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19094             if(!keepExisting || this.singleSelect){
19095                 this.clearSelections();
19096             }
19097             var r = this.grid.dataSource.getAt(index);
19098             this.selections.add(r);
19099             this.last = this.lastActive = index;
19100             if(!preventViewNotify){
19101                 this.grid.getView().onRowSelect(index);
19102             }
19103             this.fireEvent("rowselect", this, index, r);
19104             this.fireEvent("selectionchange", this);
19105         }
19106     },
19107
19108     /**
19109      * Deselects a row.
19110      * @param {Number} row The index of the row to deselect
19111      */
19112     deselectRow : function(index, preventViewNotify){
19113         if(this.locked) return;
19114         if(this.last == index){
19115             this.last = false;
19116         }
19117         if(this.lastActive == index){
19118             this.lastActive = false;
19119         }
19120         var r = this.grid.dataSource.getAt(index);
19121         this.selections.remove(r);
19122         if(!preventViewNotify){
19123             this.grid.getView().onRowDeselect(index);
19124         }
19125         this.fireEvent("rowdeselect", this, index);
19126         this.fireEvent("selectionchange", this);
19127     },
19128
19129     // private
19130     restoreLast : function(){
19131         if(this._last){
19132             this.last = this._last;
19133         }
19134     },
19135
19136     // private
19137     acceptsNav : function(row, col, cm){
19138         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19139     },
19140
19141     // private
19142     onEditorKey : function(field, e){
19143         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19144         if(k == e.TAB){
19145             e.stopEvent();
19146             ed.completeEdit();
19147             if(e.shiftKey){
19148                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19149             }else{
19150                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19151             }
19152         }else if(k == e.ENTER && !e.ctrlKey){
19153             e.stopEvent();
19154             ed.completeEdit();
19155             if(e.shiftKey){
19156                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19157             }else{
19158                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19159             }
19160         }else if(k == e.ESC){
19161             ed.cancelEdit();
19162         }
19163         if(newCell){
19164             g.startEditing(newCell[0], newCell[1]);
19165         }
19166     }
19167 });/*
19168  * Based on:
19169  * Ext JS Library 1.1.1
19170  * Copyright(c) 2006-2007, Ext JS, LLC.
19171  *
19172  * Originally Released Under LGPL - original licence link has changed is not relivant.
19173  *
19174  * Fork - LGPL
19175  * <script type="text/javascript">
19176  */
19177  
19178 /**
19179  * @class Roo.bootstrap.PagingToolbar
19180  * @extends Roo.Row
19181  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19182  * @constructor
19183  * Create a new PagingToolbar
19184  * @param {Object} config The config object
19185  */
19186 Roo.bootstrap.PagingToolbar = function(config)
19187 {
19188     // old args format still supported... - xtype is prefered..
19189         // created from xtype...
19190     var ds = config.dataSource;
19191     this.toolbarItems = [];
19192     if (config.items) {
19193         this.toolbarItems = config.items;
19194 //        config.items = [];
19195     }
19196     
19197     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19198     this.ds = ds;
19199     this.cursor = 0;
19200     if (ds) { 
19201         this.bind(ds);
19202     }
19203     
19204     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19205     
19206 };
19207
19208 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19209     /**
19210      * @cfg {Roo.data.Store} dataSource
19211      * The underlying data store providing the paged data
19212      */
19213     /**
19214      * @cfg {String/HTMLElement/Element} container
19215      * container The id or element that will contain the toolbar
19216      */
19217     /**
19218      * @cfg {Boolean} displayInfo
19219      * True to display the displayMsg (defaults to false)
19220      */
19221     /**
19222      * @cfg {Number} pageSize
19223      * The number of records to display per page (defaults to 20)
19224      */
19225     pageSize: 20,
19226     /**
19227      * @cfg {String} displayMsg
19228      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19229      */
19230     displayMsg : 'Displaying {0} - {1} of {2}',
19231     /**
19232      * @cfg {String} emptyMsg
19233      * The message to display when no records are found (defaults to "No data to display")
19234      */
19235     emptyMsg : 'No data to display',
19236     /**
19237      * Customizable piece of the default paging text (defaults to "Page")
19238      * @type String
19239      */
19240     beforePageText : "Page",
19241     /**
19242      * Customizable piece of the default paging text (defaults to "of %0")
19243      * @type String
19244      */
19245     afterPageText : "of {0}",
19246     /**
19247      * Customizable piece of the default paging text (defaults to "First Page")
19248      * @type String
19249      */
19250     firstText : "First Page",
19251     /**
19252      * Customizable piece of the default paging text (defaults to "Previous Page")
19253      * @type String
19254      */
19255     prevText : "Previous Page",
19256     /**
19257      * Customizable piece of the default paging text (defaults to "Next Page")
19258      * @type String
19259      */
19260     nextText : "Next Page",
19261     /**
19262      * Customizable piece of the default paging text (defaults to "Last Page")
19263      * @type String
19264      */
19265     lastText : "Last Page",
19266     /**
19267      * Customizable piece of the default paging text (defaults to "Refresh")
19268      * @type String
19269      */
19270     refreshText : "Refresh",
19271
19272     buttons : false,
19273     // private
19274     onRender : function(ct, position) 
19275     {
19276         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19277         this.navgroup.parentId = this.id;
19278         this.navgroup.onRender(this.el, null);
19279         // add the buttons to the navgroup
19280         
19281         if(this.displayInfo){
19282             Roo.log(this.el.select('ul.navbar-nav',true).first());
19283             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19284             this.displayEl = this.el.select('.x-paging-info', true).first();
19285 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19286 //            this.displayEl = navel.el.select('span',true).first();
19287         }
19288         
19289         var _this = this;
19290         
19291         if(this.buttons){
19292             Roo.each(_this.buttons, function(e){
19293                Roo.factory(e).onRender(_this.el, null);
19294             });
19295         }
19296             
19297         Roo.each(_this.toolbarItems, function(e) {
19298             _this.navgroup.addItem(e);
19299         });
19300         
19301         this.first = this.navgroup.addItem({
19302             tooltip: this.firstText,
19303             cls: "prev",
19304             icon : 'fa fa-backward',
19305             disabled: true,
19306             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19307         });
19308         
19309         this.prev =  this.navgroup.addItem({
19310             tooltip: this.prevText,
19311             cls: "prev",
19312             icon : 'fa fa-step-backward',
19313             disabled: true,
19314             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19315         });
19316     //this.addSeparator();
19317         
19318         
19319         var field = this.navgroup.addItem( {
19320             tagtype : 'span',
19321             cls : 'x-paging-position',
19322             
19323             html : this.beforePageText  +
19324                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19325                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19326          } ); //?? escaped?
19327         
19328         this.field = field.el.select('input', true).first();
19329         this.field.on("keydown", this.onPagingKeydown, this);
19330         this.field.on("focus", function(){this.dom.select();});
19331     
19332     
19333         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19334         //this.field.setHeight(18);
19335         //this.addSeparator();
19336         this.next = this.navgroup.addItem({
19337             tooltip: this.nextText,
19338             cls: "next",
19339             html : ' <i class="fa fa-step-forward">',
19340             disabled: true,
19341             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19342         });
19343         this.last = this.navgroup.addItem({
19344             tooltip: this.lastText,
19345             icon : 'fa fa-forward',
19346             cls: "next",
19347             disabled: true,
19348             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19349         });
19350     //this.addSeparator();
19351         this.loading = this.navgroup.addItem({
19352             tooltip: this.refreshText,
19353             icon: 'fa fa-refresh',
19354             
19355             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19356         });
19357
19358     },
19359
19360     // private
19361     updateInfo : function(){
19362         if(this.displayEl){
19363             var count = this.ds.getCount();
19364             var msg = count == 0 ?
19365                 this.emptyMsg :
19366                 String.format(
19367                     this.displayMsg,
19368                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19369                 );
19370             this.displayEl.update(msg);
19371         }
19372     },
19373
19374     // private
19375     onLoad : function(ds, r, o){
19376        this.cursor = o.params ? o.params.start : 0;
19377        var d = this.getPageData(),
19378             ap = d.activePage,
19379             ps = d.pages;
19380         
19381        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19382        this.field.dom.value = ap;
19383        this.first.setDisabled(ap == 1);
19384        this.prev.setDisabled(ap == 1);
19385        this.next.setDisabled(ap == ps);
19386        this.last.setDisabled(ap == ps);
19387        this.loading.enable();
19388        this.updateInfo();
19389     },
19390
19391     // private
19392     getPageData : function(){
19393         var total = this.ds.getTotalCount();
19394         return {
19395             total : total,
19396             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19397             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19398         };
19399     },
19400
19401     // private
19402     onLoadError : function(){
19403         this.loading.enable();
19404     },
19405
19406     // private
19407     onPagingKeydown : function(e){
19408         var k = e.getKey();
19409         var d = this.getPageData();
19410         if(k == e.RETURN){
19411             var v = this.field.dom.value, pageNum;
19412             if(!v || isNaN(pageNum = parseInt(v, 10))){
19413                 this.field.dom.value = d.activePage;
19414                 return;
19415             }
19416             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19417             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19418             e.stopEvent();
19419         }
19420         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))
19421         {
19422           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19423           this.field.dom.value = pageNum;
19424           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19425           e.stopEvent();
19426         }
19427         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19428         {
19429           var v = this.field.dom.value, pageNum; 
19430           var increment = (e.shiftKey) ? 10 : 1;
19431           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19432             increment *= -1;
19433           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19434             this.field.dom.value = d.activePage;
19435             return;
19436           }
19437           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19438           {
19439             this.field.dom.value = parseInt(v, 10) + increment;
19440             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19441             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19442           }
19443           e.stopEvent();
19444         }
19445     },
19446
19447     // private
19448     beforeLoad : function(){
19449         if(this.loading){
19450             this.loading.disable();
19451         }
19452     },
19453
19454     // private
19455     onClick : function(which){
19456         var ds = this.ds;
19457         if (!ds) {
19458             return;
19459         }
19460         switch(which){
19461             case "first":
19462                 ds.load({params:{start: 0, limit: this.pageSize}});
19463             break;
19464             case "prev":
19465                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19466             break;
19467             case "next":
19468                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19469             break;
19470             case "last":
19471                 var total = ds.getTotalCount();
19472                 var extra = total % this.pageSize;
19473                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19474                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19475             break;
19476             case "refresh":
19477                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19478             break;
19479         }
19480     },
19481
19482     /**
19483      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19484      * @param {Roo.data.Store} store The data store to unbind
19485      */
19486     unbind : function(ds){
19487         ds.un("beforeload", this.beforeLoad, this);
19488         ds.un("load", this.onLoad, this);
19489         ds.un("loadexception", this.onLoadError, this);
19490         ds.un("remove", this.updateInfo, this);
19491         ds.un("add", this.updateInfo, this);
19492         this.ds = undefined;
19493     },
19494
19495     /**
19496      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19497      * @param {Roo.data.Store} store The data store to bind
19498      */
19499     bind : function(ds){
19500         ds.on("beforeload", this.beforeLoad, this);
19501         ds.on("load", this.onLoad, this);
19502         ds.on("loadexception", this.onLoadError, this);
19503         ds.on("remove", this.updateInfo, this);
19504         ds.on("add", this.updateInfo, this);
19505         this.ds = ds;
19506     }
19507 });/*
19508  * - LGPL
19509  *
19510  * element
19511  * 
19512  */
19513
19514 /**
19515  * @class Roo.bootstrap.MessageBar
19516  * @extends Roo.bootstrap.Component
19517  * Bootstrap MessageBar class
19518  * @cfg {String} html contents of the MessageBar
19519  * @cfg {String} weight (info | success | warning | danger) default info
19520  * @cfg {String} beforeClass insert the bar before the given class
19521  * @cfg {Boolean} closable (true | false) default false
19522  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19523  * 
19524  * @constructor
19525  * Create a new Element
19526  * @param {Object} config The config object
19527  */
19528
19529 Roo.bootstrap.MessageBar = function(config){
19530     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19531 };
19532
19533 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19534     
19535     html: '',
19536     weight: 'info',
19537     closable: false,
19538     fixed: false,
19539     beforeClass: 'bootstrap-sticky-wrap',
19540     
19541     getAutoCreate : function(){
19542         
19543         var cfg = {
19544             tag: 'div',
19545             cls: 'alert alert-dismissable alert-' + this.weight,
19546             cn: [
19547                 {
19548                     tag: 'span',
19549                     cls: 'message',
19550                     html: this.html || ''
19551                 }
19552             ]
19553         }
19554         
19555         if(this.fixed){
19556             cfg.cls += ' alert-messages-fixed';
19557         }
19558         
19559         if(this.closable){
19560             cfg.cn.push({
19561                 tag: 'button',
19562                 cls: 'close',
19563                 html: 'x'
19564             });
19565         }
19566         
19567         return cfg;
19568     },
19569     
19570     onRender : function(ct, position)
19571     {
19572         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19573         
19574         if(!this.el){
19575             var cfg = Roo.apply({},  this.getAutoCreate());
19576             cfg.id = Roo.id();
19577             
19578             if (this.cls) {
19579                 cfg.cls += ' ' + this.cls;
19580             }
19581             if (this.style) {
19582                 cfg.style = this.style;
19583             }
19584             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19585             
19586             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19587         }
19588         
19589         this.el.select('>button.close').on('click', this.hide, this);
19590         
19591     },
19592     
19593     show : function()
19594     {
19595         if (!this.rendered) {
19596             this.render();
19597         }
19598         
19599         this.el.show();
19600         
19601         this.fireEvent('show', this);
19602         
19603     },
19604     
19605     hide : function()
19606     {
19607         if (!this.rendered) {
19608             this.render();
19609         }
19610         
19611         this.el.hide();
19612         
19613         this.fireEvent('hide', this);
19614     },
19615     
19616     update : function()
19617     {
19618 //        var e = this.el.dom.firstChild;
19619 //        
19620 //        if(this.closable){
19621 //            e = e.nextSibling;
19622 //        }
19623 //        
19624 //        e.data = this.html || '';
19625
19626         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19627     }
19628    
19629 });
19630
19631  
19632
19633      /*
19634  * - LGPL
19635  *
19636  * Graph
19637  * 
19638  */
19639
19640
19641 /**
19642  * @class Roo.bootstrap.Graph
19643  * @extends Roo.bootstrap.Component
19644  * Bootstrap Graph class
19645 > Prameters
19646  -sm {number} sm 4
19647  -md {number} md 5
19648  @cfg {String} graphtype  bar | vbar | pie
19649  @cfg {number} g_x coodinator | centre x (pie)
19650  @cfg {number} g_y coodinator | centre y (pie)
19651  @cfg {number} g_r radius (pie)
19652  @cfg {number} g_height height of the chart (respected by all elements in the set)
19653  @cfg {number} g_width width of the chart (respected by all elements in the set)
19654  @cfg {Object} title The title of the chart
19655     
19656  -{Array}  values
19657  -opts (object) options for the chart 
19658      o {
19659      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19660      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19661      o vgutter (number)
19662      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.
19663      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19664      o to
19665      o stretch (boolean)
19666      o }
19667  -opts (object) options for the pie
19668      o{
19669      o cut
19670      o startAngle (number)
19671      o endAngle (number)
19672      } 
19673  *
19674  * @constructor
19675  * Create a new Input
19676  * @param {Object} config The config object
19677  */
19678
19679 Roo.bootstrap.Graph = function(config){
19680     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19681     
19682     this.addEvents({
19683         // img events
19684         /**
19685          * @event click
19686          * The img click event for the img.
19687          * @param {Roo.EventObject} e
19688          */
19689         "click" : true
19690     });
19691 };
19692
19693 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19694     
19695     sm: 4,
19696     md: 5,
19697     graphtype: 'bar',
19698     g_height: 250,
19699     g_width: 400,
19700     g_x: 50,
19701     g_y: 50,
19702     g_r: 30,
19703     opts:{
19704         //g_colors: this.colors,
19705         g_type: 'soft',
19706         g_gutter: '20%'
19707
19708     },
19709     title : false,
19710
19711     getAutoCreate : function(){
19712         
19713         var cfg = {
19714             tag: 'div',
19715             html : null
19716         }
19717         
19718         
19719         return  cfg;
19720     },
19721
19722     onRender : function(ct,position){
19723         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19724         this.raphael = Raphael(this.el.dom);
19725         
19726                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19727                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19728                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19729                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19730                 /*
19731                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19732                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19733                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19734                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19735                 
19736                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19737                 r.barchart(330, 10, 300, 220, data1);
19738                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19739                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19740                 */
19741                 
19742                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19743                 // r.barchart(30, 30, 560, 250,  xdata, {
19744                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19745                 //     axis : "0 0 1 1",
19746                 //     axisxlabels :  xdata
19747                 //     //yvalues : cols,
19748                    
19749                 // });
19750 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19751 //        
19752 //        this.load(null,xdata,{
19753 //                axis : "0 0 1 1",
19754 //                axisxlabels :  xdata
19755 //                });
19756
19757     },
19758
19759     load : function(graphtype,xdata,opts){
19760         this.raphael.clear();
19761         if(!graphtype) {
19762             graphtype = this.graphtype;
19763         }
19764         if(!opts){
19765             opts = this.opts;
19766         }
19767         var r = this.raphael,
19768             fin = function () {
19769                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19770             },
19771             fout = function () {
19772                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19773             },
19774             pfin = function() {
19775                 this.sector.stop();
19776                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19777
19778                 if (this.label) {
19779                     this.label[0].stop();
19780                     this.label[0].attr({ r: 7.5 });
19781                     this.label[1].attr({ "font-weight": 800 });
19782                 }
19783             },
19784             pfout = function() {
19785                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19786
19787                 if (this.label) {
19788                     this.label[0].animate({ r: 5 }, 500, "bounce");
19789                     this.label[1].attr({ "font-weight": 400 });
19790                 }
19791             };
19792
19793         switch(graphtype){
19794             case 'bar':
19795                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19796                 break;
19797             case 'hbar':
19798                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19799                 break;
19800             case 'pie':
19801 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19802 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19803 //            
19804                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19805                 
19806                 break;
19807
19808         }
19809         
19810         if(this.title){
19811             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19812         }
19813         
19814     },
19815     
19816     setTitle: function(o)
19817     {
19818         this.title = o;
19819     },
19820     
19821     initEvents: function() {
19822         
19823         if(!this.href){
19824             this.el.on('click', this.onClick, this);
19825         }
19826     },
19827     
19828     onClick : function(e)
19829     {
19830         Roo.log('img onclick');
19831         this.fireEvent('click', this, e);
19832     }
19833    
19834 });
19835
19836  
19837 /*
19838  * - LGPL
19839  *
19840  * numberBox
19841  * 
19842  */
19843 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19844
19845 /**
19846  * @class Roo.bootstrap.dash.NumberBox
19847  * @extends Roo.bootstrap.Component
19848  * Bootstrap NumberBox class
19849  * @cfg {String} headline Box headline
19850  * @cfg {String} content Box content
19851  * @cfg {String} icon Box icon
19852  * @cfg {String} footer Footer text
19853  * @cfg {String} fhref Footer href
19854  * 
19855  * @constructor
19856  * Create a new NumberBox
19857  * @param {Object} config The config object
19858  */
19859
19860
19861 Roo.bootstrap.dash.NumberBox = function(config){
19862     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19863     
19864 };
19865
19866 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19867     
19868     headline : '',
19869     content : '',
19870     icon : '',
19871     footer : '',
19872     fhref : '',
19873     ficon : '',
19874     
19875     getAutoCreate : function(){
19876         
19877         var cfg = {
19878             tag : 'div',
19879             cls : 'small-box ',
19880             cn : [
19881                 {
19882                     tag : 'div',
19883                     cls : 'inner',
19884                     cn :[
19885                         {
19886                             tag : 'h3',
19887                             cls : 'roo-headline',
19888                             html : this.headline
19889                         },
19890                         {
19891                             tag : 'p',
19892                             cls : 'roo-content',
19893                             html : this.content
19894                         }
19895                     ]
19896                 }
19897             ]
19898         }
19899         
19900         if(this.icon){
19901             cfg.cn.push({
19902                 tag : 'div',
19903                 cls : 'icon',
19904                 cn :[
19905                     {
19906                         tag : 'i',
19907                         cls : 'ion ' + this.icon
19908                     }
19909                 ]
19910             });
19911         }
19912         
19913         if(this.footer){
19914             var footer = {
19915                 tag : 'a',
19916                 cls : 'small-box-footer',
19917                 href : this.fhref || '#',
19918                 html : this.footer
19919             };
19920             
19921             cfg.cn.push(footer);
19922             
19923         }
19924         
19925         return  cfg;
19926     },
19927
19928     onRender : function(ct,position){
19929         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19930
19931
19932        
19933                 
19934     },
19935
19936     setHeadline: function (value)
19937     {
19938         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19939     },
19940     
19941     setFooter: function (value, href)
19942     {
19943         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19944         
19945         if(href){
19946             this.el.select('a.small-box-footer',true).first().attr('href', href);
19947         }
19948         
19949     },
19950
19951     setContent: function (value)
19952     {
19953         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19954     },
19955
19956     initEvents: function() 
19957     {   
19958         
19959     }
19960     
19961 });
19962
19963  
19964 /*
19965  * - LGPL
19966  *
19967  * TabBox
19968  * 
19969  */
19970 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19971
19972 /**
19973  * @class Roo.bootstrap.dash.TabBox
19974  * @extends Roo.bootstrap.Component
19975  * Bootstrap TabBox class
19976  * @cfg {String} title Title of the TabBox
19977  * @cfg {String} icon Icon of the TabBox
19978  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19979  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19980  * 
19981  * @constructor
19982  * Create a new TabBox
19983  * @param {Object} config The config object
19984  */
19985
19986
19987 Roo.bootstrap.dash.TabBox = function(config){
19988     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19989     this.addEvents({
19990         // raw events
19991         /**
19992          * @event addpane
19993          * When a pane is added
19994          * @param {Roo.bootstrap.dash.TabPane} pane
19995          */
19996         "addpane" : true,
19997         /**
19998          * @event activatepane
19999          * When a pane is activated
20000          * @param {Roo.bootstrap.dash.TabPane} pane
20001          */
20002         "activatepane" : true
20003         
20004          
20005     });
20006     
20007     this.panes = [];
20008 };
20009
20010 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
20011
20012     title : '',
20013     icon : false,
20014     showtabs : true,
20015     tabScrollable : false,
20016     
20017     getChildContainer : function()
20018     {
20019         return this.el.select('.tab-content', true).first();
20020     },
20021     
20022     getAutoCreate : function(){
20023         
20024         var header = {
20025             tag: 'li',
20026             cls: 'pull-left header',
20027             html: this.title,
20028             cn : []
20029         };
20030         
20031         if(this.icon){
20032             header.cn.push({
20033                 tag: 'i',
20034                 cls: 'fa ' + this.icon
20035             });
20036         }
20037         
20038         var h = {
20039             tag: 'ul',
20040             cls: 'nav nav-tabs pull-right',
20041             cn: [
20042                 header
20043             ]
20044         };
20045         
20046         if(this.tabScrollable){
20047             h = {
20048                 tag: 'div',
20049                 cls: 'tab-header',
20050                 cn: [
20051                     {
20052                         tag: 'ul',
20053                         cls: 'nav nav-tabs pull-right',
20054                         cn: [
20055                             header
20056                         ]
20057                     }
20058                 ]
20059             }
20060         }
20061         
20062         var cfg = {
20063             tag: 'div',
20064             cls: 'nav-tabs-custom',
20065             cn: [
20066                 h,
20067                 {
20068                     tag: 'div',
20069                     cls: 'tab-content no-padding',
20070                     cn: []
20071                 }
20072             ]
20073         }
20074
20075         return  cfg;
20076     },
20077     initEvents : function()
20078     {
20079         //Roo.log('add add pane handler');
20080         this.on('addpane', this.onAddPane, this);
20081     },
20082      /**
20083      * Updates the box title
20084      * @param {String} html to set the title to.
20085      */
20086     setTitle : function(value)
20087     {
20088         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20089     },
20090     onAddPane : function(pane)
20091     {
20092         this.panes.push(pane);
20093         //Roo.log('addpane');
20094         //Roo.log(pane);
20095         // tabs are rendere left to right..
20096         if(!this.showtabs){
20097             return;
20098         }
20099         
20100         var ctr = this.el.select('.nav-tabs', true).first();
20101          
20102          
20103         var existing = ctr.select('.nav-tab',true);
20104         var qty = existing.getCount();;
20105         
20106         
20107         var tab = ctr.createChild({
20108             tag : 'li',
20109             cls : 'nav-tab' + (qty ? '' : ' active'),
20110             cn : [
20111                 {
20112                     tag : 'a',
20113                     href:'#',
20114                     html : pane.title
20115                 }
20116             ]
20117         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20118         pane.tab = tab;
20119         
20120         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20121         if (!qty) {
20122             pane.el.addClass('active');
20123         }
20124         
20125                 
20126     },
20127     onTabClick : function(ev,un,ob,pane)
20128     {
20129         //Roo.log('tab - prev default');
20130         ev.preventDefault();
20131         
20132         
20133         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20134         pane.tab.addClass('active');
20135         //Roo.log(pane.title);
20136         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20137         // technically we should have a deactivate event.. but maybe add later.
20138         // and it should not de-activate the selected tab...
20139         this.fireEvent('activatepane', pane);
20140         pane.el.addClass('active');
20141         pane.fireEvent('activate');
20142         
20143         
20144     },
20145     
20146     getActivePane : function()
20147     {
20148         var r = false;
20149         Roo.each(this.panes, function(p) {
20150             if(p.el.hasClass('active')){
20151                 r = p;
20152                 return false;
20153             }
20154             
20155             return;
20156         });
20157         
20158         return r;
20159     }
20160     
20161     
20162 });
20163
20164  
20165 /*
20166  * - LGPL
20167  *
20168  * Tab pane
20169  * 
20170  */
20171 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20172 /**
20173  * @class Roo.bootstrap.TabPane
20174  * @extends Roo.bootstrap.Component
20175  * Bootstrap TabPane class
20176  * @cfg {Boolean} active (false | true) Default false
20177  * @cfg {String} title title of panel
20178
20179  * 
20180  * @constructor
20181  * Create a new TabPane
20182  * @param {Object} config The config object
20183  */
20184
20185 Roo.bootstrap.dash.TabPane = function(config){
20186     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20187     
20188     this.addEvents({
20189         // raw events
20190         /**
20191          * @event activate
20192          * When a pane is activated
20193          * @param {Roo.bootstrap.dash.TabPane} pane
20194          */
20195         "activate" : true
20196          
20197     });
20198 };
20199
20200 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20201     
20202     active : false,
20203     title : '',
20204     
20205     // the tabBox that this is attached to.
20206     tab : false,
20207      
20208     getAutoCreate : function() 
20209     {
20210         var cfg = {
20211             tag: 'div',
20212             cls: 'tab-pane'
20213         }
20214         
20215         if(this.active){
20216             cfg.cls += ' active';
20217         }
20218         
20219         return cfg;
20220     },
20221     initEvents  : function()
20222     {
20223         //Roo.log('trigger add pane handler');
20224         this.parent().fireEvent('addpane', this)
20225     },
20226     
20227      /**
20228      * Updates the tab title 
20229      * @param {String} html to set the title to.
20230      */
20231     setTitle: function(str)
20232     {
20233         if (!this.tab) {
20234             return;
20235         }
20236         this.title = str;
20237         this.tab.select('a', true).first().dom.innerHTML = str;
20238         
20239     }
20240     
20241     
20242     
20243 });
20244
20245  
20246
20247
20248  /*
20249  * - LGPL
20250  *
20251  * menu
20252  * 
20253  */
20254 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20255
20256 /**
20257  * @class Roo.bootstrap.menu.Menu
20258  * @extends Roo.bootstrap.Component
20259  * Bootstrap Menu class - container for Menu
20260  * @cfg {String} html Text of the menu
20261  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20262  * @cfg {String} icon Font awesome icon
20263  * @cfg {String} pos Menu align to (top | bottom) default bottom
20264  * 
20265  * 
20266  * @constructor
20267  * Create a new Menu
20268  * @param {Object} config The config object
20269  */
20270
20271
20272 Roo.bootstrap.menu.Menu = function(config){
20273     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20274     
20275     this.addEvents({
20276         /**
20277          * @event beforeshow
20278          * Fires before this menu is displayed
20279          * @param {Roo.bootstrap.menu.Menu} this
20280          */
20281         beforeshow : true,
20282         /**
20283          * @event beforehide
20284          * Fires before this menu is hidden
20285          * @param {Roo.bootstrap.menu.Menu} this
20286          */
20287         beforehide : true,
20288         /**
20289          * @event show
20290          * Fires after this menu is displayed
20291          * @param {Roo.bootstrap.menu.Menu} this
20292          */
20293         show : true,
20294         /**
20295          * @event hide
20296          * Fires after this menu is hidden
20297          * @param {Roo.bootstrap.menu.Menu} this
20298          */
20299         hide : true,
20300         /**
20301          * @event click
20302          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20303          * @param {Roo.bootstrap.menu.Menu} this
20304          * @param {Roo.EventObject} e
20305          */
20306         click : true
20307     });
20308     
20309 };
20310
20311 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20312     
20313     submenu : false,
20314     html : '',
20315     weight : 'default',
20316     icon : false,
20317     pos : 'bottom',
20318     
20319     
20320     getChildContainer : function() {
20321         if(this.isSubMenu){
20322             return this.el;
20323         }
20324         
20325         return this.el.select('ul.dropdown-menu', true).first();  
20326     },
20327     
20328     getAutoCreate : function()
20329     {
20330         var text = [
20331             {
20332                 tag : 'span',
20333                 cls : 'roo-menu-text',
20334                 html : this.html
20335             }
20336         ];
20337         
20338         if(this.icon){
20339             text.unshift({
20340                 tag : 'i',
20341                 cls : 'fa ' + this.icon
20342             })
20343         }
20344         
20345         
20346         var cfg = {
20347             tag : 'div',
20348             cls : 'btn-group',
20349             cn : [
20350                 {
20351                     tag : 'button',
20352                     cls : 'dropdown-button btn btn-' + this.weight,
20353                     cn : text
20354                 },
20355                 {
20356                     tag : 'button',
20357                     cls : 'dropdown-toggle btn btn-' + this.weight,
20358                     cn : [
20359                         {
20360                             tag : 'span',
20361                             cls : 'caret'
20362                         }
20363                     ]
20364                 },
20365                 {
20366                     tag : 'ul',
20367                     cls : 'dropdown-menu'
20368                 }
20369             ]
20370             
20371         };
20372         
20373         if(this.pos == 'top'){
20374             cfg.cls += ' dropup';
20375         }
20376         
20377         if(this.isSubMenu){
20378             cfg = {
20379                 tag : 'ul',
20380                 cls : 'dropdown-menu'
20381             }
20382         }
20383         
20384         return cfg;
20385     },
20386     
20387     onRender : function(ct, position)
20388     {
20389         this.isSubMenu = ct.hasClass('dropdown-submenu');
20390         
20391         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20392     },
20393     
20394     initEvents : function() 
20395     {
20396         if(this.isSubMenu){
20397             return;
20398         }
20399         
20400         this.hidden = true;
20401         
20402         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20403         this.triggerEl.on('click', this.onTriggerPress, this);
20404         
20405         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20406         this.buttonEl.on('click', this.onClick, this);
20407         
20408     },
20409     
20410     list : function()
20411     {
20412         if(this.isSubMenu){
20413             return this.el;
20414         }
20415         
20416         return this.el.select('ul.dropdown-menu', true).first();
20417     },
20418     
20419     onClick : function(e)
20420     {
20421         this.fireEvent("click", this, e);
20422     },
20423     
20424     onTriggerPress  : function(e)
20425     {   
20426         if (this.isVisible()) {
20427             this.hide();
20428         } else {
20429             this.show();
20430         }
20431     },
20432     
20433     isVisible : function(){
20434         return !this.hidden;
20435     },
20436     
20437     show : function()
20438     {
20439         this.fireEvent("beforeshow", this);
20440         
20441         this.hidden = false;
20442         this.el.addClass('open');
20443         
20444         Roo.get(document).on("mouseup", this.onMouseUp, this);
20445         
20446         this.fireEvent("show", this);
20447         
20448         
20449     },
20450     
20451     hide : function()
20452     {
20453         this.fireEvent("beforehide", this);
20454         
20455         this.hidden = true;
20456         this.el.removeClass('open');
20457         
20458         Roo.get(document).un("mouseup", this.onMouseUp);
20459         
20460         this.fireEvent("hide", this);
20461     },
20462     
20463     onMouseUp : function()
20464     {
20465         this.hide();
20466     }
20467     
20468 });
20469
20470  
20471  /*
20472  * - LGPL
20473  *
20474  * menu item
20475  * 
20476  */
20477 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20478
20479 /**
20480  * @class Roo.bootstrap.menu.Item
20481  * @extends Roo.bootstrap.Component
20482  * Bootstrap MenuItem class
20483  * @cfg {Boolean} submenu (true | false) default false
20484  * @cfg {String} html text of the item
20485  * @cfg {String} href the link
20486  * @cfg {Boolean} disable (true | false) default false
20487  * @cfg {Boolean} preventDefault (true | false) default true
20488  * @cfg {String} icon Font awesome icon
20489  * @cfg {String} pos Submenu align to (left | right) default right 
20490  * 
20491  * 
20492  * @constructor
20493  * Create a new Item
20494  * @param {Object} config The config object
20495  */
20496
20497
20498 Roo.bootstrap.menu.Item = function(config){
20499     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20500     this.addEvents({
20501         /**
20502          * @event mouseover
20503          * Fires when the mouse is hovering over this menu
20504          * @param {Roo.bootstrap.menu.Item} this
20505          * @param {Roo.EventObject} e
20506          */
20507         mouseover : true,
20508         /**
20509          * @event mouseout
20510          * Fires when the mouse exits this menu
20511          * @param {Roo.bootstrap.menu.Item} this
20512          * @param {Roo.EventObject} e
20513          */
20514         mouseout : true,
20515         // raw events
20516         /**
20517          * @event click
20518          * The raw click event for the entire grid.
20519          * @param {Roo.EventObject} e
20520          */
20521         click : true
20522     });
20523 };
20524
20525 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20526     
20527     submenu : false,
20528     href : '',
20529     html : '',
20530     preventDefault: true,
20531     disable : false,
20532     icon : false,
20533     pos : 'right',
20534     
20535     getAutoCreate : function()
20536     {
20537         var text = [
20538             {
20539                 tag : 'span',
20540                 cls : 'roo-menu-item-text',
20541                 html : this.html
20542             }
20543         ];
20544         
20545         if(this.icon){
20546             text.unshift({
20547                 tag : 'i',
20548                 cls : 'fa ' + this.icon
20549             })
20550         }
20551         
20552         var cfg = {
20553             tag : 'li',
20554             cn : [
20555                 {
20556                     tag : 'a',
20557                     href : this.href || '#',
20558                     cn : text
20559                 }
20560             ]
20561         };
20562         
20563         if(this.disable){
20564             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20565         }
20566         
20567         if(this.submenu){
20568             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20569             
20570             if(this.pos == 'left'){
20571                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20572             }
20573         }
20574         
20575         return cfg;
20576     },
20577     
20578     initEvents : function() 
20579     {
20580         this.el.on('mouseover', this.onMouseOver, this);
20581         this.el.on('mouseout', this.onMouseOut, this);
20582         
20583         this.el.select('a', true).first().on('click', this.onClick, this);
20584         
20585     },
20586     
20587     onClick : function(e)
20588     {
20589         if(this.preventDefault){
20590             e.preventDefault();
20591         }
20592         
20593         this.fireEvent("click", this, e);
20594     },
20595     
20596     onMouseOver : function(e)
20597     {
20598         if(this.submenu && this.pos == 'left'){
20599             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20600         }
20601         
20602         this.fireEvent("mouseover", this, e);
20603     },
20604     
20605     onMouseOut : function(e)
20606     {
20607         this.fireEvent("mouseout", this, e);
20608     }
20609 });
20610
20611  
20612
20613  /*
20614  * - LGPL
20615  *
20616  * menu separator
20617  * 
20618  */
20619 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20620
20621 /**
20622  * @class Roo.bootstrap.menu.Separator
20623  * @extends Roo.bootstrap.Component
20624  * Bootstrap Separator class
20625  * 
20626  * @constructor
20627  * Create a new Separator
20628  * @param {Object} config The config object
20629  */
20630
20631
20632 Roo.bootstrap.menu.Separator = function(config){
20633     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20634 };
20635
20636 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20637     
20638     getAutoCreate : function(){
20639         var cfg = {
20640             tag : 'li',
20641             cls: 'divider'
20642         };
20643         
20644         return cfg;
20645     }
20646    
20647 });
20648
20649  
20650
20651  /*
20652  * - LGPL
20653  *
20654  * Tooltip
20655  * 
20656  */
20657
20658 /**
20659  * @class Roo.bootstrap.Tooltip
20660  * Bootstrap Tooltip class
20661  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20662  * to determine which dom element triggers the tooltip.
20663  * 
20664  * It needs to add support for additional attributes like tooltip-position
20665  * 
20666  * @constructor
20667  * Create a new Toolti
20668  * @param {Object} config The config object
20669  */
20670
20671 Roo.bootstrap.Tooltip = function(config){
20672     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20673 };
20674
20675 Roo.apply(Roo.bootstrap.Tooltip, {
20676     /**
20677      * @function init initialize tooltip monitoring.
20678      * @static
20679      */
20680     currentEl : false,
20681     currentTip : false,
20682     currentRegion : false,
20683     
20684     //  init : delay?
20685     
20686     init : function()
20687     {
20688         Roo.get(document).on('mouseover', this.enter ,this);
20689         Roo.get(document).on('mouseout', this.leave, this);
20690          
20691         
20692         this.currentTip = new Roo.bootstrap.Tooltip();
20693     },
20694     
20695     enter : function(ev)
20696     {
20697         var dom = ev.getTarget();
20698         //Roo.log(['enter',dom]);
20699         var el = Roo.fly(dom);
20700         if (this.currentEl) {
20701             //Roo.log(dom);
20702             //Roo.log(this.currentEl);
20703             //Roo.log(this.currentEl.contains(dom));
20704             if (this.currentEl == el) {
20705                 return;
20706             }
20707             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20708                 return;
20709             }
20710
20711         }
20712         
20713         
20714         
20715         if (this.currentTip.el) {
20716             this.currentTip.el.hide(); // force hiding...
20717         }    
20718         //Roo.log(el);
20719         if (!el.attr('tooltip')) { // parents who have tip?
20720             return;
20721         }
20722         this.currentEl = el;
20723         this.currentTip.bind(el);
20724         this.currentRegion = Roo.lib.Region.getRegion(dom);
20725         this.currentTip.enter();
20726         
20727     },
20728     leave : function(ev)
20729     {
20730         var dom = ev.getTarget();
20731         //Roo.log(['leave',dom]);
20732         if (!this.currentEl) {
20733             return;
20734         }
20735         
20736         
20737         if (dom != this.currentEl.dom) {
20738             return;
20739         }
20740         var xy = ev.getXY();
20741         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
20742             return;
20743         }
20744         // only activate leave if mouse cursor is outside... bounding box..
20745         
20746         
20747         
20748         
20749         if (this.currentTip) {
20750             this.currentTip.leave();
20751         }
20752         //Roo.log('clear currentEl');
20753         this.currentEl = false;
20754         
20755         
20756     },
20757     alignment : {
20758         'left' : ['r-l', [-2,0], 'right'],
20759         'right' : ['l-r', [2,0], 'left'],
20760         'bottom' : ['t-b', [0,2], 'top'],
20761         'top' : [ 'b-t', [0,-2], 'bottom']
20762     }
20763     
20764 });
20765
20766
20767 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
20768     
20769     
20770     bindEl : false,
20771     
20772     delay : null, // can be { show : 300 , hide: 500}
20773     
20774     timeout : null,
20775     
20776     hoverState : null, //???
20777     
20778     placement : 'bottom', 
20779     
20780     getAutoCreate : function(){
20781     
20782         var cfg = {
20783            cls : 'tooltip',
20784            role : 'tooltip',
20785            cn : [
20786                 {
20787                     cls : 'tooltip-arrow'
20788                 },
20789                 {
20790                     cls : 'tooltip-inner'
20791                 }
20792            ]
20793         };
20794         
20795         return cfg;
20796     },
20797     bind : function(el)
20798     {
20799         this.bindEl = el;
20800     },
20801       
20802     
20803     enter : function () {
20804        
20805         if (this.timeout != null) {
20806             clearTimeout(this.timeout);
20807         }
20808         
20809         this.hoverState = 'in'
20810          //Roo.log("enter - show");
20811         if (!this.delay || !this.delay.show) {
20812             this.show();
20813             return;
20814         }
20815         var _t = this;
20816         this.timeout = setTimeout(function () {
20817             if (_t.hoverState == 'in') {
20818                 _t.show();
20819             }
20820         }, this.delay.show);
20821     },
20822     leave : function()
20823     {
20824         clearTimeout(this.timeout);
20825     
20826         this.hoverState = 'out'
20827          if (!this.delay || !this.delay.hide) {
20828             this.hide();
20829             return 
20830         }
20831        
20832         var _t = this;
20833         this.timeout = setTimeout(function () {
20834             //Roo.log("leave - timeout");
20835             
20836             if (_t.hoverState == 'out') {
20837                 _t.hide();
20838                 Roo.bootstrap.Tooltip.currentEl = false;
20839             }
20840         }, delay)
20841     },
20842     
20843     show : function ()
20844     {
20845         if (!this.el) {
20846             this.render(document.body);
20847         }
20848         // set content.
20849         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
20850         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
20851         
20852         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
20853         
20854         var placement = typeof this.placement == 'function' ?
20855             this.placement.call(this, this.el, on_el) :
20856             this.placement;
20857             
20858         var autoToken = /\s?auto?\s?/i;
20859         var autoPlace = autoToken.test(placement);
20860         if (autoPlace) {
20861             placement = placement.replace(autoToken, '') || 'top';
20862         }
20863         
20864         //this.el.detach()
20865         //this.el.setXY([0,0]);
20866         this.el.show();
20867         //this.el.dom.style.display='block';
20868         this.el.addClass(placement);
20869         
20870         //this.el.appendTo(on_el);
20871         
20872         var p = this.getPosition();
20873         var box = this.el.getBox();
20874         
20875         if (autoPlace) {
20876             // fixme..
20877         }
20878         var align = Roo.bootstrap.Tooltip.alignment[placement]
20879         this.el.alignTo(this.bindEl, align[0],align[1]);
20880         //var arrow = this.el.select('.arrow',true).first();
20881         //arrow.set(align[2], 
20882         
20883         this.el.addClass('in fade');
20884         this.hoverState = null;
20885         
20886         if (this.el.hasClass('fade')) {
20887             // fade it?
20888         }
20889         
20890     },
20891     hide : function()
20892     {
20893          
20894         if (!this.el) {
20895             return;
20896         }
20897         //this.el.setXY([0,0]);
20898         this.el.removeClass('in');
20899         //this.el.hide();
20900         
20901     }
20902     
20903 });
20904  
20905
20906  /*
20907  * - LGPL
20908  *
20909  * Location Picker
20910  * 
20911  */
20912
20913 /**
20914  * @class Roo.bootstrap.LocationPicker
20915  * @extends Roo.bootstrap.Component
20916  * Bootstrap LocationPicker class
20917  * @cfg {Number} latitude Position when init default 0
20918  * @cfg {Number} longitude Position when init default 0
20919  * @cfg {Number} zoom default 15
20920  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
20921  * @cfg {Boolean} mapTypeControl default false
20922  * @cfg {Boolean} disableDoubleClickZoom default false
20923  * @cfg {Boolean} scrollwheel default true
20924  * @cfg {Boolean} streetViewControl default false
20925  * @cfg {Number} radius default 0
20926  * @cfg {String} locationName
20927  * @cfg {Boolean} draggable default true
20928  * @cfg {Boolean} enableAutocomplete default false
20929  * @cfg {Boolean} enableReverseGeocode default true
20930  * @cfg {String} markerTitle
20931  * 
20932  * @constructor
20933  * Create a new LocationPicker
20934  * @param {Object} config The config object
20935  */
20936
20937 Roo.bootstrap.LocationPicker = function(config){
20938     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
20939     
20940      this.addEvents({
20941             /**
20942              * @event positionchanged
20943              * Fires when the picker initialized.
20944              * @param {Roo.bootstrap.LocationPicker} this
20945              * @param {Google Location} location
20946              */
20947             positionchanged : true
20948         });
20949         
20950 };
20951
20952 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
20953     
20954     gMapContext: false,
20955     
20956     latitude: 0,
20957     longitude: 0,
20958     zoom: 15,
20959     mapTypeId: google.maps.MapTypeId.ROADMAP,
20960     mapTypeControl: false,
20961     disableDoubleClickZoom: false,
20962     scrollwheel: true,
20963     streetViewControl: false,
20964     radius: 0,
20965     locationName: '',
20966     draggable: true,
20967     enableAutocomplete: false,
20968     enableReverseGeocode: true,
20969     markerTitle: '',
20970     
20971     getAutoCreate: function()
20972     {
20973         var cfg = {
20974             tag: 'div',
20975             cls: 'roo-location-picker'
20976         };
20977         
20978         return cfg
20979     },
20980     
20981     initEvents: function(ct, position)
20982     {   
20983         
20984     },
20985     
20986     initial: function()
20987     {
20988         if (this.isApplied()){
20989             return;
20990         }
20991         
20992         this.gMapContext = this.GMapContext();
20993         
20994         var _this = this;
20995         
20996         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
20997             _this.setPosition(_this.gMapContext.marker.position);
20998         });
20999         
21000         this.setPosition(this.gMapContext.location);
21001     },
21002     
21003     isApplied: function() 
21004     {
21005         return this.getGmapContext() == false ? false : true;
21006     },
21007     
21008     getGmapContext: function() 
21009     {
21010         return this.gMapContext
21011     },
21012     
21013     GMapContext: function() 
21014     {
21015         var _map = new google.maps.Map(this.el.dom, this);
21016         var _marker = new google.maps.Marker({
21017             position: new google.maps.LatLng(this.latitude, this.longitude),
21018             map: _map,
21019             title: this.markerTitle,
21020             draggable: this.draggable
21021         });
21022         
21023         return {
21024             map: _map,
21025             marker: _marker,
21026             circle: null,
21027             location: _marker.position,
21028             radius: this.radius,
21029             locationName: this.locationName,
21030             addressComponents: {
21031                 formatted_address: null,
21032                 addressLine1: null,
21033                 addressLine2: null,
21034                 streetName: null,
21035                 streetNumber: null,
21036                 city: null,
21037                 district: null,
21038                 state: null,
21039                 stateOrProvince: null
21040             },
21041             settings: this,
21042             domContainer: this.el.dom,
21043             geodecoder: new google.maps.Geocoder()
21044         };
21045     },
21046     
21047     drawCircle: function(center, radius, options) 
21048     {
21049         if (this.gMapContext.circle != null) {
21050             this.gMapContext.circle.setMap(null);
21051         }
21052         if (radius > 0) {
21053             radius *= 1;
21054             options = Roo.apply({}, options, {
21055                 strokeColor: "#0000FF",
21056                 strokeOpacity: .35,
21057                 strokeWeight: 2,
21058                 fillColor: "#0000FF",
21059                 fillOpacity: .2
21060             });
21061             
21062             options.map = this.gMapContext.map;
21063             options.radius = radius;
21064             options.center = center;
21065             this.gMapContext.circle = new google.maps.Circle(options);
21066             return this.gMapContext.circle;
21067         }
21068         
21069         return null;
21070     },
21071     
21072     setPosition: function(location) 
21073     {
21074         this.gMapContext.location = location;
21075         this.gMapContext.marker.setPosition(location);
21076         this.gMapContext.map.panTo(location);
21077         this.drawCircle(location, this.gMapContext.radius, {});
21078         
21079         var _this = this;
21080         
21081         if (this.gMapContext.settings.enableReverseGeocode) {
21082             this.gMapContext.geodecoder.geocode({
21083                 latLng: this.gMapContext.location
21084             }, function(results, status) {
21085                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
21086                     _this.gMapContext.locationName = results[0].formatted_address;
21087                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
21088                 }
21089             });
21090         }
21091         
21092         this.fireEvent('positionchanged', this, location);
21093     },
21094     
21095     setPositionByLatLng: function(latitude, longitude)
21096     {
21097         this.setPosition(new google.maps.LatLng(latitude, longitude));
21098     },
21099     
21100     getCurrentPosition: function() 
21101     {
21102         return {
21103             latitude: this.gMapContext.location.lat(),
21104             longitude: this.gMapContext.location.lng()
21105         };
21106     },
21107     
21108     getAddressName: function() 
21109     {
21110         return this.gMapContext.locationName;
21111     },
21112     
21113     getAddressComponents: function() 
21114     {
21115         return this.gMapContext.addressComponents;
21116     },
21117     
21118     address_component_from_google_geocode: function(address_components) 
21119     {
21120         var result = {};
21121         for (var i = address_components.length - 1; i >= 0; i--) {
21122             var component = address_components[i];
21123             if (component.types.indexOf("postal_code") >= 0) {
21124                 result.postalCode = component.short_name;
21125             } else if (component.types.indexOf("street_number") >= 0) {
21126                 result.streetNumber = component.short_name;
21127             } else if (component.types.indexOf("route") >= 0) {
21128                 result.streetName = component.short_name;
21129             } else if (component.types.indexOf("locality") >= 0) {
21130                 result.city = component.short_name;
21131             } else if (component.types.indexOf("neighborhood") >= 0) {
21132                 result.city = component.short_name;
21133             } else if (component.types.indexOf("sublocality") >= 0) {
21134                 result.district = component.short_name;
21135             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
21136                 result.stateOrProvince = component.short_name;
21137             } else if (component.types.indexOf("country") >= 0) {
21138                 result.country = component.short_name;
21139             }
21140         }
21141         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
21142         result.addressLine2 = "";
21143         return result;
21144     }
21145     
21146 });