roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @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)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {String} rheader contet on the right of header
993
994  *     
995  * @constructor
996  * Create a new Container
997  * @param {Object} config The config object
998  */
999
1000 Roo.bootstrap.Container = function(config){
1001     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1002     
1003     this.addEvents({
1004         // raw events
1005          /**
1006          * @event expand
1007          * After the panel has been expand
1008          * 
1009          * @param {Roo.bootstrap.Container} this
1010          */
1011         "expand" : true,
1012         /**
1013          * @event collapse
1014          * After the panel has been collapsed
1015          * 
1016          * @param {Roo.bootstrap.Container} this
1017          */
1018         "collapse" : true
1019     });
1020 };
1021
1022 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1023     
1024     jumbotron : false,
1025     well: '',
1026     panel : '',
1027     header: '',
1028     footer : '',
1029     sticky: '',
1030     tag : false,
1031     alert : false,
1032     fa: false,
1033     icon : false,
1034     expandable : false,
1035     rheader : '',
1036     expanded : true,
1037   
1038      
1039     getChildContainer : function() {
1040         
1041         if(!this.el){
1042             return false;
1043         }
1044         
1045         if (this.panel.length) {
1046             return this.el.select('.panel-body',true).first();
1047         }
1048         
1049         return this.el;
1050     },
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag : this.tag || 'div',
1057             html : '',
1058             cls : ''
1059         };
1060         if (this.jumbotron) {
1061             cfg.cls = 'jumbotron';
1062         }
1063         
1064         
1065         
1066         // - this is applied by the parent..
1067         //if (this.cls) {
1068         //    cfg.cls = this.cls + '';
1069         //}
1070         
1071         if (this.sticky.length) {
1072             
1073             var bd = Roo.get(document.body);
1074             if (!bd.hasClass('bootstrap-sticky')) {
1075                 bd.addClass('bootstrap-sticky');
1076                 Roo.select('html',true).setStyle('height', '100%');
1077             }
1078              
1079             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1080         }
1081         
1082         
1083         if (this.well.length) {
1084             switch (this.well) {
1085                 case 'lg':
1086                 case 'sm':
1087                     cfg.cls +=' well well-' +this.well;
1088                     break;
1089                 default:
1090                     cfg.cls +=' well';
1091                     break;
1092             }
1093         }
1094         
1095         if (this.hidden) {
1096             cfg.cls += ' hidden';
1097         }
1098         
1099         
1100         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1101             cfg.cls +=' alert alert-' + this.alert;
1102         }
1103         
1104         var body = cfg;
1105         
1106         if (this.panel.length) {
1107             cfg.cls += ' panel panel-' + this.panel;
1108             cfg.cn = [];
1109             if (this.header.length) {
1110                 
1111                 var h = [];
1112                 
1113                 if(this.expandable){
1114                     
1115                     cfg.cls = cfg.cls + ' expandable';
1116                     
1117                     h.push({
1118                         tag: 'i',
1119                         cls: 'fa fa-minus'
1120                     });
1121                 }
1122                 
1123                 h.push(
1124                     {
1125                         tag: 'span',
1126                         cls : 'panel-title',
1127                         html : this.header
1128                     },
1129                     {
1130                         tag: 'span',
1131                         cls: 'panel-header-right',
1132                         html: this.rheader
1133                     }
1134                 );
1135                 
1136                 cfg.cn.push({
1137                     cls : 'panel-heading',
1138                     cn : h
1139                 });
1140                 
1141             }
1142             
1143             body = false;
1144             cfg.cn.push({
1145                 cls : 'panel-body',
1146                 html : this.html
1147             });
1148             
1149             
1150             if (this.footer.length) {
1151                 cfg.cn.push({
1152                     cls : 'panel-footer',
1153                     html : this.footer
1154                     
1155                 });
1156             }
1157             
1158         }
1159         
1160         if (body) {
1161             body.html = this.html || cfg.html;
1162             // prefix with the icons..
1163             if (this.fa) {
1164                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1165             }
1166             if (this.icon) {
1167                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1168             }
1169             
1170             
1171         }
1172         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1173             cfg.cls =  'container';
1174         }
1175         
1176         return cfg;
1177     },
1178     
1179     initEvents: function() 
1180     {
1181         if(!this.expandable){
1182             return;
1183         }
1184         
1185         var headerEl = this.headerEl();
1186         
1187         if(!headerEl){
1188             return;
1189         }
1190         
1191         headerEl.on('click', this.onToggleClick, this);
1192         
1193     },
1194     
1195     onToggleClick : function()
1196     {
1197         var headerEl = this.headerEl();
1198         
1199         if(!headerEl){
1200             return;
1201         }
1202         
1203         if(this.expanded){
1204             this.collapse();
1205             return;
1206         }
1207         
1208         this.expand();
1209     },
1210     
1211     expand : function()
1212     {
1213         if(this.fireEvent('expand', this)) {
1214             
1215             this.expanded = true;
1216             
1217             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1218         
1219             var toggleEl = this.toggleEl();
1220
1221             if(!toggleEl){
1222                 return;
1223             }
1224
1225             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1226         }
1227         
1228     },
1229     
1230     collapse : function()
1231     {
1232         if(this.fireEvent('collapse', this)) {
1233             
1234             this.expanded = false;
1235             
1236             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1237         
1238             var toggleEl = this.toggleEl();
1239
1240             if(!toggleEl){
1241                 return;
1242             }
1243
1244             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1245         }
1246     },
1247     
1248     toggleEl : function()
1249     {
1250         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1251             return;
1252         }
1253         
1254         return this.el.select('.panel-heading .fa',true).first();
1255     },
1256     
1257     headerEl : function()
1258     {
1259         if(!this.el || !this.panel.length || !this.header.length){
1260             return;
1261         }
1262         
1263         return this.el.select('.panel-heading',true).first()
1264     },
1265     
1266     titleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-title',true).first();
1273     },
1274     
1275     setTitle : function(v)
1276     {
1277         var titleEl = this.titleEl();
1278         
1279         if(!titleEl){
1280             return;
1281         }
1282         
1283         titleEl.dom.innerHTML = v;
1284     },
1285     
1286     getTitle : function()
1287     {
1288         
1289         var titleEl = this.titleEl();
1290         
1291         if(!titleEl){
1292             return '';
1293         }
1294         
1295         return titleEl.dom.innerHTML;
1296     },
1297     
1298     setRightTitle : function(v)
1299     {
1300         var t = this.el.select('.panel-header-right',true).first();
1301         
1302         if(!t){
1303             return;
1304         }
1305         
1306         t.dom.innerHTML = v;
1307     }
1308    
1309 });
1310
1311  /*
1312  * - LGPL
1313  *
1314  * image
1315  * 
1316  */
1317
1318
1319 /**
1320  * @class Roo.bootstrap.Img
1321  * @extends Roo.bootstrap.Component
1322  * Bootstrap Img class
1323  * @cfg {Boolean} imgResponsive false | true
1324  * @cfg {String} border rounded | circle | thumbnail
1325  * @cfg {String} src image source
1326  * @cfg {String} alt image alternative text
1327  * @cfg {String} href a tag href
1328  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1329  * @cfg {String} xsUrl xs image source
1330  * @cfg {String} smUrl sm image source
1331  * @cfg {String} mdUrl md image source
1332  * @cfg {String} lgUrl lg image source
1333  * 
1334  * @constructor
1335  * Create a new Input
1336  * @param {Object} config The config object
1337  */
1338
1339 Roo.bootstrap.Img = function(config){
1340     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1341     
1342     this.addEvents({
1343         // img events
1344         /**
1345          * @event click
1346          * The img click event for the img.
1347          * @param {Roo.EventObject} e
1348          */
1349         "click" : true
1350     });
1351 };
1352
1353 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1354     
1355     imgResponsive: true,
1356     border: '',
1357     src: '',
1358     href: false,
1359     target: false,
1360     xsUrl: '',
1361     smUrl: '',
1362     mdUrl: '',
1363     lgUrl: '',
1364
1365     getAutoCreate : function()
1366     {   
1367         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1368             return this.createSingleImg();
1369         }
1370         
1371         var cfg = {
1372             tag: 'div',
1373             cls: 'roo-image-responsive-group',
1374             cn: []
1375         }
1376         var _this = this;
1377         
1378         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1379             
1380             if(!_this[size + 'Url']){
1381                 return;
1382             }
1383             
1384             var img = {
1385                 tag: 'img',
1386                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1387                 html: _this.html || cfg.html,
1388                 src: _this[size + 'Url']
1389             }
1390             
1391             img.cls += ' roo-image-responsive-' + size;
1392             
1393             var s = ['xs', 'sm', 'md', 'lg'];
1394             
1395             s.splice(s.indexOf(size), 1);
1396             
1397             Roo.each(s, function(ss){
1398                 img.cls += ' hidden-' + ss;
1399             });
1400             
1401             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1402                 cfg.cls += ' img-' + _this.border;
1403             }
1404             
1405             if(_this.alt){
1406                 cfg.alt = _this.alt;
1407             }
1408             
1409             if(_this.href){
1410                 var a = {
1411                     tag: 'a',
1412                     href: _this.href,
1413                     cn: [
1414                         img
1415                     ]
1416                 }
1417
1418                 if(this.target){
1419                     a.target = _this.target;
1420                 }
1421             }
1422             
1423             cfg.cn.push((_this.href) ? a : img);
1424             
1425         });
1426         
1427         return cfg;
1428     },
1429     
1430     createSingleImg : function()
1431     {
1432         var cfg = {
1433             tag: 'img',
1434             cls: (this.imgResponsive) ? 'img-responsive' : '',
1435             html : null
1436         }
1437         
1438         cfg.html = this.html || cfg.html;
1439         
1440         cfg.src = this.src || cfg.src;
1441         
1442         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1443             cfg.cls += ' img-' + this.border;
1444         }
1445         
1446         if(this.alt){
1447             cfg.alt = this.alt;
1448         }
1449         
1450         if(this.href){
1451             var a = {
1452                 tag: 'a',
1453                 href: this.href,
1454                 cn: [
1455                     cfg
1456                 ]
1457             }
1458             
1459             if(this.target){
1460                 a.target = this.target;
1461             }
1462             
1463         }
1464         
1465         return (this.href) ? a : cfg;
1466     },
1467     
1468     initEvents: function() 
1469     {
1470         if(!this.href){
1471             this.el.on('click', this.onClick, this);
1472         }
1473         
1474     },
1475     
1476     onClick : function(e)
1477     {
1478         Roo.log('img onclick');
1479         this.fireEvent('click', this, e);
1480     }
1481    
1482 });
1483
1484  /*
1485  * - LGPL
1486  *
1487  * image
1488  * 
1489  */
1490
1491
1492 /**
1493  * @class Roo.bootstrap.Link
1494  * @extends Roo.bootstrap.Component
1495  * Bootstrap Link Class
1496  * @cfg {String} alt image alternative text
1497  * @cfg {String} href a tag href
1498  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1499  * @cfg {String} html the content of the link.
1500  * @cfg {String} anchor name for the anchor link
1501
1502  * @cfg {Boolean} preventDefault (true | false) default false
1503
1504  * 
1505  * @constructor
1506  * Create a new Input
1507  * @param {Object} config The config object
1508  */
1509
1510 Roo.bootstrap.Link = function(config){
1511     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1512     
1513     this.addEvents({
1514         // img events
1515         /**
1516          * @event click
1517          * The img click event for the img.
1518          * @param {Roo.EventObject} e
1519          */
1520         "click" : true
1521     });
1522 };
1523
1524 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1525     
1526     href: false,
1527     target: false,
1528     preventDefault: false,
1529     anchor : false,
1530     alt : false,
1531
1532     getAutoCreate : function()
1533     {
1534         
1535         var cfg = {
1536             tag: 'a'
1537         };
1538         // anchor's do not require html/href...
1539         if (this.anchor === false) {
1540             cfg.html = this.html || '';
1541             cfg.href = this.href || '#';
1542         } else {
1543             cfg.name = this.anchor;
1544             if (this.html !== false) {
1545                 cfg.html = this.html;
1546             }
1547             if (this.href !== false) {
1548                 cfg.href = this.href;
1549             }
1550         }
1551         
1552         if(this.alt !== false){
1553             cfg.alt = this.alt;
1554         }
1555         
1556         
1557         if(this.target !== false) {
1558             cfg.target = this.target;
1559         }
1560         
1561         return cfg;
1562     },
1563     
1564     initEvents: function() {
1565         
1566         if(!this.href || this.preventDefault){
1567             this.el.on('click', this.onClick, this);
1568         }
1569     },
1570     
1571     onClick : function(e)
1572     {
1573         if(this.preventDefault){
1574             e.preventDefault();
1575         }
1576         //Roo.log('img onclick');
1577         this.fireEvent('click', this, e);
1578     }
1579    
1580 });
1581
1582  /*
1583  * - LGPL
1584  *
1585  * header
1586  * 
1587  */
1588
1589 /**
1590  * @class Roo.bootstrap.Header
1591  * @extends Roo.bootstrap.Component
1592  * Bootstrap Header class
1593  * @cfg {String} html content of header
1594  * @cfg {Number} level (1|2|3|4|5|6) default 1
1595  * 
1596  * @constructor
1597  * Create a new Header
1598  * @param {Object} config The config object
1599  */
1600
1601
1602 Roo.bootstrap.Header  = function(config){
1603     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1604 };
1605
1606 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1607     
1608     //href : false,
1609     html : false,
1610     level : 1,
1611     
1612     
1613     
1614     getAutoCreate : function(){
1615         
1616         
1617         
1618         var cfg = {
1619             tag: 'h' + (1 *this.level),
1620             html: this.html || ''
1621         } ;
1622         
1623         return cfg;
1624     }
1625    
1626 });
1627
1628  
1629
1630  /*
1631  * Based on:
1632  * Ext JS Library 1.1.1
1633  * Copyright(c) 2006-2007, Ext JS, LLC.
1634  *
1635  * Originally Released Under LGPL - original licence link has changed is not relivant.
1636  *
1637  * Fork - LGPL
1638  * <script type="text/javascript">
1639  */
1640  
1641 /**
1642  * @class Roo.bootstrap.MenuMgr
1643  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1644  * @singleton
1645  */
1646 Roo.bootstrap.MenuMgr = function(){
1647    var menus, active, groups = {}, attached = false, lastShow = new Date();
1648
1649    // private - called when first menu is created
1650    function init(){
1651        menus = {};
1652        active = new Roo.util.MixedCollection();
1653        Roo.get(document).addKeyListener(27, function(){
1654            if(active.length > 0){
1655                hideAll();
1656            }
1657        });
1658    }
1659
1660    // private
1661    function hideAll(){
1662        if(active && active.length > 0){
1663            var c = active.clone();
1664            c.each(function(m){
1665                m.hide();
1666            });
1667        }
1668    }
1669
1670    // private
1671    function onHide(m){
1672        active.remove(m);
1673        if(active.length < 1){
1674            Roo.get(document).un("mouseup", onMouseDown);
1675             
1676            attached = false;
1677        }
1678    }
1679
1680    // private
1681    function onShow(m){
1682        var last = active.last();
1683        lastShow = new Date();
1684        active.add(m);
1685        if(!attached){
1686           Roo.get(document).on("mouseup", onMouseDown);
1687            
1688            attached = true;
1689        }
1690        if(m.parentMenu){
1691           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1692           m.parentMenu.activeChild = m;
1693        }else if(last && last.isVisible()){
1694           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1695        }
1696    }
1697
1698    // private
1699    function onBeforeHide(m){
1700        if(m.activeChild){
1701            m.activeChild.hide();
1702        }
1703        if(m.autoHideTimer){
1704            clearTimeout(m.autoHideTimer);
1705            delete m.autoHideTimer;
1706        }
1707    }
1708
1709    // private
1710    function onBeforeShow(m){
1711        var pm = m.parentMenu;
1712        if(!pm && !m.allowOtherMenus){
1713            hideAll();
1714        }else if(pm && pm.activeChild && active != m){
1715            pm.activeChild.hide();
1716        }
1717    }
1718
1719    // private this should really trigger on mouseup..
1720    function onMouseDown(e){
1721         Roo.log("on Mouse Up");
1722         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1723             Roo.log("hideAll");
1724             hideAll();
1725             e.stopEvent();
1726         }
1727         
1728         
1729    }
1730
1731    // private
1732    function onBeforeCheck(mi, state){
1733        if(state){
1734            var g = groups[mi.group];
1735            for(var i = 0, l = g.length; i < l; i++){
1736                if(g[i] != mi){
1737                    g[i].setChecked(false);
1738                }
1739            }
1740        }
1741    }
1742
1743    return {
1744
1745        /**
1746         * Hides all menus that are currently visible
1747         */
1748        hideAll : function(){
1749             hideAll();  
1750        },
1751
1752        // private
1753        register : function(menu){
1754            if(!menus){
1755                init();
1756            }
1757            menus[menu.id] = menu;
1758            menu.on("beforehide", onBeforeHide);
1759            menu.on("hide", onHide);
1760            menu.on("beforeshow", onBeforeShow);
1761            menu.on("show", onShow);
1762            var g = menu.group;
1763            if(g && menu.events["checkchange"]){
1764                if(!groups[g]){
1765                    groups[g] = [];
1766                }
1767                groups[g].push(menu);
1768                menu.on("checkchange", onCheck);
1769            }
1770        },
1771
1772         /**
1773          * Returns a {@link Roo.menu.Menu} object
1774          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1775          * be used to generate and return a new Menu instance.
1776          */
1777        get : function(menu){
1778            if(typeof menu == "string"){ // menu id
1779                return menus[menu];
1780            }else if(menu.events){  // menu instance
1781                return menu;
1782            }
1783            /*else if(typeof menu.length == 'number'){ // array of menu items?
1784                return new Roo.bootstrap.Menu({items:menu});
1785            }else{ // otherwise, must be a config
1786                return new Roo.bootstrap.Menu(menu);
1787            }
1788            */
1789            return false;
1790        },
1791
1792        // private
1793        unregister : function(menu){
1794            delete menus[menu.id];
1795            menu.un("beforehide", onBeforeHide);
1796            menu.un("hide", onHide);
1797            menu.un("beforeshow", onBeforeShow);
1798            menu.un("show", onShow);
1799            var g = menu.group;
1800            if(g && menu.events["checkchange"]){
1801                groups[g].remove(menu);
1802                menu.un("checkchange", onCheck);
1803            }
1804        },
1805
1806        // private
1807        registerCheckable : function(menuItem){
1808            var g = menuItem.group;
1809            if(g){
1810                if(!groups[g]){
1811                    groups[g] = [];
1812                }
1813                groups[g].push(menuItem);
1814                menuItem.on("beforecheckchange", onBeforeCheck);
1815            }
1816        },
1817
1818        // private
1819        unregisterCheckable : function(menuItem){
1820            var g = menuItem.group;
1821            if(g){
1822                groups[g].remove(menuItem);
1823                menuItem.un("beforecheckchange", onBeforeCheck);
1824            }
1825        }
1826    };
1827 }();/*
1828  * - LGPL
1829  *
1830  * menu
1831  * 
1832  */
1833
1834 /**
1835  * @class Roo.bootstrap.Menu
1836  * @extends Roo.bootstrap.Component
1837  * Bootstrap Menu class - container for MenuItems
1838  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1839  * 
1840  * @constructor
1841  * Create a new Menu
1842  * @param {Object} config The config object
1843  */
1844
1845
1846 Roo.bootstrap.Menu = function(config){
1847     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1848     if (this.registerMenu) {
1849         Roo.bootstrap.MenuMgr.register(this);
1850     }
1851     this.addEvents({
1852         /**
1853          * @event beforeshow
1854          * Fires before this menu is displayed
1855          * @param {Roo.menu.Menu} this
1856          */
1857         beforeshow : true,
1858         /**
1859          * @event beforehide
1860          * Fires before this menu is hidden
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforehide : true,
1864         /**
1865          * @event show
1866          * Fires after this menu is displayed
1867          * @param {Roo.menu.Menu} this
1868          */
1869         show : true,
1870         /**
1871          * @event hide
1872          * Fires after this menu is hidden
1873          * @param {Roo.menu.Menu} this
1874          */
1875         hide : true,
1876         /**
1877          * @event click
1878          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1879          * @param {Roo.menu.Menu} this
1880          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1881          * @param {Roo.EventObject} e
1882          */
1883         click : true,
1884         /**
1885          * @event mouseover
1886          * Fires when the mouse is hovering over this menu
1887          * @param {Roo.menu.Menu} this
1888          * @param {Roo.EventObject} e
1889          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1890          */
1891         mouseover : true,
1892         /**
1893          * @event mouseout
1894          * Fires when the mouse exits this menu
1895          * @param {Roo.menu.Menu} this
1896          * @param {Roo.EventObject} e
1897          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1898          */
1899         mouseout : true,
1900         /**
1901          * @event itemclick
1902          * Fires when a menu item contained in this menu is clicked
1903          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1904          * @param {Roo.EventObject} e
1905          */
1906         itemclick: true
1907     });
1908     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1909 };
1910
1911 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1912     
1913    /// html : false,
1914     //align : '',
1915     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1916     type: false,
1917     /**
1918      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1919      */
1920     registerMenu : true,
1921     
1922     menuItems :false, // stores the menu items..
1923     
1924     hidden:true,
1925     
1926     parentMenu : false,
1927     
1928     getChildContainer : function() {
1929         return this.el;  
1930     },
1931     
1932     getAutoCreate : function(){
1933          
1934         //if (['right'].indexOf(this.align)!==-1) {
1935         //    cfg.cn[1].cls += ' pull-right'
1936         //}
1937         
1938         
1939         var cfg = {
1940             tag : 'ul',
1941             cls : 'dropdown-menu' ,
1942             style : 'z-index:1000'
1943             
1944         }
1945         
1946         if (this.type === 'submenu') {
1947             cfg.cls = 'submenu active';
1948         }
1949         if (this.type === 'treeview') {
1950             cfg.cls = 'treeview-menu';
1951         }
1952         
1953         return cfg;
1954     },
1955     initEvents : function() {
1956         
1957        // Roo.log("ADD event");
1958        // Roo.log(this.triggerEl.dom);
1959         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1960         
1961         this.triggerEl.addClass('dropdown-toggle');
1962         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1963
1964         this.el.on("mouseover", this.onMouseOver, this);
1965         this.el.on("mouseout", this.onMouseOut, this);
1966         
1967         
1968     },
1969     findTargetItem : function(e){
1970         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1971         if(!t){
1972             return false;
1973         }
1974         //Roo.log(t);         Roo.log(t.id);
1975         if(t && t.id){
1976             //Roo.log(this.menuitems);
1977             return this.menuitems.get(t.id);
1978             
1979             //return this.items.get(t.menuItemId);
1980         }
1981         
1982         return false;
1983     },
1984     onClick : function(e){
1985         Roo.log("menu.onClick");
1986         var t = this.findTargetItem(e);
1987         if(!t || t.isContainer){
1988             return;
1989         }
1990         Roo.log(e);
1991         /*
1992         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1993             if(t == this.activeItem && t.shouldDeactivate(e)){
1994                 this.activeItem.deactivate();
1995                 delete this.activeItem;
1996                 return;
1997             }
1998             if(t.canActivate){
1999                 this.setActiveItem(t, true);
2000             }
2001             return;
2002             
2003             
2004         }
2005         */
2006        
2007         Roo.log('pass click event');
2008         
2009         t.onClick(e);
2010         
2011         this.fireEvent("click", this, t, e);
2012         
2013         this.hide();
2014     },
2015      onMouseOver : function(e){
2016         var t  = this.findTargetItem(e);
2017         //Roo.log(t);
2018         //if(t){
2019         //    if(t.canActivate && !t.disabled){
2020         //        this.setActiveItem(t, true);
2021         //    }
2022         //}
2023         
2024         this.fireEvent("mouseover", this, e, t);
2025     },
2026     isVisible : function(){
2027         return !this.hidden;
2028     },
2029      onMouseOut : function(e){
2030         var t  = this.findTargetItem(e);
2031         
2032         //if(t ){
2033         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2034         //        this.activeItem.deactivate();
2035         //        delete this.activeItem;
2036         //    }
2037         //}
2038         this.fireEvent("mouseout", this, e, t);
2039     },
2040     
2041     
2042     /**
2043      * Displays this menu relative to another element
2044      * @param {String/HTMLElement/Roo.Element} element The element to align to
2045      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2046      * the element (defaults to this.defaultAlign)
2047      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2048      */
2049     show : function(el, pos, parentMenu){
2050         this.parentMenu = parentMenu;
2051         if(!this.el){
2052             this.render();
2053         }
2054         this.fireEvent("beforeshow", this);
2055         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2056     },
2057      /**
2058      * Displays this menu at a specific xy position
2059      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2060      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2061      */
2062     showAt : function(xy, parentMenu, /* private: */_e){
2063         this.parentMenu = parentMenu;
2064         if(!this.el){
2065             this.render();
2066         }
2067         if(_e !== false){
2068             this.fireEvent("beforeshow", this);
2069             //xy = this.el.adjustForConstraints(xy);
2070         }
2071         
2072         //this.el.show();
2073         this.hideMenuItems();
2074         this.hidden = false;
2075         this.triggerEl.addClass('open');
2076         
2077         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2078             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2079         }
2080         
2081         this.el.setXY(xy);
2082         this.focus();
2083         this.fireEvent("show", this);
2084     },
2085     
2086     focus : function(){
2087         return;
2088         if(!this.hidden){
2089             this.doFocus.defer(50, this);
2090         }
2091     },
2092
2093     doFocus : function(){
2094         if(!this.hidden){
2095             this.focusEl.focus();
2096         }
2097     },
2098
2099     /**
2100      * Hides this menu and optionally all parent menus
2101      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2102      */
2103     hide : function(deep){
2104         
2105         this.hideMenuItems();
2106         if(this.el && this.isVisible()){
2107             this.fireEvent("beforehide", this);
2108             if(this.activeItem){
2109                 this.activeItem.deactivate();
2110                 this.activeItem = null;
2111             }
2112             this.triggerEl.removeClass('open');;
2113             this.hidden = true;
2114             this.fireEvent("hide", this);
2115         }
2116         if(deep === true && this.parentMenu){
2117             this.parentMenu.hide(true);
2118         }
2119     },
2120     
2121     onTriggerPress  : function(e)
2122     {
2123         
2124         Roo.log('trigger press');
2125         //Roo.log(e.getTarget());
2126        // Roo.log(this.triggerEl.dom);
2127         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2128             return;
2129         }
2130         
2131         if (this.isVisible()) {
2132             Roo.log('hide');
2133             this.hide();
2134         } else {
2135             Roo.log('show');
2136             this.show(this.triggerEl, false, false);
2137         }
2138         
2139         e.stopEvent();
2140     },
2141     
2142          
2143        
2144     
2145     hideMenuItems : function()
2146     {
2147         //$(backdrop).remove()
2148         Roo.select('.open',true).each(function(aa) {
2149             
2150             aa.removeClass('open');
2151           //var parent = getParent($(this))
2152           //var relatedTarget = { relatedTarget: this }
2153           
2154            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2155           //if (e.isDefaultPrevented()) return
2156            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2157         })
2158     },
2159     addxtypeChild : function (tree, cntr) {
2160         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2161           
2162         this.menuitems.add(comp);
2163         return comp;
2164
2165     },
2166     getEl : function()
2167     {
2168         Roo.log(this.el);
2169         return this.el;
2170     }
2171 });
2172
2173  
2174  /*
2175  * - LGPL
2176  *
2177  * menu item
2178  * 
2179  */
2180
2181
2182 /**
2183  * @class Roo.bootstrap.MenuItem
2184  * @extends Roo.bootstrap.Component
2185  * Bootstrap MenuItem class
2186  * @cfg {String} html the menu label
2187  * @cfg {String} href the link
2188  * @cfg {Boolean} preventDefault (true | false) default true
2189  * @cfg {Boolean} isContainer (true | false) default false
2190  * 
2191  * 
2192  * @constructor
2193  * Create a new MenuItem
2194  * @param {Object} config The config object
2195  */
2196
2197
2198 Roo.bootstrap.MenuItem = function(config){
2199     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2200     this.addEvents({
2201         // raw events
2202         /**
2203          * @event click
2204          * The raw click event for the entire grid.
2205          * @param {Roo.bootstrap.MenuItem} this
2206          * @param {Roo.EventObject} e
2207          */
2208         "click" : true
2209     });
2210 };
2211
2212 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2213     
2214     href : false,
2215     html : false,
2216     preventDefault: true,
2217     isContainer : false,
2218     
2219     getAutoCreate : function(){
2220         
2221         if(this.isContainer){
2222             return {
2223                 tag: 'li',
2224                 cls: 'dropdown-menu-item'
2225             };
2226         }
2227         
2228         var cfg= {
2229             tag: 'li',
2230             cls: 'dropdown-menu-item',
2231             cn: [
2232                     {
2233                         tag : 'a',
2234                         href : '#',
2235                         html : 'Link'
2236                     }
2237                 ]
2238         };
2239         if (this.parent().type == 'treeview') {
2240             cfg.cls = 'treeview-menu';
2241         }
2242         
2243         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2244         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2245         return cfg;
2246     },
2247     
2248     initEvents: function() {
2249         
2250         //this.el.select('a').on('click', this.onClick, this);
2251         
2252     },
2253     onClick : function(e)
2254     {
2255         Roo.log('item on click ');
2256         //if(this.preventDefault){
2257         //    e.preventDefault();
2258         //}
2259         //this.parent().hideMenuItems();
2260         
2261         this.fireEvent('click', this, e);
2262     },
2263     getEl : function()
2264     {
2265         return this.el;
2266     }
2267 });
2268
2269  
2270
2271  /*
2272  * - LGPL
2273  *
2274  * menu separator
2275  * 
2276  */
2277
2278
2279 /**
2280  * @class Roo.bootstrap.MenuSeparator
2281  * @extends Roo.bootstrap.Component
2282  * Bootstrap MenuSeparator class
2283  * 
2284  * @constructor
2285  * Create a new MenuItem
2286  * @param {Object} config The config object
2287  */
2288
2289
2290 Roo.bootstrap.MenuSeparator = function(config){
2291     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2292 };
2293
2294 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2295     
2296     getAutoCreate : function(){
2297         var cfg = {
2298             cls: 'divider',
2299             tag : 'li'
2300         };
2301         
2302         return cfg;
2303     }
2304    
2305 });
2306
2307  
2308
2309  
2310 /*
2311 * Licence: LGPL
2312 */
2313
2314 /**
2315  * @class Roo.bootstrap.Modal
2316  * @extends Roo.bootstrap.Component
2317  * Bootstrap Modal class
2318  * @cfg {String} title Title of dialog
2319  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2320  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2321  * @cfg {Boolean} specificTitle default false
2322  * @cfg {Array} buttons Array of buttons or standard button set..
2323  * @cfg {String} buttonPosition (left|right|center) default right
2324  * @cfg {Boolean} animate default true
2325  * @cfg {Boolean} allow_close default true
2326  * 
2327  * @constructor
2328  * Create a new Modal Dialog
2329  * @param {Object} config The config object
2330  */
2331
2332 Roo.bootstrap.Modal = function(config){
2333     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2334     this.addEvents({
2335         // raw events
2336         /**
2337          * @event btnclick
2338          * The raw btnclick event for the button
2339          * @param {Roo.EventObject} e
2340          */
2341         "btnclick" : true
2342     });
2343     this.buttons = this.buttons || [];
2344      
2345     if (this.tmpl) {
2346         this.tmpl = Roo.factory(this.tmpl);
2347     }
2348     
2349 };
2350
2351 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2352     
2353     title : 'test dialog',
2354    
2355     buttons : false,
2356     
2357     // set on load...
2358      
2359     html: false,
2360     
2361     tmp: false,
2362     
2363     specificTitle: false,
2364     
2365     buttonPosition: 'right',
2366     
2367     allow_close : true,
2368     
2369     animate : true,
2370     
2371     
2372      // private
2373     bodyEl:  false,
2374     footerEl:  false,
2375     titleEl:  false,
2376     closeEl:  false,
2377     
2378     
2379     onRender : function(ct, position)
2380     {
2381         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2382      
2383         if(!this.el){
2384             var cfg = Roo.apply({},  this.getAutoCreate());
2385             cfg.id = Roo.id();
2386             //if(!cfg.name){
2387             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2388             //}
2389             //if (!cfg.name.length) {
2390             //    delete cfg.name;
2391            // }
2392             if (this.cls) {
2393                 cfg.cls += ' ' + this.cls;
2394             }
2395             if (this.style) {
2396                 cfg.style = this.style;
2397             }
2398             this.el = Roo.get(document.body).createChild(cfg, position);
2399         }
2400         //var type = this.el.dom.type;
2401         
2402         
2403         
2404         
2405         if(this.tabIndex !== undefined){
2406             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2407         }
2408         
2409         
2410         this.bodyEl = this.el.select('.modal-body',true).first();
2411         this.closeEl = this.el.select('.modal-header .close', true).first();
2412         this.footerEl = this.el.select('.modal-footer',true).first();
2413         this.titleEl = this.el.select('.modal-title',true).first();
2414         
2415         
2416          
2417         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2418         this.maskEl.enableDisplayMode("block");
2419         this.maskEl.hide();
2420         //this.el.addClass("x-dlg-modal");
2421     
2422         if (this.buttons.length) {
2423             Roo.each(this.buttons, function(bb) {
2424                 b = Roo.apply({}, bb);
2425                 b.xns = b.xns || Roo.bootstrap;
2426                 b.xtype = b.xtype || 'Button';
2427                 if (typeof(b.listeners) == 'undefined') {
2428                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2429                 }
2430                 
2431                 var btn = Roo.factory(b);
2432                 
2433                 btn.onRender(this.el.select('.modal-footer div').first());
2434                 
2435             },this);
2436         }
2437         // render the children.
2438         var nitems = [];
2439         
2440         if(typeof(this.items) != 'undefined'){
2441             var items = this.items;
2442             delete this.items;
2443
2444             for(var i =0;i < items.length;i++) {
2445                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2446             }
2447         }
2448         
2449         this.items = nitems;
2450         
2451         // where are these used - they used to be body/close/footer
2452         
2453        
2454         this.initEvents();
2455         //this.el.addClass([this.fieldClass, this.cls]);
2456         
2457     },
2458     
2459     getAutoCreate : function(){
2460         
2461         
2462         var bdy = {
2463                 cls : 'modal-body',
2464                 html : this.html || ''
2465         };
2466         
2467         var title = {
2468             tag: 'h4',
2469             cls : 'modal-title',
2470             html : this.title
2471         };
2472         
2473         if(this.specificTitle){
2474             title = this.title;
2475             
2476         };
2477         
2478         var header = [];
2479         if (this.allow_close) {
2480             header.push({
2481                 tag: 'button',
2482                 cls : 'close',
2483                 html : '&times'
2484             });
2485         }
2486         header.push(title);
2487         
2488         var modal = {
2489             cls: "modal",
2490             style : 'display: none',
2491             cn : [
2492                 {
2493                     cls: "modal-dialog",
2494                     cn : [
2495                         {
2496                             cls : "modal-content",
2497                             cn : [
2498                                 {
2499                                     cls : 'modal-header',
2500                                     cn : header
2501                                 },
2502                                 bdy,
2503                                 {
2504                                     cls : 'modal-footer',
2505                                     cn : [
2506                                         {
2507                                             tag: 'div',
2508                                             cls: 'btn-' + this.buttonPosition
2509                                         }
2510                                     ]
2511                                     
2512                                 }
2513                                 
2514                                 
2515                             ]
2516                             
2517                         }
2518                     ]
2519                         
2520                 }
2521             ]
2522         };
2523         
2524         if(this.animate){
2525             modal.cls += ' fade';
2526         }
2527         
2528         return modal;
2529           
2530     },
2531     getChildContainer : function() {
2532          
2533          return this.bodyEl;
2534         
2535     },
2536     getButtonContainer : function() {
2537          return this.el.select('.modal-footer div',true).first();
2538         
2539     },
2540     initEvents : function()
2541     {
2542         if (this.allow_close) {
2543             this.closeEl.on('click', this.hide, this);
2544         }
2545
2546     },
2547     show : function() {
2548         
2549         if (!this.rendered) {
2550             this.render();
2551         }
2552         
2553         this.el.setStyle('display', 'block');
2554         
2555         if(this.animate){
2556             var _this = this;
2557             (function(){ _this.el.addClass('in'); }).defer(50);
2558         }else{
2559             this.el.addClass('in');
2560         }
2561         
2562         // not sure how we can show data in here.. 
2563         //if (this.tmpl) {
2564         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2565         //}
2566         
2567         Roo.get(document.body).addClass("x-body-masked");
2568         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2569         this.maskEl.show();
2570         this.el.setStyle('zIndex', '10001');
2571        
2572         this.fireEvent('show', this);
2573         
2574         
2575     },
2576     hide : function()
2577     {
2578         this.maskEl.hide();
2579         Roo.get(document.body).removeClass("x-body-masked");
2580         this.el.removeClass('in');
2581         
2582         if(this.animate){
2583             var _this = this;
2584             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2585         }else{
2586             this.el.setStyle('display', 'none');
2587         }
2588         
2589         this.fireEvent('hide', this);
2590     },
2591     
2592     addButton : function(str, cb)
2593     {
2594          
2595         
2596         var b = Roo.apply({}, { html : str } );
2597         b.xns = b.xns || Roo.bootstrap;
2598         b.xtype = b.xtype || 'Button';
2599         if (typeof(b.listeners) == 'undefined') {
2600             b.listeners = { click : cb.createDelegate(this)  };
2601         }
2602         
2603         var btn = Roo.factory(b);
2604            
2605         btn.onRender(this.el.select('.modal-footer div').first());
2606         
2607         return btn;   
2608        
2609     },
2610     
2611     setDefaultButton : function(btn)
2612     {
2613         //this.el.select('.modal-footer').()
2614     },
2615     resizeTo: function(w,h)
2616     {
2617         // skip..
2618     },
2619     setContentSize  : function(w, h)
2620     {
2621         
2622     },
2623     onButtonClick: function(btn,e)
2624     {
2625         //Roo.log([a,b,c]);
2626         this.fireEvent('btnclick', btn.name, e);
2627     },
2628      /**
2629      * Set the title of the Dialog
2630      * @param {String} str new Title
2631      */
2632     setTitle: function(str) {
2633         this.titleEl.dom.innerHTML = str;    
2634     },
2635     /**
2636      * Set the body of the Dialog
2637      * @param {String} str new Title
2638      */
2639     setBody: function(str) {
2640         this.bodyEl.dom.innerHTML = str;    
2641     },
2642     /**
2643      * Set the body of the Dialog using the template
2644      * @param {Obj} data - apply this data to the template and replace the body contents.
2645      */
2646     applyBody: function(obj)
2647     {
2648         if (!this.tmpl) {
2649             Roo.log("Error - using apply Body without a template");
2650             //code
2651         }
2652         this.tmpl.overwrite(this.bodyEl, obj);
2653     }
2654     
2655 });
2656
2657
2658 Roo.apply(Roo.bootstrap.Modal,  {
2659     /**
2660          * Button config that displays a single OK button
2661          * @type Object
2662          */
2663         OK :  [{
2664             name : 'ok',
2665             weight : 'primary',
2666             html : 'OK'
2667         }], 
2668         /**
2669          * Button config that displays Yes and No buttons
2670          * @type Object
2671          */
2672         YESNO : [
2673             {
2674                 name  : 'no',
2675                 html : 'No'
2676             },
2677             {
2678                 name  :'yes',
2679                 weight : 'primary',
2680                 html : 'Yes'
2681             }
2682         ],
2683         
2684         /**
2685          * Button config that displays OK and Cancel buttons
2686          * @type Object
2687          */
2688         OKCANCEL : [
2689             {
2690                name : 'cancel',
2691                 html : 'Cancel'
2692             },
2693             {
2694                 name : 'ok',
2695                 weight : 'primary',
2696                 html : 'OK'
2697             }
2698         ],
2699         /**
2700          * Button config that displays Yes, No and Cancel buttons
2701          * @type Object
2702          */
2703         YESNOCANCEL : [
2704             {
2705                 name : 'yes',
2706                 weight : 'primary',
2707                 html : 'Yes'
2708             },
2709             {
2710                 name : 'no',
2711                 html : 'No'
2712             },
2713             {
2714                 name : 'cancel',
2715                 html : 'Cancel'
2716             }
2717         ]
2718 });
2719  
2720  /*
2721  * - LGPL
2722  *
2723  * messagebox - can be used as a replace
2724  * 
2725  */
2726 /**
2727  * @class Roo.MessageBox
2728  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2729  * Example usage:
2730  *<pre><code>
2731 // Basic alert:
2732 Roo.Msg.alert('Status', 'Changes saved successfully.');
2733
2734 // Prompt for user data:
2735 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2736     if (btn == 'ok'){
2737         // process text value...
2738     }
2739 });
2740
2741 // Show a dialog using config options:
2742 Roo.Msg.show({
2743    title:'Save Changes?',
2744    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2745    buttons: Roo.Msg.YESNOCANCEL,
2746    fn: processResult,
2747    animEl: 'elId'
2748 });
2749 </code></pre>
2750  * @singleton
2751  */
2752 Roo.bootstrap.MessageBox = function(){
2753     var dlg, opt, mask, waitTimer;
2754     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2755     var buttons, activeTextEl, bwidth;
2756
2757     
2758     // private
2759     var handleButton = function(button){
2760         dlg.hide();
2761         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2762     };
2763
2764     // private
2765     var handleHide = function(){
2766         if(opt && opt.cls){
2767             dlg.el.removeClass(opt.cls);
2768         }
2769         //if(waitTimer){
2770         //    Roo.TaskMgr.stop(waitTimer);
2771         //    waitTimer = null;
2772         //}
2773     };
2774
2775     // private
2776     var updateButtons = function(b){
2777         var width = 0;
2778         if(!b){
2779             buttons["ok"].hide();
2780             buttons["cancel"].hide();
2781             buttons["yes"].hide();
2782             buttons["no"].hide();
2783             //dlg.footer.dom.style.display = 'none';
2784             return width;
2785         }
2786         dlg.footerEl.dom.style.display = '';
2787         for(var k in buttons){
2788             if(typeof buttons[k] != "function"){
2789                 if(b[k]){
2790                     buttons[k].show();
2791                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2792                     width += buttons[k].el.getWidth()+15;
2793                 }else{
2794                     buttons[k].hide();
2795                 }
2796             }
2797         }
2798         return width;
2799     };
2800
2801     // private
2802     var handleEsc = function(d, k, e){
2803         if(opt && opt.closable !== false){
2804             dlg.hide();
2805         }
2806         if(e){
2807             e.stopEvent();
2808         }
2809     };
2810
2811     return {
2812         /**
2813          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2814          * @return {Roo.BasicDialog} The BasicDialog element
2815          */
2816         getDialog : function(){
2817            if(!dlg){
2818                 dlg = new Roo.bootstrap.Modal( {
2819                     //draggable: true,
2820                     //resizable:false,
2821                     //constraintoviewport:false,
2822                     //fixedcenter:true,
2823                     //collapsible : false,
2824                     //shim:true,
2825                     //modal: true,
2826                   //  width:400,
2827                   //  height:100,
2828                     //buttonAlign:"center",
2829                     closeClick : function(){
2830                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2831                             handleButton("no");
2832                         }else{
2833                             handleButton("cancel");
2834                         }
2835                     }
2836                 });
2837                 dlg.render();
2838                 dlg.on("hide", handleHide);
2839                 mask = dlg.mask;
2840                 //dlg.addKeyListener(27, handleEsc);
2841                 buttons = {};
2842                 this.buttons = buttons;
2843                 var bt = this.buttonText;
2844                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2845                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2846                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2847                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2848                 Roo.log(buttons)
2849                 bodyEl = dlg.bodyEl.createChild({
2850
2851                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2852                         '<textarea class="roo-mb-textarea"></textarea>' +
2853                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2854                 });
2855                 msgEl = bodyEl.dom.firstChild;
2856                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2857                 textboxEl.enableDisplayMode();
2858                 textboxEl.addKeyListener([10,13], function(){
2859                     if(dlg.isVisible() && opt && opt.buttons){
2860                         if(opt.buttons.ok){
2861                             handleButton("ok");
2862                         }else if(opt.buttons.yes){
2863                             handleButton("yes");
2864                         }
2865                     }
2866                 });
2867                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2868                 textareaEl.enableDisplayMode();
2869                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2870                 progressEl.enableDisplayMode();
2871                 var pf = progressEl.dom.firstChild;
2872                 if (pf) {
2873                     pp = Roo.get(pf.firstChild);
2874                     pp.setHeight(pf.offsetHeight);
2875                 }
2876                 
2877             }
2878             return dlg;
2879         },
2880
2881         /**
2882          * Updates the message box body text
2883          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2884          * the XHTML-compliant non-breaking space character '&amp;#160;')
2885          * @return {Roo.MessageBox} This message box
2886          */
2887         updateText : function(text){
2888             if(!dlg.isVisible() && !opt.width){
2889                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2890             }
2891             msgEl.innerHTML = text || '&#160;';
2892       
2893             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2894             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2895             var w = Math.max(
2896                     Math.min(opt.width || cw , this.maxWidth), 
2897                     Math.max(opt.minWidth || this.minWidth, bwidth)
2898             );
2899             if(opt.prompt){
2900                 activeTextEl.setWidth(w);
2901             }
2902             if(dlg.isVisible()){
2903                 dlg.fixedcenter = false;
2904             }
2905             // to big, make it scroll. = But as usual stupid IE does not support
2906             // !important..
2907             
2908             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2909                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2910                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2911             } else {
2912                 bodyEl.dom.style.height = '';
2913                 bodyEl.dom.style.overflowY = '';
2914             }
2915             if (cw > w) {
2916                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2917             } else {
2918                 bodyEl.dom.style.overflowX = '';
2919             }
2920             
2921             dlg.setContentSize(w, bodyEl.getHeight());
2922             if(dlg.isVisible()){
2923                 dlg.fixedcenter = true;
2924             }
2925             return this;
2926         },
2927
2928         /**
2929          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2930          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2931          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2932          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2933          * @return {Roo.MessageBox} This message box
2934          */
2935         updateProgress : function(value, text){
2936             if(text){
2937                 this.updateText(text);
2938             }
2939             if (pp) { // weird bug on my firefox - for some reason this is not defined
2940                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2941             }
2942             return this;
2943         },        
2944
2945         /**
2946          * Returns true if the message box is currently displayed
2947          * @return {Boolean} True if the message box is visible, else false
2948          */
2949         isVisible : function(){
2950             return dlg && dlg.isVisible();  
2951         },
2952
2953         /**
2954          * Hides the message box if it is displayed
2955          */
2956         hide : function(){
2957             if(this.isVisible()){
2958                 dlg.hide();
2959             }  
2960         },
2961
2962         /**
2963          * Displays a new message box, or reinitializes an existing message box, based on the config options
2964          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2965          * The following config object properties are supported:
2966          * <pre>
2967 Property    Type             Description
2968 ----------  ---------------  ------------------------------------------------------------------------------------
2969 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2970                                    closes (defaults to undefined)
2971 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2972                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2973 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2974                                    progress and wait dialogs will ignore this property and always hide the
2975                                    close button as they can only be closed programmatically.
2976 cls               String           A custom CSS class to apply to the message box element
2977 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2978                                    displayed (defaults to 75)
2979 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2980                                    function will be btn (the name of the button that was clicked, if applicable,
2981                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2982                                    Progress and wait dialogs will ignore this option since they do not respond to
2983                                    user actions and can only be closed programmatically, so any required function
2984                                    should be called by the same code after it closes the dialog.
2985 icon              String           A CSS class that provides a background image to be used as an icon for
2986                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2987 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2988 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2989 modal             Boolean          False to allow user interaction with the page while the message box is
2990                                    displayed (defaults to true)
2991 msg               String           A string that will replace the existing message box body text (defaults
2992                                    to the XHTML-compliant non-breaking space character '&#160;')
2993 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2994 progress          Boolean          True to display a progress bar (defaults to false)
2995 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2996 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2997 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2998 title             String           The title text
2999 value             String           The string value to set into the active textbox element if displayed
3000 wait              Boolean          True to display a progress bar (defaults to false)
3001 width             Number           The width of the dialog in pixels
3002 </pre>
3003          *
3004          * Example usage:
3005          * <pre><code>
3006 Roo.Msg.show({
3007    title: 'Address',
3008    msg: 'Please enter your address:',
3009    width: 300,
3010    buttons: Roo.MessageBox.OKCANCEL,
3011    multiline: true,
3012    fn: saveAddress,
3013    animEl: 'addAddressBtn'
3014 });
3015 </code></pre>
3016          * @param {Object} config Configuration options
3017          * @return {Roo.MessageBox} This message box
3018          */
3019         show : function(options)
3020         {
3021             
3022             // this causes nightmares if you show one dialog after another
3023             // especially on callbacks..
3024              
3025             if(this.isVisible()){
3026                 
3027                 this.hide();
3028                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3029                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3030                 Roo.log("New Dialog Message:" +  options.msg )
3031                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3032                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3033                 
3034             }
3035             var d = this.getDialog();
3036             opt = options;
3037             d.setTitle(opt.title || "&#160;");
3038             d.closeEl.setDisplayed(opt.closable !== false);
3039             activeTextEl = textboxEl;
3040             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3041             if(opt.prompt){
3042                 if(opt.multiline){
3043                     textboxEl.hide();
3044                     textareaEl.show();
3045                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3046                         opt.multiline : this.defaultTextHeight);
3047                     activeTextEl = textareaEl;
3048                 }else{
3049                     textboxEl.show();
3050                     textareaEl.hide();
3051                 }
3052             }else{
3053                 textboxEl.hide();
3054                 textareaEl.hide();
3055             }
3056             progressEl.setDisplayed(opt.progress === true);
3057             this.updateProgress(0);
3058             activeTextEl.dom.value = opt.value || "";
3059             if(opt.prompt){
3060                 dlg.setDefaultButton(activeTextEl);
3061             }else{
3062                 var bs = opt.buttons;
3063                 var db = null;
3064                 if(bs && bs.ok){
3065                     db = buttons["ok"];
3066                 }else if(bs && bs.yes){
3067                     db = buttons["yes"];
3068                 }
3069                 dlg.setDefaultButton(db);
3070             }
3071             bwidth = updateButtons(opt.buttons);
3072             this.updateText(opt.msg);
3073             if(opt.cls){
3074                 d.el.addClass(opt.cls);
3075             }
3076             d.proxyDrag = opt.proxyDrag === true;
3077             d.modal = opt.modal !== false;
3078             d.mask = opt.modal !== false ? mask : false;
3079             if(!d.isVisible()){
3080                 // force it to the end of the z-index stack so it gets a cursor in FF
3081                 document.body.appendChild(dlg.el.dom);
3082                 d.animateTarget = null;
3083                 d.show(options.animEl);
3084             }
3085             return this;
3086         },
3087
3088         /**
3089          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3090          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3091          * and closing the message box when the process is complete.
3092          * @param {String} title The title bar text
3093          * @param {String} msg The message box body text
3094          * @return {Roo.MessageBox} This message box
3095          */
3096         progress : function(title, msg){
3097             this.show({
3098                 title : title,
3099                 msg : msg,
3100                 buttons: false,
3101                 progress:true,
3102                 closable:false,
3103                 minWidth: this.minProgressWidth,
3104                 modal : true
3105             });
3106             return this;
3107         },
3108
3109         /**
3110          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3111          * If a callback function is passed it will be called after the user clicks the button, and the
3112          * id of the button that was clicked will be passed as the only parameter to the callback
3113          * (could also be the top-right close button).
3114          * @param {String} title The title bar text
3115          * @param {String} msg The message box body text
3116          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3117          * @param {Object} scope (optional) The scope of the callback function
3118          * @return {Roo.MessageBox} This message box
3119          */
3120         alert : function(title, msg, fn, scope){
3121             this.show({
3122                 title : title,
3123                 msg : msg,
3124                 buttons: this.OK,
3125                 fn: fn,
3126                 scope : scope,
3127                 modal : true
3128             });
3129             return this;
3130         },
3131
3132         /**
3133          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3134          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3135          * You are responsible for closing the message box when the process is complete.
3136          * @param {String} msg The message box body text
3137          * @param {String} title (optional) The title bar text
3138          * @return {Roo.MessageBox} This message box
3139          */
3140         wait : function(msg, title){
3141             this.show({
3142                 title : title,
3143                 msg : msg,
3144                 buttons: false,
3145                 closable:false,
3146                 progress:true,
3147                 modal:true,
3148                 width:300,
3149                 wait:true
3150             });
3151             waitTimer = Roo.TaskMgr.start({
3152                 run: function(i){
3153                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3154                 },
3155                 interval: 1000
3156             });
3157             return this;
3158         },
3159
3160         /**
3161          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3162          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3163          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3164          * @param {String} title The title bar text
3165          * @param {String} msg The message box body text
3166          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3167          * @param {Object} scope (optional) The scope of the callback function
3168          * @return {Roo.MessageBox} This message box
3169          */
3170         confirm : function(title, msg, fn, scope){
3171             this.show({
3172                 title : title,
3173                 msg : msg,
3174                 buttons: this.YESNO,
3175                 fn: fn,
3176                 scope : scope,
3177                 modal : true
3178             });
3179             return this;
3180         },
3181
3182         /**
3183          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3184          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3185          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3186          * (could also be the top-right close button) and the text that was entered will be passed as the two
3187          * parameters to the callback.
3188          * @param {String} title The title bar text
3189          * @param {String} msg The message box body text
3190          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3191          * @param {Object} scope (optional) The scope of the callback function
3192          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3193          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3194          * @return {Roo.MessageBox} This message box
3195          */
3196         prompt : function(title, msg, fn, scope, multiline){
3197             this.show({
3198                 title : title,
3199                 msg : msg,
3200                 buttons: this.OKCANCEL,
3201                 fn: fn,
3202                 minWidth:250,
3203                 scope : scope,
3204                 prompt:true,
3205                 multiline: multiline,
3206                 modal : true
3207             });
3208             return this;
3209         },
3210
3211         /**
3212          * Button config that displays a single OK button
3213          * @type Object
3214          */
3215         OK : {ok:true},
3216         /**
3217          * Button config that displays Yes and No buttons
3218          * @type Object
3219          */
3220         YESNO : {yes:true, no:true},
3221         /**
3222          * Button config that displays OK and Cancel buttons
3223          * @type Object
3224          */
3225         OKCANCEL : {ok:true, cancel:true},
3226         /**
3227          * Button config that displays Yes, No and Cancel buttons
3228          * @type Object
3229          */
3230         YESNOCANCEL : {yes:true, no:true, cancel:true},
3231
3232         /**
3233          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3234          * @type Number
3235          */
3236         defaultTextHeight : 75,
3237         /**
3238          * The maximum width in pixels of the message box (defaults to 600)
3239          * @type Number
3240          */
3241         maxWidth : 600,
3242         /**
3243          * The minimum width in pixels of the message box (defaults to 100)
3244          * @type Number
3245          */
3246         minWidth : 100,
3247         /**
3248          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3249          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3250          * @type Number
3251          */
3252         minProgressWidth : 250,
3253         /**
3254          * An object containing the default button text strings that can be overriden for localized language support.
3255          * Supported properties are: ok, cancel, yes and no.
3256          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3257          * @type Object
3258          */
3259         buttonText : {
3260             ok : "OK",
3261             cancel : "Cancel",
3262             yes : "Yes",
3263             no : "No"
3264         }
3265     };
3266 }();
3267
3268 /**
3269  * Shorthand for {@link Roo.MessageBox}
3270  */
3271 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3272 Roo.Msg = Roo.Msg || Roo.MessageBox;
3273 /*
3274  * - LGPL
3275  *
3276  * navbar
3277  * 
3278  */
3279
3280 /**
3281  * @class Roo.bootstrap.Navbar
3282  * @extends Roo.bootstrap.Component
3283  * Bootstrap Navbar class
3284
3285  * @constructor
3286  * Create a new Navbar
3287  * @param {Object} config The config object
3288  */
3289
3290
3291 Roo.bootstrap.Navbar = function(config){
3292     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3293     
3294 };
3295
3296 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3297     
3298     
3299    
3300     // private
3301     navItems : false,
3302     loadMask : false,
3303     
3304     
3305     getAutoCreate : function(){
3306         
3307         
3308         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3309         
3310     },
3311     
3312     initEvents :function ()
3313     {
3314         //Roo.log(this.el.select('.navbar-toggle',true));
3315         this.el.select('.navbar-toggle',true).on('click', function() {
3316            // Roo.log('click');
3317             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3318         }, this);
3319         
3320         var mark = {
3321             tag: "div",
3322             cls:"x-dlg-mask"
3323         }
3324         
3325         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3326         
3327         var size = this.el.getSize();
3328         this.maskEl.setSize(size.width, size.height);
3329         this.maskEl.enableDisplayMode("block");
3330         this.maskEl.hide();
3331         
3332         if(this.loadMask){
3333             this.maskEl.show();
3334         }
3335     },
3336     
3337     
3338     getChildContainer : function()
3339     {
3340         if (this.el.select('.collapse').getCount()) {
3341             return this.el.select('.collapse',true).first();
3342         }
3343         
3344         return this.el;
3345     },
3346     
3347     mask : function()
3348     {
3349         this.maskEl.show();
3350     },
3351     
3352     unmask : function()
3353     {
3354         this.maskEl.hide();
3355     } 
3356     
3357     
3358     
3359     
3360 });
3361
3362
3363
3364  
3365
3366  /*
3367  * - LGPL
3368  *
3369  * navbar
3370  * 
3371  */
3372
3373 /**
3374  * @class Roo.bootstrap.NavSimplebar
3375  * @extends Roo.bootstrap.Navbar
3376  * Bootstrap Sidebar class
3377  *
3378  * @cfg {Boolean} inverse is inverted color
3379  * 
3380  * @cfg {String} type (nav | pills | tabs)
3381  * @cfg {Boolean} arrangement stacked | justified
3382  * @cfg {String} align (left | right) alignment
3383  * 
3384  * @cfg {Boolean} main (true|false) main nav bar? default false
3385  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3386  * 
3387  * @cfg {String} tag (header|footer|nav|div) default is nav 
3388
3389  * 
3390  * 
3391  * 
3392  * @constructor
3393  * Create a new Sidebar
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.NavSimplebar = function(config){
3399     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3403     
3404     inverse: false,
3405     
3406     type: false,
3407     arrangement: '',
3408     align : false,
3409     
3410     
3411     
3412     main : false,
3413     
3414     
3415     tag : false,
3416     
3417     
3418     getAutoCreate : function(){
3419         
3420         
3421         var cfg = {
3422             tag : this.tag || 'div',
3423             cls : 'navbar'
3424         };
3425           
3426         
3427         cfg.cn = [
3428             {
3429                 cls: 'nav',
3430                 tag : 'ul'
3431             }
3432         ];
3433         
3434          
3435         this.type = this.type || 'nav';
3436         if (['tabs','pills'].indexOf(this.type)!==-1) {
3437             cfg.cn[0].cls += ' nav-' + this.type
3438         
3439         
3440         } else {
3441             if (this.type!=='nav') {
3442                 Roo.log('nav type must be nav/tabs/pills')
3443             }
3444             cfg.cn[0].cls += ' navbar-nav'
3445         }
3446         
3447         
3448         
3449         
3450         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3451             cfg.cn[0].cls += ' nav-' + this.arrangement;
3452         }
3453         
3454         
3455         if (this.align === 'right') {
3456             cfg.cn[0].cls += ' navbar-right';
3457         }
3458         
3459         if (this.inverse) {
3460             cfg.cls += ' navbar-inverse';
3461             
3462         }
3463         
3464         
3465         return cfg;
3466     
3467         
3468     }
3469     
3470     
3471     
3472 });
3473
3474
3475
3476  
3477
3478  
3479        /*
3480  * - LGPL
3481  *
3482  * navbar
3483  * 
3484  */
3485
3486 /**
3487  * @class Roo.bootstrap.NavHeaderbar
3488  * @extends Roo.bootstrap.NavSimplebar
3489  * Bootstrap Sidebar class
3490  *
3491  * @cfg {String} brand what is brand
3492  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3493  * @cfg {String} brand_href href of the brand
3494  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3495  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3496  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3497  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3498  * 
3499  * @constructor
3500  * Create a new Sidebar
3501  * @param {Object} config The config object
3502  */
3503
3504
3505 Roo.bootstrap.NavHeaderbar = function(config){
3506     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3507       
3508 };
3509
3510 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3511     
3512     position: '',
3513     brand: '',
3514     brand_href: false,
3515     srButton : true,
3516     autohide : false,
3517     desktopCenter : false,
3518    
3519     
3520     getAutoCreate : function(){
3521         
3522         var   cfg = {
3523             tag: this.nav || 'nav',
3524             cls: 'navbar',
3525             role: 'navigation',
3526             cn: []
3527         };
3528         
3529         var cn = cfg.cn;
3530         if (this.desktopCenter) {
3531             cn.push({cls : 'container', cn : []});
3532             cn = cn[0].cn;
3533         }
3534         
3535         if(this.srButton){
3536             cn.push({
3537                 tag: 'div',
3538                 cls: 'navbar-header',
3539                 cn: [
3540                     {
3541                         tag: 'button',
3542                         type: 'button',
3543                         cls: 'navbar-toggle',
3544                         'data-toggle': 'collapse',
3545                         cn: [
3546                             {
3547                                 tag: 'span',
3548                                 cls: 'sr-only',
3549                                 html: 'Toggle navigation'
3550                             },
3551                             {
3552                                 tag: 'span',
3553                                 cls: 'icon-bar'
3554                             },
3555                             {
3556                                 tag: 'span',
3557                                 cls: 'icon-bar'
3558                             },
3559                             {
3560                                 tag: 'span',
3561                                 cls: 'icon-bar'
3562                             }
3563                         ]
3564                     }
3565                 ]
3566             });
3567         }
3568         
3569         cn.push({
3570             tag: 'div',
3571             cls: 'collapse navbar-collapse',
3572             cn : []
3573         });
3574         
3575         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3576         
3577         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3578             cfg.cls += ' navbar-' + this.position;
3579             
3580             // tag can override this..
3581             
3582             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3583         }
3584         
3585         if (this.brand !== '') {
3586             cn[0].cn.push({
3587                 tag: 'a',
3588                 href: this.brand_href ? this.brand_href : '#',
3589                 cls: 'navbar-brand',
3590                 cn: [
3591                 this.brand
3592                 ]
3593             });
3594         }
3595         
3596         if(this.main){
3597             cfg.cls += ' main-nav';
3598         }
3599         
3600         
3601         return cfg;
3602
3603         
3604     },
3605     getHeaderChildContainer : function()
3606     {
3607         if (this.el.select('.navbar-header').getCount()) {
3608             return this.el.select('.navbar-header',true).first();
3609         }
3610         
3611         return this.getChildContainer();
3612     },
3613     
3614     
3615     initEvents : function()
3616     {
3617         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3618         
3619         if (this.autohide) {
3620             
3621             var prevScroll = 0;
3622             var ft = this.el;
3623             
3624             Roo.get(document).on('scroll',function(e) {
3625                 var ns = Roo.get(document).getScroll().top;
3626                 var os = prevScroll;
3627                 prevScroll = ns;
3628                 
3629                 if(ns > os){
3630                     ft.removeClass('slideDown');
3631                     ft.addClass('slideUp');
3632                     return;
3633                 }
3634                 ft.removeClass('slideUp');
3635                 ft.addClass('slideDown');
3636                  
3637               
3638           },this);
3639         }
3640     }    
3641     
3642 });
3643
3644
3645
3646  
3647
3648  /*
3649  * - LGPL
3650  *
3651  * navbar
3652  * 
3653  */
3654
3655 /**
3656  * @class Roo.bootstrap.NavSidebar
3657  * @extends Roo.bootstrap.Navbar
3658  * Bootstrap Sidebar class
3659  * 
3660  * @constructor
3661  * Create a new Sidebar
3662  * @param {Object} config The config object
3663  */
3664
3665
3666 Roo.bootstrap.NavSidebar = function(config){
3667     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3668 };
3669
3670 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3671     
3672     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3673     
3674     getAutoCreate : function(){
3675         
3676         
3677         return  {
3678             tag: 'div',
3679             cls: 'sidebar sidebar-nav'
3680         };
3681     
3682         
3683     }
3684     
3685     
3686     
3687 });
3688
3689
3690
3691  
3692
3693  /*
3694  * - LGPL
3695  *
3696  * nav group
3697  * 
3698  */
3699
3700 /**
3701  * @class Roo.bootstrap.NavGroup
3702  * @extends Roo.bootstrap.Component
3703  * Bootstrap NavGroup class
3704  * @cfg {String} align (left|right)
3705  * @cfg {Boolean} inverse
3706  * @cfg {String} type (nav|pills|tab) default nav
3707  * @cfg {String} navId - reference Id for navbar.
3708
3709  * 
3710  * @constructor
3711  * Create a new nav group
3712  * @param {Object} config The config object
3713  */
3714
3715 Roo.bootstrap.NavGroup = function(config){
3716     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3717     this.navItems = [];
3718    
3719     Roo.bootstrap.NavGroup.register(this);
3720      this.addEvents({
3721         /**
3722              * @event changed
3723              * Fires when the active item changes
3724              * @param {Roo.bootstrap.NavGroup} this
3725              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3726              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3727          */
3728         'changed': true
3729      });
3730     
3731 };
3732
3733 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3734     
3735     align: '',
3736     inverse: false,
3737     form: false,
3738     type: 'nav',
3739     navId : '',
3740     // private
3741     
3742     navItems : false, 
3743     
3744     getAutoCreate : function()
3745     {
3746         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3747         
3748         cfg = {
3749             tag : 'ul',
3750             cls: 'nav' 
3751         }
3752         
3753         if (['tabs','pills'].indexOf(this.type)!==-1) {
3754             cfg.cls += ' nav-' + this.type
3755         } else {
3756             if (this.type!=='nav') {
3757                 Roo.log('nav type must be nav/tabs/pills')
3758             }
3759             cfg.cls += ' navbar-nav'
3760         }
3761         
3762         if (this.parent().sidebar) {
3763             cfg = {
3764                 tag: 'ul',
3765                 cls: 'dashboard-menu sidebar-menu'
3766             }
3767             
3768             return cfg;
3769         }
3770         
3771         if (this.form === true) {
3772             cfg = {
3773                 tag: 'form',
3774                 cls: 'navbar-form'
3775             }
3776             
3777             if (this.align === 'right') {
3778                 cfg.cls += ' navbar-right';
3779             } else {
3780                 cfg.cls += ' navbar-left';
3781             }
3782         }
3783         
3784         if (this.align === 'right') {
3785             cfg.cls += ' navbar-right';
3786         }
3787         
3788         if (this.inverse) {
3789             cfg.cls += ' navbar-inverse';
3790             
3791         }
3792         
3793         
3794         return cfg;
3795     },
3796     /**
3797     * sets the active Navigation item
3798     * @param {Roo.bootstrap.NavItem} the new current navitem
3799     */
3800     setActiveItem : function(item)
3801     {
3802         var prev = false;
3803         Roo.each(this.navItems, function(v){
3804             if (v == item) {
3805                 return ;
3806             }
3807             if (v.isActive()) {
3808                 v.setActive(false, true);
3809                 prev = v;
3810                 
3811             }
3812             
3813         });
3814
3815         item.setActive(true, true);
3816         this.fireEvent('changed', this, item, prev);
3817         
3818         
3819     },
3820     /**
3821     * gets the active Navigation item
3822     * @return {Roo.bootstrap.NavItem} the current navitem
3823     */
3824     getActive : function()
3825     {
3826         
3827         var prev = false;
3828         Roo.each(this.navItems, function(v){
3829             
3830             if (v.isActive()) {
3831                 prev = v;
3832                 
3833             }
3834             
3835         });
3836         return prev;
3837     },
3838     
3839     indexOfNav : function()
3840     {
3841         
3842         var prev = false;
3843         Roo.each(this.navItems, function(v,i){
3844             
3845             if (v.isActive()) {
3846                 prev = i;
3847                 
3848             }
3849             
3850         });
3851         return prev;
3852     },
3853     /**
3854     * adds a Navigation item
3855     * @param {Roo.bootstrap.NavItem} the navitem to add
3856     */
3857     addItem : function(cfg)
3858     {
3859         var cn = new Roo.bootstrap.NavItem(cfg);
3860         this.register(cn);
3861         cn.parentId = this.id;
3862         cn.onRender(this.el, null);
3863         return cn;
3864     },
3865     /**
3866     * register a Navigation item
3867     * @param {Roo.bootstrap.NavItem} the navitem to add
3868     */
3869     register : function(item)
3870     {
3871         this.navItems.push( item);
3872         item.navId = this.navId;
3873     
3874     },
3875     
3876     /**
3877     * clear all the Navigation item
3878     */
3879    
3880     clearAll : function()
3881     {
3882         this.navItems = [];
3883         this.el.dom.innerHTML = '';
3884     },
3885     
3886     getNavItem: function(tabId)
3887     {
3888         var ret = false;
3889         Roo.each(this.navItems, function(e) {
3890             if (e.tabId == tabId) {
3891                ret =  e;
3892                return false;
3893             }
3894             return true;
3895             
3896         });
3897         return ret;
3898     },
3899     
3900     setActiveNext : function()
3901     {
3902         var i = this.indexOfNav(this.getActive());
3903         if (i > this.navItems.length) {
3904             return;
3905         }
3906         this.setActiveItem(this.navItems[i+1]);
3907     },
3908     setActivePrev : function()
3909     {
3910         var i = this.indexOfNav(this.getActive());
3911         if (i  < 1) {
3912             return;
3913         }
3914         this.setActiveItem(this.navItems[i-1]);
3915     },
3916     clearWasActive : function(except) {
3917         Roo.each(this.navItems, function(e) {
3918             if (e.tabId != except.tabId && e.was_active) {
3919                e.was_active = false;
3920                return false;
3921             }
3922             return true;
3923             
3924         });
3925     },
3926     getWasActive : function ()
3927     {
3928         var r = false;
3929         Roo.each(this.navItems, function(e) {
3930             if (e.was_active) {
3931                r = e;
3932                return false;
3933             }
3934             return true;
3935             
3936         });
3937         return r;
3938     }
3939     
3940     
3941 });
3942
3943  
3944 Roo.apply(Roo.bootstrap.NavGroup, {
3945     
3946     groups: {},
3947      /**
3948     * register a Navigation Group
3949     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3950     */
3951     register : function(navgrp)
3952     {
3953         this.groups[navgrp.navId] = navgrp;
3954         
3955     },
3956     /**
3957     * fetch a Navigation Group based on the navigation ID
3958     * @param {string} the navgroup to add
3959     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3960     */
3961     get: function(navId) {
3962         if (typeof(this.groups[navId]) == 'undefined') {
3963             return false;
3964             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3965         }
3966         return this.groups[navId] ;
3967     }
3968     
3969     
3970     
3971 });
3972
3973  /*
3974  * - LGPL
3975  *
3976  * row
3977  * 
3978  */
3979
3980 /**
3981  * @class Roo.bootstrap.NavItem
3982  * @extends Roo.bootstrap.Component
3983  * Bootstrap Navbar.NavItem class
3984  * @cfg {String} href  link to
3985  * @cfg {String} html content of button
3986  * @cfg {String} badge text inside badge
3987  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3988  * @cfg {String} glyphicon name of glyphicon
3989  * @cfg {String} icon name of font awesome icon
3990  * @cfg {Boolean} active Is item active
3991  * @cfg {Boolean} disabled Is item disabled
3992  
3993  * @cfg {Boolean} preventDefault (true | false) default false
3994  * @cfg {String} tabId the tab that this item activates.
3995  * @cfg {String} tagtype (a|span) render as a href or span?
3996  * @cfg {Boolean} animateRef (true|false) link to element default false  
3997   
3998  * @constructor
3999  * Create a new Navbar Item
4000  * @param {Object} config The config object
4001  */
4002 Roo.bootstrap.NavItem = function(config){
4003     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4004     this.addEvents({
4005         // raw events
4006         /**
4007          * @event click
4008          * The raw click event for the entire grid.
4009          * @param {Roo.EventObject} e
4010          */
4011         "click" : true,
4012          /**
4013             * @event changed
4014             * Fires when the active item active state changes
4015             * @param {Roo.bootstrap.NavItem} this
4016             * @param {boolean} state the new state
4017              
4018          */
4019         'changed': true,
4020         /**
4021             * @event scrollto
4022             * Fires when scroll to element
4023             * @param {Roo.bootstrap.NavItem} this
4024             * @param {Object} options
4025             * @param {Roo.EventObject} e
4026              
4027          */
4028         'scrollto': true
4029     });
4030    
4031 };
4032
4033 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4034     
4035     href: false,
4036     html: '',
4037     badge: '',
4038     icon: false,
4039     glyphicon: false,
4040     active: false,
4041     preventDefault : false,
4042     tabId : false,
4043     tagtype : 'a',
4044     disabled : false,
4045     animateRef : false,
4046     was_active : false,
4047     
4048     getAutoCreate : function(){
4049          
4050         var cfg = {
4051             tag: 'li',
4052             cls: 'nav-item'
4053             
4054         }
4055         if (this.active) {
4056             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4057         }
4058         if (this.disabled) {
4059             cfg.cls += ' disabled';
4060         }
4061         
4062         if (this.href || this.html || this.glyphicon || this.icon) {
4063             cfg.cn = [
4064                 {
4065                     tag: this.tagtype,
4066                     href : this.href || "#",
4067                     html: this.html || ''
4068                 }
4069             ];
4070             
4071             if (this.icon) {
4072                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4073             }
4074
4075             if(this.glyphicon) {
4076                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4077             }
4078             
4079             if (this.menu) {
4080                 
4081                 cfg.cn[0].html += " <span class='caret'></span>";
4082              
4083             }
4084             
4085             if (this.badge !== '') {
4086                  
4087                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4088             }
4089         }
4090         
4091         
4092         
4093         return cfg;
4094     },
4095     initEvents: function() 
4096     {
4097         if (typeof (this.menu) != 'undefined') {
4098             this.menu.parentType = this.xtype;
4099             this.menu.triggerEl = this.el;
4100             this.menu = this.addxtype(Roo.apply({}, this.menu));
4101         }
4102         
4103         this.el.select('a',true).on('click', this.onClick, this);
4104         
4105         if(this.tagtype == 'span'){
4106             this.el.select('span',true).on('click', this.onClick, this);
4107         }
4108        
4109         // at this point parent should be available..
4110         this.parent().register(this);
4111     },
4112     
4113     onClick : function(e)
4114     {
4115         if(
4116                 this.preventDefault || 
4117                 this.href == '#' 
4118         ){
4119             
4120             e.preventDefault();
4121         }
4122         
4123         if (this.disabled) {
4124             return;
4125         }
4126         
4127         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4128         if (tg && tg.transition) {
4129             Roo.log("waiting for the transitionend");
4130             return;
4131         }
4132         
4133         
4134         
4135         //Roo.log("fire event clicked");
4136         if(this.fireEvent('click', this, e) === false){
4137             return;
4138         };
4139         
4140         if(this.tagtype == 'span'){
4141             return;
4142         }
4143         
4144         //Roo.log(this.href);
4145         var ael = this.el.select('a',true).first();
4146         //Roo.log(ael);
4147         
4148         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4149             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4150             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4151                 return; // ignore... - it's a 'hash' to another page.
4152             }
4153             
4154             e.preventDefault();
4155             this.scrollToElement(e);
4156         }
4157         
4158         
4159         var p =  this.parent();
4160    
4161         if (['tabs','pills'].indexOf(p.type)!==-1) {
4162             if (typeof(p.setActiveItem) !== 'undefined') {
4163                 p.setActiveItem(this);
4164             }
4165         }
4166         
4167         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4168         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4169             // remove the collapsed menu expand...
4170             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4171         }
4172     },
4173     
4174     isActive: function () {
4175         return this.active
4176     },
4177     setActive : function(state, fire, is_was_active)
4178     {
4179         if (this.active && !state & this.navId) {
4180             this.was_active = true;
4181             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4182             if (nv) {
4183                 nv.clearWasActive(this);
4184             }
4185             
4186         }
4187         this.active = state;
4188         
4189         if (!state ) {
4190             this.el.removeClass('active');
4191         } else if (!this.el.hasClass('active')) {
4192             this.el.addClass('active');
4193         }
4194         if (fire) {
4195             this.fireEvent('changed', this, state);
4196         }
4197         
4198         // show a panel if it's registered and related..
4199         
4200         if (!this.navId || !this.tabId || !state || is_was_active) {
4201             return;
4202         }
4203         
4204         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4205         if (!tg) {
4206             return;
4207         }
4208         var pan = tg.getPanelByName(this.tabId);
4209         if (!pan) {
4210             return;
4211         }
4212         // if we can not flip to new panel - go back to old nav highlight..
4213         if (false == tg.showPanel(pan)) {
4214             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4215             if (nv) {
4216                 var onav = nv.getWasActive();
4217                 if (onav) {
4218                     onav.setActive(true, false, true);
4219                 }
4220             }
4221             
4222         }
4223         
4224         
4225         
4226     },
4227      // this should not be here...
4228     setDisabled : function(state)
4229     {
4230         this.disabled = state;
4231         if (!state ) {
4232             this.el.removeClass('disabled');
4233         } else if (!this.el.hasClass('disabled')) {
4234             this.el.addClass('disabled');
4235         }
4236         
4237     },
4238     
4239     /**
4240      * Fetch the element to display the tooltip on.
4241      * @return {Roo.Element} defaults to this.el
4242      */
4243     tooltipEl : function()
4244     {
4245         return this.el.select('' + this.tagtype + '', true).first();
4246     },
4247     
4248     scrollToElement : function(e)
4249     {
4250         var c = document.body;
4251         
4252         /*
4253          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4254          */
4255         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4256             c = document.documentElement;
4257         }
4258         
4259         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4260         
4261         if(!target){
4262             return;
4263         }
4264
4265         var o = target.calcOffsetsTo(c);
4266         
4267         var options = {
4268             target : target,
4269             value : o[1]
4270         }
4271         
4272         this.fireEvent('scrollto', this, options, e);
4273         
4274         Roo.get(c).scrollTo('top', options.value, true);
4275         
4276         return;
4277     }
4278 });
4279  
4280
4281  /*
4282  * - LGPL
4283  *
4284  * sidebar item
4285  *
4286  *  li
4287  *    <span> icon </span>
4288  *    <span> text </span>
4289  *    <span>badge </span>
4290  */
4291
4292 /**
4293  * @class Roo.bootstrap.NavSidebarItem
4294  * @extends Roo.bootstrap.NavItem
4295  * Bootstrap Navbar.NavSidebarItem class
4296  * @constructor
4297  * Create a new Navbar Button
4298  * @param {Object} config The config object
4299  */
4300 Roo.bootstrap.NavSidebarItem = function(config){
4301     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4302     this.addEvents({
4303         // raw events
4304         /**
4305          * @event click
4306          * The raw click event for the entire grid.
4307          * @param {Roo.EventObject} e
4308          */
4309         "click" : true,
4310          /**
4311             * @event changed
4312             * Fires when the active item active state changes
4313             * @param {Roo.bootstrap.NavSidebarItem} this
4314             * @param {boolean} state the new state
4315              
4316          */
4317         'changed': true
4318     });
4319    
4320 };
4321
4322 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4323     
4324     
4325     getAutoCreate : function(){
4326         
4327         
4328         var a = {
4329                 tag: 'a',
4330                 href : this.href || '#',
4331                 cls: '',
4332                 html : '',
4333                 cn : []
4334         };
4335         var cfg = {
4336             tag: 'li',
4337             cls: '',
4338             cn: [ a ]
4339         }
4340         var span = {
4341             tag: 'span',
4342             html : this.html || ''
4343         }
4344         
4345         
4346         if (this.active) {
4347             cfg.cls += ' active';
4348         }
4349         
4350         // left icon..
4351         if (this.glyphicon || this.icon) {
4352             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4353             a.cn.push({ tag : 'i', cls : c }) ;
4354         }
4355         // html..
4356         a.cn.push(span);
4357         // then badge..
4358         if (this.badge !== '') {
4359             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4360         }
4361         // fi
4362         if (this.menu) {
4363             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4364             a.cls += 'dropdown-toggle treeview' ;
4365             
4366         }
4367         
4368         
4369         
4370         return cfg;
4371          
4372            
4373     }
4374    
4375      
4376  
4377 });
4378  
4379
4380  /*
4381  * - LGPL
4382  *
4383  * row
4384  * 
4385  */
4386
4387 /**
4388  * @class Roo.bootstrap.Row
4389  * @extends Roo.bootstrap.Component
4390  * Bootstrap Row class (contains columns...)
4391  * 
4392  * @constructor
4393  * Create a new Row
4394  * @param {Object} config The config object
4395  */
4396
4397 Roo.bootstrap.Row = function(config){
4398     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4399 };
4400
4401 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4402     
4403     getAutoCreate : function(){
4404        return {
4405             cls: 'row clearfix'
4406        };
4407     }
4408     
4409     
4410 });
4411
4412  
4413
4414  /*
4415  * - LGPL
4416  *
4417  * element
4418  * 
4419  */
4420
4421 /**
4422  * @class Roo.bootstrap.Element
4423  * @extends Roo.bootstrap.Component
4424  * Bootstrap Element class
4425  * @cfg {String} html contents of the element
4426  * @cfg {String} tag tag of the element
4427  * @cfg {String} cls class of the element
4428  * @cfg {Boolean} preventDefault (true|false) default false
4429  * @cfg {Boolean} clickable (true|false) default false
4430  * 
4431  * @constructor
4432  * Create a new Element
4433  * @param {Object} config The config object
4434  */
4435
4436 Roo.bootstrap.Element = function(config){
4437     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4438     
4439     this.addEvents({
4440         // raw events
4441         /**
4442          * @event click
4443          * When a element is chick
4444          * @param {Roo.bootstrap.Element} this
4445          * @param {Roo.EventObject} e
4446          */
4447         "click" : true
4448     });
4449 };
4450
4451 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4452     
4453     tag: 'div',
4454     cls: '',
4455     html: '',
4456     preventDefault: false, 
4457     clickable: false,
4458     
4459     getAutoCreate : function(){
4460         
4461         var cfg = {
4462             tag: this.tag,
4463             cls: this.cls,
4464             html: this.html
4465         }
4466         
4467         return cfg;
4468     },
4469     
4470     initEvents: function() 
4471     {
4472         Roo.bootstrap.Element.superclass.initEvents.call(this);
4473         
4474         if(this.clickable){
4475             this.el.on('click', this.onClick, this);
4476         }
4477         
4478     },
4479     
4480     onClick : function(e)
4481     {
4482         if(this.preventDefault){
4483             e.preventDefault();
4484         }
4485         
4486         this.fireEvent('click', this, e);
4487     },
4488     
4489     getValue : function()
4490     {
4491         return this.el.dom.innerHTML;
4492     },
4493     
4494     setValue : function(value)
4495     {
4496         this.el.dom.innerHTML = value;
4497     }
4498    
4499 });
4500
4501  
4502
4503  /*
4504  * - LGPL
4505  *
4506  * pagination
4507  * 
4508  */
4509
4510 /**
4511  * @class Roo.bootstrap.Pagination
4512  * @extends Roo.bootstrap.Component
4513  * Bootstrap Pagination class
4514  * @cfg {String} size xs | sm | md | lg
4515  * @cfg {Boolean} inverse false | true
4516  * 
4517  * @constructor
4518  * Create a new Pagination
4519  * @param {Object} config The config object
4520  */
4521
4522 Roo.bootstrap.Pagination = function(config){
4523     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4524 };
4525
4526 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4527     
4528     cls: false,
4529     size: false,
4530     inverse: false,
4531     
4532     getAutoCreate : function(){
4533         var cfg = {
4534             tag: 'ul',
4535                 cls: 'pagination'
4536         };
4537         if (this.inverse) {
4538             cfg.cls += ' inverse';
4539         }
4540         if (this.html) {
4541             cfg.html=this.html;
4542         }
4543         if (this.cls) {
4544             cfg.cls += " " + this.cls;
4545         }
4546         return cfg;
4547     }
4548    
4549 });
4550
4551  
4552
4553  /*
4554  * - LGPL
4555  *
4556  * Pagination item
4557  * 
4558  */
4559
4560
4561 /**
4562  * @class Roo.bootstrap.PaginationItem
4563  * @extends Roo.bootstrap.Component
4564  * Bootstrap PaginationItem class
4565  * @cfg {String} html text
4566  * @cfg {String} href the link
4567  * @cfg {Boolean} preventDefault (true | false) default true
4568  * @cfg {Boolean} active (true | false) default false
4569  * @cfg {Boolean} disabled default false
4570  * 
4571  * 
4572  * @constructor
4573  * Create a new PaginationItem
4574  * @param {Object} config The config object
4575  */
4576
4577
4578 Roo.bootstrap.PaginationItem = function(config){
4579     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4580     this.addEvents({
4581         // raw events
4582         /**
4583          * @event click
4584          * The raw click event for the entire grid.
4585          * @param {Roo.EventObject} e
4586          */
4587         "click" : true
4588     });
4589 };
4590
4591 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4592     
4593     href : false,
4594     html : false,
4595     preventDefault: true,
4596     active : false,
4597     cls : false,
4598     disabled: false,
4599     
4600     getAutoCreate : function(){
4601         var cfg= {
4602             tag: 'li',
4603             cn: [
4604                 {
4605                     tag : 'a',
4606                     href : this.href ? this.href : '#',
4607                     html : this.html ? this.html : ''
4608                 }
4609             ]
4610         };
4611         
4612         if(this.cls){
4613             cfg.cls = this.cls;
4614         }
4615         
4616         if(this.disabled){
4617             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4618         }
4619         
4620         if(this.active){
4621             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4622         }
4623         
4624         return cfg;
4625     },
4626     
4627     initEvents: function() {
4628         
4629         this.el.on('click', this.onClick, this);
4630         
4631     },
4632     onClick : function(e)
4633     {
4634         Roo.log('PaginationItem on click ');
4635         if(this.preventDefault){
4636             e.preventDefault();
4637         }
4638         
4639         if(this.disabled){
4640             return;
4641         }
4642         
4643         this.fireEvent('click', this, e);
4644     }
4645    
4646 });
4647
4648  
4649
4650  /*
4651  * - LGPL
4652  *
4653  * slider
4654  * 
4655  */
4656
4657
4658 /**
4659  * @class Roo.bootstrap.Slider
4660  * @extends Roo.bootstrap.Component
4661  * Bootstrap Slider class
4662  *    
4663  * @constructor
4664  * Create a new Slider
4665  * @param {Object} config The config object
4666  */
4667
4668 Roo.bootstrap.Slider = function(config){
4669     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4670 };
4671
4672 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4673     
4674     getAutoCreate : function(){
4675         
4676         var cfg = {
4677             tag: 'div',
4678             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4679             cn: [
4680                 {
4681                     tag: 'a',
4682                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4683                 }
4684             ]
4685         }
4686         
4687         return cfg;
4688     }
4689    
4690 });
4691
4692  /*
4693  * Based on:
4694  * Ext JS Library 1.1.1
4695  * Copyright(c) 2006-2007, Ext JS, LLC.
4696  *
4697  * Originally Released Under LGPL - original licence link has changed is not relivant.
4698  *
4699  * Fork - LGPL
4700  * <script type="text/javascript">
4701  */
4702  
4703
4704 /**
4705  * @class Roo.grid.ColumnModel
4706  * @extends Roo.util.Observable
4707  * This is the default implementation of a ColumnModel used by the Grid. It defines
4708  * the columns in the grid.
4709  * <br>Usage:<br>
4710  <pre><code>
4711  var colModel = new Roo.grid.ColumnModel([
4712         {header: "Ticker", width: 60, sortable: true, locked: true},
4713         {header: "Company Name", width: 150, sortable: true},
4714         {header: "Market Cap.", width: 100, sortable: true},
4715         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4716         {header: "Employees", width: 100, sortable: true, resizable: false}
4717  ]);
4718  </code></pre>
4719  * <p>
4720  
4721  * The config options listed for this class are options which may appear in each
4722  * individual column definition.
4723  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4724  * @constructor
4725  * @param {Object} config An Array of column config objects. See this class's
4726  * config objects for details.
4727 */
4728 Roo.grid.ColumnModel = function(config){
4729         /**
4730      * The config passed into the constructor
4731      */
4732     this.config = config;
4733     this.lookup = {};
4734
4735     // if no id, create one
4736     // if the column does not have a dataIndex mapping,
4737     // map it to the order it is in the config
4738     for(var i = 0, len = config.length; i < len; i++){
4739         var c = config[i];
4740         if(typeof c.dataIndex == "undefined"){
4741             c.dataIndex = i;
4742         }
4743         if(typeof c.renderer == "string"){
4744             c.renderer = Roo.util.Format[c.renderer];
4745         }
4746         if(typeof c.id == "undefined"){
4747             c.id = Roo.id();
4748         }
4749         if(c.editor && c.editor.xtype){
4750             c.editor  = Roo.factory(c.editor, Roo.grid);
4751         }
4752         if(c.editor && c.editor.isFormField){
4753             c.editor = new Roo.grid.GridEditor(c.editor);
4754         }
4755         this.lookup[c.id] = c;
4756     }
4757
4758     /**
4759      * The width of columns which have no width specified (defaults to 100)
4760      * @type Number
4761      */
4762     this.defaultWidth = 100;
4763
4764     /**
4765      * Default sortable of columns which have no sortable specified (defaults to false)
4766      * @type Boolean
4767      */
4768     this.defaultSortable = false;
4769
4770     this.addEvents({
4771         /**
4772              * @event widthchange
4773              * Fires when the width of a column changes.
4774              * @param {ColumnModel} this
4775              * @param {Number} columnIndex The column index
4776              * @param {Number} newWidth The new width
4777              */
4778             "widthchange": true,
4779         /**
4780              * @event headerchange
4781              * Fires when the text of a header changes.
4782              * @param {ColumnModel} this
4783              * @param {Number} columnIndex The column index
4784              * @param {Number} newText The new header text
4785              */
4786             "headerchange": true,
4787         /**
4788              * @event hiddenchange
4789              * Fires when a column is hidden or "unhidden".
4790              * @param {ColumnModel} this
4791              * @param {Number} columnIndex The column index
4792              * @param {Boolean} hidden true if hidden, false otherwise
4793              */
4794             "hiddenchange": true,
4795             /**
4796          * @event columnmoved
4797          * Fires when a column is moved.
4798          * @param {ColumnModel} this
4799          * @param {Number} oldIndex
4800          * @param {Number} newIndex
4801          */
4802         "columnmoved" : true,
4803         /**
4804          * @event columlockchange
4805          * Fires when a column's locked state is changed
4806          * @param {ColumnModel} this
4807          * @param {Number} colIndex
4808          * @param {Boolean} locked true if locked
4809          */
4810         "columnlockchange" : true
4811     });
4812     Roo.grid.ColumnModel.superclass.constructor.call(this);
4813 };
4814 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4815     /**
4816      * @cfg {String} header The header text to display in the Grid view.
4817      */
4818     /**
4819      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4820      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4821      * specified, the column's index is used as an index into the Record's data Array.
4822      */
4823     /**
4824      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4825      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4826      */
4827     /**
4828      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4829      * Defaults to the value of the {@link #defaultSortable} property.
4830      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4831      */
4832     /**
4833      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4834      */
4835     /**
4836      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4837      */
4838     /**
4839      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4840      */
4841     /**
4842      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4843      */
4844     /**
4845      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4846      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4847      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4848      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4849      */
4850        /**
4851      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4852      */
4853     /**
4854      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4855      */
4856     /**
4857      * @cfg {String} cursor (Optional)
4858      */
4859     /**
4860      * @cfg {String} tooltip (Optional)
4861      */
4862     /**
4863      * Returns the id of the column at the specified index.
4864      * @param {Number} index The column index
4865      * @return {String} the id
4866      */
4867     getColumnId : function(index){
4868         return this.config[index].id;
4869     },
4870
4871     /**
4872      * Returns the column for a specified id.
4873      * @param {String} id The column id
4874      * @return {Object} the column
4875      */
4876     getColumnById : function(id){
4877         return this.lookup[id];
4878     },
4879
4880     
4881     /**
4882      * Returns the column for a specified dataIndex.
4883      * @param {String} dataIndex The column dataIndex
4884      * @return {Object|Boolean} the column or false if not found
4885      */
4886     getColumnByDataIndex: function(dataIndex){
4887         var index = this.findColumnIndex(dataIndex);
4888         return index > -1 ? this.config[index] : false;
4889     },
4890     
4891     /**
4892      * Returns the index for a specified column id.
4893      * @param {String} id The column id
4894      * @return {Number} the index, or -1 if not found
4895      */
4896     getIndexById : function(id){
4897         for(var i = 0, len = this.config.length; i < len; i++){
4898             if(this.config[i].id == id){
4899                 return i;
4900             }
4901         }
4902         return -1;
4903     },
4904     
4905     /**
4906      * Returns the index for a specified column dataIndex.
4907      * @param {String} dataIndex The column dataIndex
4908      * @return {Number} the index, or -1 if not found
4909      */
4910     
4911     findColumnIndex : function(dataIndex){
4912         for(var i = 0, len = this.config.length; i < len; i++){
4913             if(this.config[i].dataIndex == dataIndex){
4914                 return i;
4915             }
4916         }
4917         return -1;
4918     },
4919     
4920     
4921     moveColumn : function(oldIndex, newIndex){
4922         var c = this.config[oldIndex];
4923         this.config.splice(oldIndex, 1);
4924         this.config.splice(newIndex, 0, c);
4925         this.dataMap = null;
4926         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4927     },
4928
4929     isLocked : function(colIndex){
4930         return this.config[colIndex].locked === true;
4931     },
4932
4933     setLocked : function(colIndex, value, suppressEvent){
4934         if(this.isLocked(colIndex) == value){
4935             return;
4936         }
4937         this.config[colIndex].locked = value;
4938         if(!suppressEvent){
4939             this.fireEvent("columnlockchange", this, colIndex, value);
4940         }
4941     },
4942
4943     getTotalLockedWidth : function(){
4944         var totalWidth = 0;
4945         for(var i = 0; i < this.config.length; i++){
4946             if(this.isLocked(i) && !this.isHidden(i)){
4947                 this.totalWidth += this.getColumnWidth(i);
4948             }
4949         }
4950         return totalWidth;
4951     },
4952
4953     getLockedCount : function(){
4954         for(var i = 0, len = this.config.length; i < len; i++){
4955             if(!this.isLocked(i)){
4956                 return i;
4957             }
4958         }
4959     },
4960
4961     /**
4962      * Returns the number of columns.
4963      * @return {Number}
4964      */
4965     getColumnCount : function(visibleOnly){
4966         if(visibleOnly === true){
4967             var c = 0;
4968             for(var i = 0, len = this.config.length; i < len; i++){
4969                 if(!this.isHidden(i)){
4970                     c++;
4971                 }
4972             }
4973             return c;
4974         }
4975         return this.config.length;
4976     },
4977
4978     /**
4979      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4980      * @param {Function} fn
4981      * @param {Object} scope (optional)
4982      * @return {Array} result
4983      */
4984     getColumnsBy : function(fn, scope){
4985         var r = [];
4986         for(var i = 0, len = this.config.length; i < len; i++){
4987             var c = this.config[i];
4988             if(fn.call(scope||this, c, i) === true){
4989                 r[r.length] = c;
4990             }
4991         }
4992         return r;
4993     },
4994
4995     /**
4996      * Returns true if the specified column is sortable.
4997      * @param {Number} col The column index
4998      * @return {Boolean}
4999      */
5000     isSortable : function(col){
5001         if(typeof this.config[col].sortable == "undefined"){
5002             return this.defaultSortable;
5003         }
5004         return this.config[col].sortable;
5005     },
5006
5007     /**
5008      * Returns the rendering (formatting) function defined for the column.
5009      * @param {Number} col The column index.
5010      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5011      */
5012     getRenderer : function(col){
5013         if(!this.config[col].renderer){
5014             return Roo.grid.ColumnModel.defaultRenderer;
5015         }
5016         return this.config[col].renderer;
5017     },
5018
5019     /**
5020      * Sets the rendering (formatting) function for a column.
5021      * @param {Number} col The column index
5022      * @param {Function} fn The function to use to process the cell's raw data
5023      * to return HTML markup for the grid view. The render function is called with
5024      * the following parameters:<ul>
5025      * <li>Data value.</li>
5026      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5027      * <li>css A CSS style string to apply to the table cell.</li>
5028      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5029      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5030      * <li>Row index</li>
5031      * <li>Column index</li>
5032      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5033      */
5034     setRenderer : function(col, fn){
5035         this.config[col].renderer = fn;
5036     },
5037
5038     /**
5039      * Returns the width for the specified column.
5040      * @param {Number} col The column index
5041      * @return {Number}
5042      */
5043     getColumnWidth : function(col){
5044         return this.config[col].width * 1 || this.defaultWidth;
5045     },
5046
5047     /**
5048      * Sets the width for a column.
5049      * @param {Number} col The column index
5050      * @param {Number} width The new width
5051      */
5052     setColumnWidth : function(col, width, suppressEvent){
5053         this.config[col].width = width;
5054         this.totalWidth = null;
5055         if(!suppressEvent){
5056              this.fireEvent("widthchange", this, col, width);
5057         }
5058     },
5059
5060     /**
5061      * Returns the total width of all columns.
5062      * @param {Boolean} includeHidden True to include hidden column widths
5063      * @return {Number}
5064      */
5065     getTotalWidth : function(includeHidden){
5066         if(!this.totalWidth){
5067             this.totalWidth = 0;
5068             for(var i = 0, len = this.config.length; i < len; i++){
5069                 if(includeHidden || !this.isHidden(i)){
5070                     this.totalWidth += this.getColumnWidth(i);
5071                 }
5072             }
5073         }
5074         return this.totalWidth;
5075     },
5076
5077     /**
5078      * Returns the header for the specified column.
5079      * @param {Number} col The column index
5080      * @return {String}
5081      */
5082     getColumnHeader : function(col){
5083         return this.config[col].header;
5084     },
5085
5086     /**
5087      * Sets the header for a column.
5088      * @param {Number} col The column index
5089      * @param {String} header The new header
5090      */
5091     setColumnHeader : function(col, header){
5092         this.config[col].header = header;
5093         this.fireEvent("headerchange", this, col, header);
5094     },
5095
5096     /**
5097      * Returns the tooltip for the specified column.
5098      * @param {Number} col The column index
5099      * @return {String}
5100      */
5101     getColumnTooltip : function(col){
5102             return this.config[col].tooltip;
5103     },
5104     /**
5105      * Sets the tooltip for a column.
5106      * @param {Number} col The column index
5107      * @param {String} tooltip The new tooltip
5108      */
5109     setColumnTooltip : function(col, tooltip){
5110             this.config[col].tooltip = tooltip;
5111     },
5112
5113     /**
5114      * Returns the dataIndex for the specified column.
5115      * @param {Number} col The column index
5116      * @return {Number}
5117      */
5118     getDataIndex : function(col){
5119         return this.config[col].dataIndex;
5120     },
5121
5122     /**
5123      * Sets the dataIndex for a column.
5124      * @param {Number} col The column index
5125      * @param {Number} dataIndex The new dataIndex
5126      */
5127     setDataIndex : function(col, dataIndex){
5128         this.config[col].dataIndex = dataIndex;
5129     },
5130
5131     
5132     
5133     /**
5134      * Returns true if the cell is editable.
5135      * @param {Number} colIndex The column index
5136      * @param {Number} rowIndex The row index
5137      * @return {Boolean}
5138      */
5139     isCellEditable : function(colIndex, rowIndex){
5140         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5141     },
5142
5143     /**
5144      * Returns the editor defined for the cell/column.
5145      * return false or null to disable editing.
5146      * @param {Number} colIndex The column index
5147      * @param {Number} rowIndex The row index
5148      * @return {Object}
5149      */
5150     getCellEditor : function(colIndex, rowIndex){
5151         return this.config[colIndex].editor;
5152     },
5153
5154     /**
5155      * Sets if a column is editable.
5156      * @param {Number} col The column index
5157      * @param {Boolean} editable True if the column is editable
5158      */
5159     setEditable : function(col, editable){
5160         this.config[col].editable = editable;
5161     },
5162
5163
5164     /**
5165      * Returns true if the column is hidden.
5166      * @param {Number} colIndex The column index
5167      * @return {Boolean}
5168      */
5169     isHidden : function(colIndex){
5170         return this.config[colIndex].hidden;
5171     },
5172
5173
5174     /**
5175      * Returns true if the column width cannot be changed
5176      */
5177     isFixed : function(colIndex){
5178         return this.config[colIndex].fixed;
5179     },
5180
5181     /**
5182      * Returns true if the column can be resized
5183      * @return {Boolean}
5184      */
5185     isResizable : function(colIndex){
5186         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5187     },
5188     /**
5189      * Sets if a column is hidden.
5190      * @param {Number} colIndex The column index
5191      * @param {Boolean} hidden True if the column is hidden
5192      */
5193     setHidden : function(colIndex, hidden){
5194         this.config[colIndex].hidden = hidden;
5195         this.totalWidth = null;
5196         this.fireEvent("hiddenchange", this, colIndex, hidden);
5197     },
5198
5199     /**
5200      * Sets the editor for a column.
5201      * @param {Number} col The column index
5202      * @param {Object} editor The editor object
5203      */
5204     setEditor : function(col, editor){
5205         this.config[col].editor = editor;
5206     }
5207 });
5208
5209 Roo.grid.ColumnModel.defaultRenderer = function(value){
5210         if(typeof value == "string" && value.length < 1){
5211             return "&#160;";
5212         }
5213         return value;
5214 };
5215
5216 // Alias for backwards compatibility
5217 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5218 /*
5219  * Based on:
5220  * Ext JS Library 1.1.1
5221  * Copyright(c) 2006-2007, Ext JS, LLC.
5222  *
5223  * Originally Released Under LGPL - original licence link has changed is not relivant.
5224  *
5225  * Fork - LGPL
5226  * <script type="text/javascript">
5227  */
5228  
5229 /**
5230  * @class Roo.LoadMask
5231  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5232  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5233  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5234  * element's UpdateManager load indicator and will be destroyed after the initial load.
5235  * @constructor
5236  * Create a new LoadMask
5237  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5238  * @param {Object} config The config object
5239  */
5240 Roo.LoadMask = function(el, config){
5241     this.el = Roo.get(el);
5242     Roo.apply(this, config);
5243     if(this.store){
5244         this.store.on('beforeload', this.onBeforeLoad, this);
5245         this.store.on('load', this.onLoad, this);
5246         this.store.on('loadexception', this.onLoadException, this);
5247         this.removeMask = false;
5248     }else{
5249         var um = this.el.getUpdateManager();
5250         um.showLoadIndicator = false; // disable the default indicator
5251         um.on('beforeupdate', this.onBeforeLoad, this);
5252         um.on('update', this.onLoad, this);
5253         um.on('failure', this.onLoad, this);
5254         this.removeMask = true;
5255     }
5256 };
5257
5258 Roo.LoadMask.prototype = {
5259     /**
5260      * @cfg {Boolean} removeMask
5261      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5262      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5263      */
5264     /**
5265      * @cfg {String} msg
5266      * The text to display in a centered loading message box (defaults to 'Loading...')
5267      */
5268     msg : 'Loading...',
5269     /**
5270      * @cfg {String} msgCls
5271      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5272      */
5273     msgCls : 'x-mask-loading',
5274
5275     /**
5276      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5277      * @type Boolean
5278      */
5279     disabled: false,
5280
5281     /**
5282      * Disables the mask to prevent it from being displayed
5283      */
5284     disable : function(){
5285        this.disabled = true;
5286     },
5287
5288     /**
5289      * Enables the mask so that it can be displayed
5290      */
5291     enable : function(){
5292         this.disabled = false;
5293     },
5294     
5295     onLoadException : function()
5296     {
5297         Roo.log(arguments);
5298         
5299         if (typeof(arguments[3]) != 'undefined') {
5300             Roo.MessageBox.alert("Error loading",arguments[3]);
5301         } 
5302         /*
5303         try {
5304             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5305                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5306             }   
5307         } catch(e) {
5308             
5309         }
5310         */
5311     
5312         
5313         
5314         this.el.unmask(this.removeMask);
5315     },
5316     // private
5317     onLoad : function()
5318     {
5319         this.el.unmask(this.removeMask);
5320     },
5321
5322     // private
5323     onBeforeLoad : function(){
5324         if(!this.disabled){
5325             this.el.mask(this.msg, this.msgCls);
5326         }
5327     },
5328
5329     // private
5330     destroy : function(){
5331         if(this.store){
5332             this.store.un('beforeload', this.onBeforeLoad, this);
5333             this.store.un('load', this.onLoad, this);
5334             this.store.un('loadexception', this.onLoadException, this);
5335         }else{
5336             var um = this.el.getUpdateManager();
5337             um.un('beforeupdate', this.onBeforeLoad, this);
5338             um.un('update', this.onLoad, this);
5339             um.un('failure', this.onLoad, this);
5340         }
5341     }
5342 };/*
5343  * - LGPL
5344  *
5345  * table
5346  * 
5347  */
5348
5349 /**
5350  * @class Roo.bootstrap.Table
5351  * @extends Roo.bootstrap.Component
5352  * Bootstrap Table class
5353  * @cfg {String} cls table class
5354  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5355  * @cfg {String} bgcolor Specifies the background color for a table
5356  * @cfg {Number} border Specifies whether the table cells should have borders or not
5357  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5358  * @cfg {Number} cellspacing Specifies the space between cells
5359  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5360  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5361  * @cfg {String} sortable Specifies that the table should be sortable
5362  * @cfg {String} summary Specifies a summary of the content of a table
5363  * @cfg {Number} width Specifies the width of a table
5364  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5365  * 
5366  * @cfg {boolean} striped Should the rows be alternative striped
5367  * @cfg {boolean} bordered Add borders to the table
5368  * @cfg {boolean} hover Add hover highlighting
5369  * @cfg {boolean} condensed Format condensed
5370  * @cfg {boolean} responsive Format condensed
5371  * @cfg {Boolean} loadMask (true|false) default false
5372  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5373  * @cfg {Boolean} thead (true|false) generate thead, default true
5374  * @cfg {Boolean} RowSelection (true|false) default false
5375  * @cfg {Boolean} CellSelection (true|false) default false
5376  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5377  
5378  * 
5379  * @constructor
5380  * Create a new Table
5381  * @param {Object} config The config object
5382  */
5383
5384 Roo.bootstrap.Table = function(config){
5385     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5386     
5387     if (this.sm) {
5388         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5389         this.sm = this.selModel;
5390         this.sm.xmodule = this.xmodule || false;
5391     }
5392     if (this.cm && typeof(this.cm.config) == 'undefined') {
5393         this.colModel = new Roo.grid.ColumnModel(this.cm);
5394         this.cm = this.colModel;
5395         this.cm.xmodule = this.xmodule || false;
5396     }
5397     if (this.store) {
5398         this.store= Roo.factory(this.store, Roo.data);
5399         this.ds = this.store;
5400         this.ds.xmodule = this.xmodule || false;
5401          
5402     }
5403     if (this.footer && this.store) {
5404         this.footer.dataSource = this.ds;
5405         this.footer = Roo.factory(this.footer);
5406     }
5407     
5408     /** @private */
5409     this.addEvents({
5410         /**
5411          * @event cellclick
5412          * Fires when a cell is clicked
5413          * @param {Roo.bootstrap.Table} this
5414          * @param {Roo.Element} el
5415          * @param {Number} rowIndex
5416          * @param {Number} columnIndex
5417          * @param {Roo.EventObject} e
5418          */
5419         "cellclick" : true,
5420         /**
5421          * @event celldblclick
5422          * Fires when a cell is double clicked
5423          * @param {Roo.bootstrap.Table} this
5424          * @param {Roo.Element} el
5425          * @param {Number} rowIndex
5426          * @param {Number} columnIndex
5427          * @param {Roo.EventObject} e
5428          */
5429         "celldblclick" : true,
5430         /**
5431          * @event rowclick
5432          * Fires when a row is clicked
5433          * @param {Roo.bootstrap.Table} this
5434          * @param {Roo.Element} el
5435          * @param {Number} rowIndex
5436          * @param {Roo.EventObject} e
5437          */
5438         "rowclick" : true,
5439         /**
5440          * @event rowdblclick
5441          * Fires when a row is double clicked
5442          * @param {Roo.bootstrap.Table} this
5443          * @param {Roo.Element} el
5444          * @param {Number} rowIndex
5445          * @param {Roo.EventObject} e
5446          */
5447         "rowdblclick" : true,
5448         /**
5449          * @event mouseover
5450          * Fires when a mouseover occur
5451          * @param {Roo.bootstrap.Table} this
5452          * @param {Roo.Element} el
5453          * @param {Number} rowIndex
5454          * @param {Number} columnIndex
5455          * @param {Roo.EventObject} e
5456          */
5457         "mouseover" : true,
5458         /**
5459          * @event mouseout
5460          * Fires when a mouseout occur
5461          * @param {Roo.bootstrap.Table} this
5462          * @param {Roo.Element} el
5463          * @param {Number} rowIndex
5464          * @param {Number} columnIndex
5465          * @param {Roo.EventObject} e
5466          */
5467         "mouseout" : true,
5468         /**
5469          * @event rowclass
5470          * Fires when a row is rendered, so you can change add a style to it.
5471          * @param {Roo.bootstrap.Table} this
5472          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5473          */
5474         'rowclass' : true,
5475           /**
5476          * @event rowsrendered
5477          * Fires when all the  rows have been rendered
5478          * @param {Roo.bootstrap.Table} this
5479          */
5480         'rowsrendered' : true
5481         
5482     });
5483 };
5484
5485 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5486     
5487     cls: false,
5488     align: false,
5489     bgcolor: false,
5490     border: false,
5491     cellpadding: false,
5492     cellspacing: false,
5493     frame: false,
5494     rules: false,
5495     sortable: false,
5496     summary: false,
5497     width: false,
5498     striped : false,
5499     bordered: false,
5500     hover:  false,
5501     condensed : false,
5502     responsive : false,
5503     sm : false,
5504     cm : false,
5505     store : false,
5506     loadMask : false,
5507     tfoot : true,
5508     thead : true,
5509     RowSelection : false,
5510     CellSelection : false,
5511     layout : false,
5512     
5513     // Roo.Element - the tbody
5514     mainBody: false, 
5515     
5516     getAutoCreate : function(){
5517         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5518         
5519         cfg = {
5520             tag: 'table',
5521             cls : 'table',
5522             cn : []
5523         }
5524             
5525         if (this.striped) {
5526             cfg.cls += ' table-striped';
5527         }
5528         
5529         if (this.hover) {
5530             cfg.cls += ' table-hover';
5531         }
5532         if (this.bordered) {
5533             cfg.cls += ' table-bordered';
5534         }
5535         if (this.condensed) {
5536             cfg.cls += ' table-condensed';
5537         }
5538         if (this.responsive) {
5539             cfg.cls += ' table-responsive';
5540         }
5541         
5542         if (this.cls) {
5543             cfg.cls+=  ' ' +this.cls;
5544         }
5545         
5546         // this lot should be simplifed...
5547         
5548         if (this.align) {
5549             cfg.align=this.align;
5550         }
5551         if (this.bgcolor) {
5552             cfg.bgcolor=this.bgcolor;
5553         }
5554         if (this.border) {
5555             cfg.border=this.border;
5556         }
5557         if (this.cellpadding) {
5558             cfg.cellpadding=this.cellpadding;
5559         }
5560         if (this.cellspacing) {
5561             cfg.cellspacing=this.cellspacing;
5562         }
5563         if (this.frame) {
5564             cfg.frame=this.frame;
5565         }
5566         if (this.rules) {
5567             cfg.rules=this.rules;
5568         }
5569         if (this.sortable) {
5570             cfg.sortable=this.sortable;
5571         }
5572         if (this.summary) {
5573             cfg.summary=this.summary;
5574         }
5575         if (this.width) {
5576             cfg.width=this.width;
5577         }
5578         if (this.layout) {
5579             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5580         }
5581         
5582         if(this.store || this.cm){
5583             if(this.thead){
5584                 cfg.cn.push(this.renderHeader());
5585             }
5586             
5587             cfg.cn.push(this.renderBody());
5588             
5589             if(this.tfoot){
5590                 cfg.cn.push(this.renderFooter());
5591             }
5592             
5593             cfg.cls+=  ' TableGrid';
5594         }
5595         
5596         return { cn : [ cfg ] };
5597     },
5598     
5599     initEvents : function()
5600     {   
5601         if(!this.store || !this.cm){
5602             return;
5603         }
5604         
5605         //Roo.log('initEvents with ds!!!!');
5606         
5607         this.mainBody = this.el.select('tbody', true).first();
5608         
5609         
5610         var _this = this;
5611         
5612         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5613             e.on('click', _this.sort, _this);
5614         });
5615         
5616         this.el.on("click", this.onClick, this);
5617         this.el.on("dblclick", this.onDblClick, this);
5618         
5619         // why is this done????? = it breaks dialogs??
5620         //this.parent().el.setStyle('position', 'relative');
5621         
5622         
5623         if (this.footer) {
5624             this.footer.parentId = this.id;
5625             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5626         }
5627         
5628         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5629         
5630         this.store.on('load', this.onLoad, this);
5631         this.store.on('beforeload', this.onBeforeLoad, this);
5632         this.store.on('update', this.onUpdate, this);
5633         this.store.on('add', this.onAdd, this);
5634         
5635     },
5636     
5637     onMouseover : function(e, el)
5638     {
5639         var cell = Roo.get(el);
5640         
5641         if(!cell){
5642             return;
5643         }
5644         
5645         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5646             cell = cell.findParent('td', false, true);
5647         }
5648         
5649         var row = cell.findParent('tr', false, true);
5650         var cellIndex = cell.dom.cellIndex;
5651         var rowIndex = row.dom.rowIndex - 1; // start from 0
5652         
5653         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5654         
5655     },
5656     
5657     onMouseout : function(e, el)
5658     {
5659         var cell = Roo.get(el);
5660         
5661         if(!cell){
5662             return;
5663         }
5664         
5665         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5666             cell = cell.findParent('td', false, true);
5667         }
5668         
5669         var row = cell.findParent('tr', false, true);
5670         var cellIndex = cell.dom.cellIndex;
5671         var rowIndex = row.dom.rowIndex - 1; // start from 0
5672         
5673         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5674         
5675     },
5676     
5677     onClick : function(e, el)
5678     {
5679         var cell = Roo.get(el);
5680         
5681         if(!cell || (!this.CellSelection && !this.RowSelection)){
5682             return;
5683         }
5684         
5685         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5686             cell = cell.findParent('td', false, true);
5687         }
5688         
5689         if(!cell || typeof(cell) == 'undefined'){
5690             return;
5691         }
5692         
5693         var row = cell.findParent('tr', false, true);
5694         
5695         if(!row || typeof(row) == 'undefined'){
5696             return;
5697         }
5698         
5699         var cellIndex = cell.dom.cellIndex;
5700         var rowIndex = this.getRowIndex(row);
5701         
5702         if(this.CellSelection){
5703             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5704         }
5705         
5706         if(this.RowSelection){
5707             this.fireEvent('rowclick', this, row, rowIndex, e);
5708         }
5709         
5710         
5711     },
5712     
5713     onDblClick : function(e,el)
5714     {
5715         var cell = Roo.get(el);
5716         
5717         if(!cell || (!this.CellSelection && !this.RowSelection)){
5718             return;
5719         }
5720         
5721         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5722             cell = cell.findParent('td', false, true);
5723         }
5724         
5725         if(!cell || typeof(cell) == 'undefined'){
5726             return;
5727         }
5728         
5729         var row = cell.findParent('tr', false, true);
5730         
5731         if(!row || typeof(row) == 'undefined'){
5732             return;
5733         }
5734         
5735         var cellIndex = cell.dom.cellIndex;
5736         var rowIndex = this.getRowIndex(row);
5737         
5738         if(this.CellSelection){
5739             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5740         }
5741         
5742         if(this.RowSelection){
5743             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5744         }
5745     },
5746     
5747     sort : function(e,el)
5748     {
5749         var col = Roo.get(el);
5750         
5751         if(!col.hasClass('sortable')){
5752             return;
5753         }
5754         
5755         var sort = col.attr('sort');
5756         var dir = 'ASC';
5757         
5758         if(col.hasClass('glyphicon-arrow-up')){
5759             dir = 'DESC';
5760         }
5761         
5762         this.store.sortInfo = {field : sort, direction : dir};
5763         
5764         if (this.footer) {
5765             Roo.log("calling footer first");
5766             this.footer.onClick('first');
5767         } else {
5768         
5769             this.store.load({ params : { start : 0 } });
5770         }
5771     },
5772     
5773     renderHeader : function()
5774     {
5775         var header = {
5776             tag: 'thead',
5777             cn : []
5778         };
5779         
5780         var cm = this.cm;
5781         
5782         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5783             
5784             var config = cm.config[i];
5785                     
5786             var c = {
5787                 tag: 'th',
5788                 style : '',
5789                 html: cm.getColumnHeader(i)
5790             };
5791             
5792             if(typeof(config.tooltip) != 'undefined'){
5793                 c.tooltip = config.tooltip;
5794             }
5795             
5796             if(typeof(config.colspan) != 'undefined'){
5797                 c.colspan = config.colspan;
5798             }
5799             
5800             if(typeof(config.hidden) != 'undefined' && config.hidden){
5801                 c.style += ' display:none;';
5802             }
5803             
5804             if(typeof(config.dataIndex) != 'undefined'){
5805                 c.sort = config.dataIndex;
5806             }
5807             
5808             if(typeof(config.sortable) != 'undefined' && config.sortable){
5809                 c.cls = 'sortable';
5810             }
5811             
5812             if(typeof(config.align) != 'undefined' && config.align.length){
5813                 c.style += ' text-align:' + config.align + ';';
5814             }
5815             
5816             if(typeof(config.width) != 'undefined'){
5817                 c.style += ' width:' + config.width + 'px;';
5818             }
5819             
5820             if(typeof(config.cls) != 'undefined'){
5821                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5822             }
5823             
5824             header.cn.push(c)
5825         }
5826         
5827         return header;
5828     },
5829     
5830     renderBody : function()
5831     {
5832         var body = {
5833             tag: 'tbody',
5834             cn : [
5835                 {
5836                     tag: 'tr',
5837                     cn : [
5838                         {
5839                             tag : 'td',
5840                             colspan :  this.cm.getColumnCount()
5841                         }
5842                     ]
5843                 }
5844             ]
5845         };
5846         
5847         return body;
5848     },
5849     
5850     renderFooter : function()
5851     {
5852         var footer = {
5853             tag: 'tfoot',
5854             cn : [
5855                 {
5856                     tag: 'tr',
5857                     cn : [
5858                         {
5859                             tag : 'td',
5860                             colspan :  this.cm.getColumnCount()
5861                         }
5862                     ]
5863                 }
5864             ]
5865         };
5866         
5867         return footer;
5868     },
5869     
5870     
5871     
5872     onLoad : function()
5873     {
5874         Roo.log('ds onload');
5875         this.clear();
5876         
5877         var _this = this;
5878         var cm = this.cm;
5879         var ds = this.store;
5880         
5881         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5882             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5883             
5884             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5885                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5886             }
5887             
5888             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5889                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5890             }
5891         });
5892         
5893         var tbody =  this.mainBody;
5894               
5895         if(ds.getCount() > 0){
5896             ds.data.each(function(d,rowIndex){
5897                 var row =  this.renderRow(cm, ds, rowIndex);
5898                 
5899                 tbody.createChild(row);
5900                 
5901                 var _this = this;
5902                 
5903                 if(row.cellObjects.length){
5904                     Roo.each(row.cellObjects, function(r){
5905                         _this.renderCellObject(r);
5906                     })
5907                 }
5908                 
5909             }, this);
5910         }
5911         
5912         Roo.each(this.el.select('tbody td', true).elements, function(e){
5913             e.on('mouseover', _this.onMouseover, _this);
5914         });
5915         
5916         Roo.each(this.el.select('tbody td', true).elements, function(e){
5917             e.on('mouseout', _this.onMouseout, _this);
5918         });
5919         this.fireEvent('rowsrendered', this);
5920         //if(this.loadMask){
5921         //    this.maskEl.hide();
5922         //}
5923     },
5924     
5925     
5926     onUpdate : function(ds,record)
5927     {
5928         this.refreshRow(record);
5929     },
5930     
5931     onRemove : function(ds, record, index, isUpdate){
5932         if(isUpdate !== true){
5933             this.fireEvent("beforerowremoved", this, index, record);
5934         }
5935         var bt = this.mainBody.dom;
5936         
5937         var rows = this.el.select('tbody > tr', true).elements;
5938         
5939         if(typeof(rows[index]) != 'undefined'){
5940             bt.removeChild(rows[index].dom);
5941         }
5942         
5943 //        if(bt.rows[index]){
5944 //            bt.removeChild(bt.rows[index]);
5945 //        }
5946         
5947         if(isUpdate !== true){
5948             //this.stripeRows(index);
5949             //this.syncRowHeights(index, index);
5950             //this.layout();
5951             this.fireEvent("rowremoved", this, index, record);
5952         }
5953     },
5954     
5955     onAdd : function(ds, records, rowIndex)
5956     {
5957         //Roo.log('on Add called');
5958         // - note this does not handle multiple adding very well..
5959         var bt = this.mainBody.dom;
5960         for (var i =0 ; i < records.length;i++) {
5961             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5962             //Roo.log(records[i]);
5963             //Roo.log(this.store.getAt(rowIndex+i));
5964             this.insertRow(this.store, rowIndex + i, false);
5965             return;
5966         }
5967         
5968     },
5969     
5970     
5971     refreshRow : function(record){
5972         var ds = this.store, index;
5973         if(typeof record == 'number'){
5974             index = record;
5975             record = ds.getAt(index);
5976         }else{
5977             index = ds.indexOf(record);
5978         }
5979         this.insertRow(ds, index, true);
5980         this.onRemove(ds, record, index+1, true);
5981         //this.syncRowHeights(index, index);
5982         //this.layout();
5983         this.fireEvent("rowupdated", this, index, record);
5984     },
5985     
5986     insertRow : function(dm, rowIndex, isUpdate){
5987         
5988         if(!isUpdate){
5989             this.fireEvent("beforerowsinserted", this, rowIndex);
5990         }
5991             //var s = this.getScrollState();
5992         var row = this.renderRow(this.cm, this.store, rowIndex);
5993         // insert before rowIndex..
5994         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5995         
5996         var _this = this;
5997                 
5998         if(row.cellObjects.length){
5999             Roo.each(row.cellObjects, function(r){
6000                 _this.renderCellObject(r);
6001             })
6002         }
6003             
6004         if(!isUpdate){
6005             this.fireEvent("rowsinserted", this, rowIndex);
6006             //this.syncRowHeights(firstRow, lastRow);
6007             //this.stripeRows(firstRow);
6008             //this.layout();
6009         }
6010         
6011     },
6012     
6013     
6014     getRowDom : function(rowIndex)
6015     {
6016         var rows = this.el.select('tbody > tr', true).elements;
6017         
6018         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6019         
6020     },
6021     // returns the object tree for a tr..
6022   
6023     
6024     renderRow : function(cm, ds, rowIndex) 
6025     {
6026         
6027         var d = ds.getAt(rowIndex);
6028         
6029         var row = {
6030             tag : 'tr',
6031             cn : []
6032         };
6033             
6034         var cellObjects = [];
6035         
6036         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6037             var config = cm.config[i];
6038             
6039             var renderer = cm.getRenderer(i);
6040             var value = '';
6041             var id = false;
6042             
6043             if(typeof(renderer) !== 'undefined'){
6044                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6045             }
6046             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6047             // and are rendered into the cells after the row is rendered - using the id for the element.
6048             
6049             if(typeof(value) === 'object'){
6050                 id = Roo.id();
6051                 cellObjects.push({
6052                     container : id,
6053                     cfg : value 
6054                 })
6055             }
6056             
6057             var rowcfg = {
6058                 record: d,
6059                 rowIndex : rowIndex,
6060                 colIndex : i,
6061                 rowClass : ''
6062             }
6063
6064             this.fireEvent('rowclass', this, rowcfg);
6065             
6066             var td = {
6067                 tag: 'td',
6068                 cls : rowcfg.rowClass,
6069                 style: '',
6070                 html: (typeof(value) === 'object') ? '' : value
6071             };
6072             
6073             if (id) {
6074                 td.id = id;
6075             }
6076             
6077             if(typeof(config.colspan) != 'undefined'){
6078                 td.colspan = config.colspan;
6079             }
6080             
6081             if(typeof(config.hidden) != 'undefined' && config.hidden){
6082                 td.style += ' display:none;';
6083             }
6084             
6085             if(typeof(config.align) != 'undefined' && config.align.length){
6086                 td.style += ' text-align:' + config.align + ';';
6087             }
6088             
6089             if(typeof(config.width) != 'undefined'){
6090                 td.style += ' width:' +  config.width + 'px;';
6091             }
6092             
6093             if(typeof(config.cursor) != 'undefined'){
6094                 td.style += ' cursor:' +  config.cursor + ';';
6095             }
6096             
6097             if(typeof(config.cls) != 'undefined'){
6098                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6099             }
6100              
6101             row.cn.push(td);
6102            
6103         }
6104         
6105         row.cellObjects = cellObjects;
6106         
6107         return row;
6108           
6109     },
6110     
6111     
6112     
6113     onBeforeLoad : function()
6114     {
6115         //Roo.log('ds onBeforeLoad');
6116         
6117         //this.clear();
6118         
6119         //if(this.loadMask){
6120         //    this.maskEl.show();
6121         //}
6122     },
6123      /**
6124      * Remove all rows
6125      */
6126     clear : function()
6127     {
6128         this.el.select('tbody', true).first().dom.innerHTML = '';
6129     },
6130     /**
6131      * Show or hide a row.
6132      * @param {Number} rowIndex to show or hide
6133      * @param {Boolean} state hide
6134      */
6135     setRowVisibility : function(rowIndex, state)
6136     {
6137         var bt = this.mainBody.dom;
6138         
6139         var rows = this.el.select('tbody > tr', true).elements;
6140         
6141         if(typeof(rows[rowIndex]) == 'undefined'){
6142             return;
6143         }
6144         rows[rowIndex].dom.style.display = state ? '' : 'none';
6145     },
6146     
6147     
6148     getSelectionModel : function(){
6149         if(!this.selModel){
6150             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6151         }
6152         return this.selModel;
6153     },
6154     /*
6155      * Render the Roo.bootstrap object from renderder
6156      */
6157     renderCellObject : function(r)
6158     {
6159         var _this = this;
6160         
6161         var t = r.cfg.render(r.container);
6162         
6163         if(r.cfg.cn){
6164             Roo.each(r.cfg.cn, function(c){
6165                 var child = {
6166                     container: t.getChildContainer(),
6167                     cfg: c
6168                 }
6169                 _this.renderCellObject(child);
6170             })
6171         }
6172     },
6173     
6174     getRowIndex : function(row)
6175     {
6176         var rowIndex = -1;
6177         
6178         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6179             if(el != row){
6180                 return;
6181             }
6182             
6183             rowIndex = index;
6184         });
6185         
6186         return rowIndex;
6187     }
6188    
6189 });
6190
6191  
6192
6193  /*
6194  * - LGPL
6195  *
6196  * table cell
6197  * 
6198  */
6199
6200 /**
6201  * @class Roo.bootstrap.TableCell
6202  * @extends Roo.bootstrap.Component
6203  * Bootstrap TableCell class
6204  * @cfg {String} html cell contain text
6205  * @cfg {String} cls cell class
6206  * @cfg {String} tag cell tag (td|th) default td
6207  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6208  * @cfg {String} align Aligns the content in a cell
6209  * @cfg {String} axis Categorizes cells
6210  * @cfg {String} bgcolor Specifies the background color of a cell
6211  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6212  * @cfg {Number} colspan Specifies the number of columns a cell should span
6213  * @cfg {String} headers Specifies one or more header cells a cell is related to
6214  * @cfg {Number} height Sets the height of a cell
6215  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6216  * @cfg {Number} rowspan Sets the number of rows a cell should span
6217  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6218  * @cfg {String} valign Vertical aligns the content in a cell
6219  * @cfg {Number} width Specifies the width of a cell
6220  * 
6221  * @constructor
6222  * Create a new TableCell
6223  * @param {Object} config The config object
6224  */
6225
6226 Roo.bootstrap.TableCell = function(config){
6227     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6228 };
6229
6230 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6231     
6232     html: false,
6233     cls: false,
6234     tag: false,
6235     abbr: false,
6236     align: false,
6237     axis: false,
6238     bgcolor: false,
6239     charoff: false,
6240     colspan: false,
6241     headers: false,
6242     height: false,
6243     nowrap: false,
6244     rowspan: false,
6245     scope: false,
6246     valign: false,
6247     width: false,
6248     
6249     
6250     getAutoCreate : function(){
6251         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6252         
6253         cfg = {
6254             tag: 'td'
6255         }
6256         
6257         if(this.tag){
6258             cfg.tag = this.tag;
6259         }
6260         
6261         if (this.html) {
6262             cfg.html=this.html
6263         }
6264         if (this.cls) {
6265             cfg.cls=this.cls
6266         }
6267         if (this.abbr) {
6268             cfg.abbr=this.abbr
6269         }
6270         if (this.align) {
6271             cfg.align=this.align
6272         }
6273         if (this.axis) {
6274             cfg.axis=this.axis
6275         }
6276         if (this.bgcolor) {
6277             cfg.bgcolor=this.bgcolor
6278         }
6279         if (this.charoff) {
6280             cfg.charoff=this.charoff
6281         }
6282         if (this.colspan) {
6283             cfg.colspan=this.colspan
6284         }
6285         if (this.headers) {
6286             cfg.headers=this.headers
6287         }
6288         if (this.height) {
6289             cfg.height=this.height
6290         }
6291         if (this.nowrap) {
6292             cfg.nowrap=this.nowrap
6293         }
6294         if (this.rowspan) {
6295             cfg.rowspan=this.rowspan
6296         }
6297         if (this.scope) {
6298             cfg.scope=this.scope
6299         }
6300         if (this.valign) {
6301             cfg.valign=this.valign
6302         }
6303         if (this.width) {
6304             cfg.width=this.width
6305         }
6306         
6307         
6308         return cfg;
6309     }
6310    
6311 });
6312
6313  
6314
6315  /*
6316  * - LGPL
6317  *
6318  * table row
6319  * 
6320  */
6321
6322 /**
6323  * @class Roo.bootstrap.TableRow
6324  * @extends Roo.bootstrap.Component
6325  * Bootstrap TableRow class
6326  * @cfg {String} cls row class
6327  * @cfg {String} align Aligns the content in a table row
6328  * @cfg {String} bgcolor Specifies a background color for a table row
6329  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6330  * @cfg {String} valign Vertical aligns the content in a table row
6331  * 
6332  * @constructor
6333  * Create a new TableRow
6334  * @param {Object} config The config object
6335  */
6336
6337 Roo.bootstrap.TableRow = function(config){
6338     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6339 };
6340
6341 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6342     
6343     cls: false,
6344     align: false,
6345     bgcolor: false,
6346     charoff: false,
6347     valign: false,
6348     
6349     getAutoCreate : function(){
6350         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6351         
6352         cfg = {
6353             tag: 'tr'
6354         }
6355             
6356         if(this.cls){
6357             cfg.cls = this.cls;
6358         }
6359         if(this.align){
6360             cfg.align = this.align;
6361         }
6362         if(this.bgcolor){
6363             cfg.bgcolor = this.bgcolor;
6364         }
6365         if(this.charoff){
6366             cfg.charoff = this.charoff;
6367         }
6368         if(this.valign){
6369             cfg.valign = this.valign;
6370         }
6371         
6372         return cfg;
6373     }
6374    
6375 });
6376
6377  
6378
6379  /*
6380  * - LGPL
6381  *
6382  * table body
6383  * 
6384  */
6385
6386 /**
6387  * @class Roo.bootstrap.TableBody
6388  * @extends Roo.bootstrap.Component
6389  * Bootstrap TableBody class
6390  * @cfg {String} cls element class
6391  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6392  * @cfg {String} align Aligns the content inside the element
6393  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6394  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6395  * 
6396  * @constructor
6397  * Create a new TableBody
6398  * @param {Object} config The config object
6399  */
6400
6401 Roo.bootstrap.TableBody = function(config){
6402     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6403 };
6404
6405 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6406     
6407     cls: false,
6408     tag: false,
6409     align: false,
6410     charoff: false,
6411     valign: false,
6412     
6413     getAutoCreate : function(){
6414         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6415         
6416         cfg = {
6417             tag: 'tbody'
6418         }
6419             
6420         if (this.cls) {
6421             cfg.cls=this.cls
6422         }
6423         if(this.tag){
6424             cfg.tag = this.tag;
6425         }
6426         
6427         if(this.align){
6428             cfg.align = this.align;
6429         }
6430         if(this.charoff){
6431             cfg.charoff = this.charoff;
6432         }
6433         if(this.valign){
6434             cfg.valign = this.valign;
6435         }
6436         
6437         return cfg;
6438     }
6439     
6440     
6441 //    initEvents : function()
6442 //    {
6443 //        
6444 //        if(!this.store){
6445 //            return;
6446 //        }
6447 //        
6448 //        this.store = Roo.factory(this.store, Roo.data);
6449 //        this.store.on('load', this.onLoad, this);
6450 //        
6451 //        this.store.load();
6452 //        
6453 //    },
6454 //    
6455 //    onLoad: function () 
6456 //    {   
6457 //        this.fireEvent('load', this);
6458 //    }
6459 //    
6460 //   
6461 });
6462
6463  
6464
6465  /*
6466  * Based on:
6467  * Ext JS Library 1.1.1
6468  * Copyright(c) 2006-2007, Ext JS, LLC.
6469  *
6470  * Originally Released Under LGPL - original licence link has changed is not relivant.
6471  *
6472  * Fork - LGPL
6473  * <script type="text/javascript">
6474  */
6475
6476 // as we use this in bootstrap.
6477 Roo.namespace('Roo.form');
6478  /**
6479  * @class Roo.form.Action
6480  * Internal Class used to handle form actions
6481  * @constructor
6482  * @param {Roo.form.BasicForm} el The form element or its id
6483  * @param {Object} config Configuration options
6484  */
6485
6486  
6487  
6488 // define the action interface
6489 Roo.form.Action = function(form, options){
6490     this.form = form;
6491     this.options = options || {};
6492 };
6493 /**
6494  * Client Validation Failed
6495  * @const 
6496  */
6497 Roo.form.Action.CLIENT_INVALID = 'client';
6498 /**
6499  * Server Validation Failed
6500  * @const 
6501  */
6502 Roo.form.Action.SERVER_INVALID = 'server';
6503  /**
6504  * Connect to Server Failed
6505  * @const 
6506  */
6507 Roo.form.Action.CONNECT_FAILURE = 'connect';
6508 /**
6509  * Reading Data from Server Failed
6510  * @const 
6511  */
6512 Roo.form.Action.LOAD_FAILURE = 'load';
6513
6514 Roo.form.Action.prototype = {
6515     type : 'default',
6516     failureType : undefined,
6517     response : undefined,
6518     result : undefined,
6519
6520     // interface method
6521     run : function(options){
6522
6523     },
6524
6525     // interface method
6526     success : function(response){
6527
6528     },
6529
6530     // interface method
6531     handleResponse : function(response){
6532
6533     },
6534
6535     // default connection failure
6536     failure : function(response){
6537         
6538         this.response = response;
6539         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6540         this.form.afterAction(this, false);
6541     },
6542
6543     processResponse : function(response){
6544         this.response = response;
6545         if(!response.responseText){
6546             return true;
6547         }
6548         this.result = this.handleResponse(response);
6549         return this.result;
6550     },
6551
6552     // utility functions used internally
6553     getUrl : function(appendParams){
6554         var url = this.options.url || this.form.url || this.form.el.dom.action;
6555         if(appendParams){
6556             var p = this.getParams();
6557             if(p){
6558                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6559             }
6560         }
6561         return url;
6562     },
6563
6564     getMethod : function(){
6565         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6566     },
6567
6568     getParams : function(){
6569         var bp = this.form.baseParams;
6570         var p = this.options.params;
6571         if(p){
6572             if(typeof p == "object"){
6573                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6574             }else if(typeof p == 'string' && bp){
6575                 p += '&' + Roo.urlEncode(bp);
6576             }
6577         }else if(bp){
6578             p = Roo.urlEncode(bp);
6579         }
6580         return p;
6581     },
6582
6583     createCallback : function(){
6584         return {
6585             success: this.success,
6586             failure: this.failure,
6587             scope: this,
6588             timeout: (this.form.timeout*1000),
6589             upload: this.form.fileUpload ? this.success : undefined
6590         };
6591     }
6592 };
6593
6594 Roo.form.Action.Submit = function(form, options){
6595     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6596 };
6597
6598 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6599     type : 'submit',
6600
6601     haveProgress : false,
6602     uploadComplete : false,
6603     
6604     // uploadProgress indicator.
6605     uploadProgress : function()
6606     {
6607         if (!this.form.progressUrl) {
6608             return;
6609         }
6610         
6611         if (!this.haveProgress) {
6612             Roo.MessageBox.progress("Uploading", "Uploading");
6613         }
6614         if (this.uploadComplete) {
6615            Roo.MessageBox.hide();
6616            return;
6617         }
6618         
6619         this.haveProgress = true;
6620    
6621         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6622         
6623         var c = new Roo.data.Connection();
6624         c.request({
6625             url : this.form.progressUrl,
6626             params: {
6627                 id : uid
6628             },
6629             method: 'GET',
6630             success : function(req){
6631                //console.log(data);
6632                 var rdata = false;
6633                 var edata;
6634                 try  {
6635                    rdata = Roo.decode(req.responseText)
6636                 } catch (e) {
6637                     Roo.log("Invalid data from server..");
6638                     Roo.log(edata);
6639                     return;
6640                 }
6641                 if (!rdata || !rdata.success) {
6642                     Roo.log(rdata);
6643                     Roo.MessageBox.alert(Roo.encode(rdata));
6644                     return;
6645                 }
6646                 var data = rdata.data;
6647                 
6648                 if (this.uploadComplete) {
6649                    Roo.MessageBox.hide();
6650                    return;
6651                 }
6652                    
6653                 if (data){
6654                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6655                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6656                     );
6657                 }
6658                 this.uploadProgress.defer(2000,this);
6659             },
6660        
6661             failure: function(data) {
6662                 Roo.log('progress url failed ');
6663                 Roo.log(data);
6664             },
6665             scope : this
6666         });
6667            
6668     },
6669     
6670     
6671     run : function()
6672     {
6673         // run get Values on the form, so it syncs any secondary forms.
6674         this.form.getValues();
6675         
6676         var o = this.options;
6677         var method = this.getMethod();
6678         var isPost = method == 'POST';
6679         if(o.clientValidation === false || this.form.isValid()){
6680             
6681             if (this.form.progressUrl) {
6682                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6683                     (new Date() * 1) + '' + Math.random());
6684                     
6685             } 
6686             
6687             
6688             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6689                 form:this.form.el.dom,
6690                 url:this.getUrl(!isPost),
6691                 method: method,
6692                 params:isPost ? this.getParams() : null,
6693                 isUpload: this.form.fileUpload
6694             }));
6695             
6696             this.uploadProgress();
6697
6698         }else if (o.clientValidation !== false){ // client validation failed
6699             this.failureType = Roo.form.Action.CLIENT_INVALID;
6700             this.form.afterAction(this, false);
6701         }
6702     },
6703
6704     success : function(response)
6705     {
6706         this.uploadComplete= true;
6707         if (this.haveProgress) {
6708             Roo.MessageBox.hide();
6709         }
6710         
6711         
6712         var result = this.processResponse(response);
6713         if(result === true || result.success){
6714             this.form.afterAction(this, true);
6715             return;
6716         }
6717         if(result.errors){
6718             this.form.markInvalid(result.errors);
6719             this.failureType = Roo.form.Action.SERVER_INVALID;
6720         }
6721         this.form.afterAction(this, false);
6722     },
6723     failure : function(response)
6724     {
6725         this.uploadComplete= true;
6726         if (this.haveProgress) {
6727             Roo.MessageBox.hide();
6728         }
6729         
6730         this.response = response;
6731         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6732         this.form.afterAction(this, false);
6733     },
6734     
6735     handleResponse : function(response){
6736         if(this.form.errorReader){
6737             var rs = this.form.errorReader.read(response);
6738             var errors = [];
6739             if(rs.records){
6740                 for(var i = 0, len = rs.records.length; i < len; i++) {
6741                     var r = rs.records[i];
6742                     errors[i] = r.data;
6743                 }
6744             }
6745             if(errors.length < 1){
6746                 errors = null;
6747             }
6748             return {
6749                 success : rs.success,
6750                 errors : errors
6751             };
6752         }
6753         var ret = false;
6754         try {
6755             ret = Roo.decode(response.responseText);
6756         } catch (e) {
6757             ret = {
6758                 success: false,
6759                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6760                 errors : []
6761             };
6762         }
6763         return ret;
6764         
6765     }
6766 });
6767
6768
6769 Roo.form.Action.Load = function(form, options){
6770     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6771     this.reader = this.form.reader;
6772 };
6773
6774 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6775     type : 'load',
6776
6777     run : function(){
6778         
6779         Roo.Ajax.request(Roo.apply(
6780                 this.createCallback(), {
6781                     method:this.getMethod(),
6782                     url:this.getUrl(false),
6783                     params:this.getParams()
6784         }));
6785     },
6786
6787     success : function(response){
6788         
6789         var result = this.processResponse(response);
6790         if(result === true || !result.success || !result.data){
6791             this.failureType = Roo.form.Action.LOAD_FAILURE;
6792             this.form.afterAction(this, false);
6793             return;
6794         }
6795         this.form.clearInvalid();
6796         this.form.setValues(result.data);
6797         this.form.afterAction(this, true);
6798     },
6799
6800     handleResponse : function(response){
6801         if(this.form.reader){
6802             var rs = this.form.reader.read(response);
6803             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6804             return {
6805                 success : rs.success,
6806                 data : data
6807             };
6808         }
6809         return Roo.decode(response.responseText);
6810     }
6811 });
6812
6813 Roo.form.Action.ACTION_TYPES = {
6814     'load' : Roo.form.Action.Load,
6815     'submit' : Roo.form.Action.Submit
6816 };/*
6817  * - LGPL
6818  *
6819  * form
6820  * 
6821  */
6822
6823 /**
6824  * @class Roo.bootstrap.Form
6825  * @extends Roo.bootstrap.Component
6826  * Bootstrap Form class
6827  * @cfg {String} method  GET | POST (default POST)
6828  * @cfg {String} labelAlign top | left (default top)
6829  * @cfg {String} align left  | right - for navbars
6830  * @cfg {Boolean} loadMask load mask when submit (default true)
6831
6832  * 
6833  * @constructor
6834  * Create a new Form
6835  * @param {Object} config The config object
6836  */
6837
6838
6839 Roo.bootstrap.Form = function(config){
6840     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6841     this.addEvents({
6842         /**
6843          * @event clientvalidation
6844          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6845          * @param {Form} this
6846          * @param {Boolean} valid true if the form has passed client-side validation
6847          */
6848         clientvalidation: true,
6849         /**
6850          * @event beforeaction
6851          * Fires before any action is performed. Return false to cancel the action.
6852          * @param {Form} this
6853          * @param {Action} action The action to be performed
6854          */
6855         beforeaction: true,
6856         /**
6857          * @event actionfailed
6858          * Fires when an action fails.
6859          * @param {Form} this
6860          * @param {Action} action The action that failed
6861          */
6862         actionfailed : true,
6863         /**
6864          * @event actioncomplete
6865          * Fires when an action is completed.
6866          * @param {Form} this
6867          * @param {Action} action The action that completed
6868          */
6869         actioncomplete : true
6870     });
6871     
6872 };
6873
6874 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6875       
6876      /**
6877      * @cfg {String} method
6878      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6879      */
6880     method : 'POST',
6881     /**
6882      * @cfg {String} url
6883      * The URL to use for form actions if one isn't supplied in the action options.
6884      */
6885     /**
6886      * @cfg {Boolean} fileUpload
6887      * Set to true if this form is a file upload.
6888      */
6889      
6890     /**
6891      * @cfg {Object} baseParams
6892      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6893      */
6894       
6895     /**
6896      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6897      */
6898     timeout: 30,
6899     /**
6900      * @cfg {Sting} align (left|right) for navbar forms
6901      */
6902     align : 'left',
6903
6904     // private
6905     activeAction : null,
6906  
6907     /**
6908      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6909      * element by passing it or its id or mask the form itself by passing in true.
6910      * @type Mixed
6911      */
6912     waitMsgTarget : false,
6913     
6914     loadMask : true,
6915     
6916     getAutoCreate : function(){
6917         
6918         var cfg = {
6919             tag: 'form',
6920             method : this.method || 'POST',
6921             id : this.id || Roo.id(),
6922             cls : ''
6923         }
6924         if (this.parent().xtype.match(/^Nav/)) {
6925             cfg.cls = 'navbar-form navbar-' + this.align;
6926             
6927         }
6928         
6929         if (this.labelAlign == 'left' ) {
6930             cfg.cls += ' form-horizontal';
6931         }
6932         
6933         
6934         return cfg;
6935     },
6936     initEvents : function()
6937     {
6938         this.el.on('submit', this.onSubmit, this);
6939         // this was added as random key presses on the form where triggering form submit.
6940         this.el.on('keypress', function(e) {
6941             if (e.getCharCode() != 13) {
6942                 return true;
6943             }
6944             // we might need to allow it for textareas.. and some other items.
6945             // check e.getTarget().
6946             
6947             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6948                 return true;
6949             }
6950         
6951             Roo.log("keypress blocked");
6952             
6953             e.preventDefault();
6954             return false;
6955         });
6956         
6957     },
6958     // private
6959     onSubmit : function(e){
6960         e.stopEvent();
6961     },
6962     
6963      /**
6964      * Returns true if client-side validation on the form is successful.
6965      * @return Boolean
6966      */
6967     isValid : function(){
6968         var items = this.getItems();
6969         var valid = true;
6970         items.each(function(f){
6971            if(!f.validate()){
6972                valid = false;
6973                
6974            }
6975         });
6976         return valid;
6977     },
6978     /**
6979      * Returns true if any fields in this form have changed since their original load.
6980      * @return Boolean
6981      */
6982     isDirty : function(){
6983         var dirty = false;
6984         var items = this.getItems();
6985         items.each(function(f){
6986            if(f.isDirty()){
6987                dirty = true;
6988                return false;
6989            }
6990            return true;
6991         });
6992         return dirty;
6993     },
6994      /**
6995      * Performs a predefined action (submit or load) or custom actions you define on this form.
6996      * @param {String} actionName The name of the action type
6997      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6998      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6999      * accept other config options):
7000      * <pre>
7001 Property          Type             Description
7002 ----------------  ---------------  ----------------------------------------------------------------------------------
7003 url               String           The url for the action (defaults to the form's url)
7004 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7005 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7006 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7007                                    validate the form on the client (defaults to false)
7008      * </pre>
7009      * @return {BasicForm} this
7010      */
7011     doAction : function(action, options){
7012         if(typeof action == 'string'){
7013             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7014         }
7015         if(this.fireEvent('beforeaction', this, action) !== false){
7016             this.beforeAction(action);
7017             action.run.defer(100, action);
7018         }
7019         return this;
7020     },
7021     
7022     // private
7023     beforeAction : function(action){
7024         var o = action.options;
7025         
7026         if(this.loadMask){
7027             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7028         }
7029         // not really supported yet.. ??
7030         
7031         //if(this.waitMsgTarget === true){
7032         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7033         //}else if(this.waitMsgTarget){
7034         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7035         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7036         //}else {
7037         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7038        // }
7039          
7040     },
7041
7042     // private
7043     afterAction : function(action, success){
7044         this.activeAction = null;
7045         var o = action.options;
7046         
7047         //if(this.waitMsgTarget === true){
7048             this.el.unmask();
7049         //}else if(this.waitMsgTarget){
7050         //    this.waitMsgTarget.unmask();
7051         //}else{
7052         //    Roo.MessageBox.updateProgress(1);
7053         //    Roo.MessageBox.hide();
7054        // }
7055         // 
7056         if(success){
7057             if(o.reset){
7058                 this.reset();
7059             }
7060             Roo.callback(o.success, o.scope, [this, action]);
7061             this.fireEvent('actioncomplete', this, action);
7062             
7063         }else{
7064             
7065             // failure condition..
7066             // we have a scenario where updates need confirming.
7067             // eg. if a locking scenario exists..
7068             // we look for { errors : { needs_confirm : true }} in the response.
7069             if (
7070                 (typeof(action.result) != 'undefined')  &&
7071                 (typeof(action.result.errors) != 'undefined')  &&
7072                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7073            ){
7074                 var _t = this;
7075                 Roo.log("not supported yet");
7076                  /*
7077                 
7078                 Roo.MessageBox.confirm(
7079                     "Change requires confirmation",
7080                     action.result.errorMsg,
7081                     function(r) {
7082                         if (r != 'yes') {
7083                             return;
7084                         }
7085                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7086                     }
7087                     
7088                 );
7089                 */
7090                 
7091                 
7092                 return;
7093             }
7094             
7095             Roo.callback(o.failure, o.scope, [this, action]);
7096             // show an error message if no failed handler is set..
7097             if (!this.hasListener('actionfailed')) {
7098                 Roo.log("need to add dialog support");
7099                 /*
7100                 Roo.MessageBox.alert("Error",
7101                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7102                         action.result.errorMsg :
7103                         "Saving Failed, please check your entries or try again"
7104                 );
7105                 */
7106             }
7107             
7108             this.fireEvent('actionfailed', this, action);
7109         }
7110         
7111     },
7112     /**
7113      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7114      * @param {String} id The value to search for
7115      * @return Field
7116      */
7117     findField : function(id){
7118         var items = this.getItems();
7119         var field = items.get(id);
7120         if(!field){
7121              items.each(function(f){
7122                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7123                     field = f;
7124                     return false;
7125                 }
7126                 return true;
7127             });
7128         }
7129         return field || null;
7130     },
7131      /**
7132      * Mark fields in this form invalid in bulk.
7133      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7134      * @return {BasicForm} this
7135      */
7136     markInvalid : function(errors){
7137         if(errors instanceof Array){
7138             for(var i = 0, len = errors.length; i < len; i++){
7139                 var fieldError = errors[i];
7140                 var f = this.findField(fieldError.id);
7141                 if(f){
7142                     f.markInvalid(fieldError.msg);
7143                 }
7144             }
7145         }else{
7146             var field, id;
7147             for(id in errors){
7148                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7149                     field.markInvalid(errors[id]);
7150                 }
7151             }
7152         }
7153         //Roo.each(this.childForms || [], function (f) {
7154         //    f.markInvalid(errors);
7155         //});
7156         
7157         return this;
7158     },
7159
7160     /**
7161      * Set values for fields in this form in bulk.
7162      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7163      * @return {BasicForm} this
7164      */
7165     setValues : function(values){
7166         if(values instanceof Array){ // array of objects
7167             for(var i = 0, len = values.length; i < len; i++){
7168                 var v = values[i];
7169                 var f = this.findField(v.id);
7170                 if(f){
7171                     f.setValue(v.value);
7172                     if(this.trackResetOnLoad){
7173                         f.originalValue = f.getValue();
7174                     }
7175                 }
7176             }
7177         }else{ // object hash
7178             var field, id;
7179             for(id in values){
7180                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7181                     
7182                     if (field.setFromData && 
7183                         field.valueField && 
7184                         field.displayField &&
7185                         // combos' with local stores can 
7186                         // be queried via setValue()
7187                         // to set their value..
7188                         (field.store && !field.store.isLocal)
7189                         ) {
7190                         // it's a combo
7191                         var sd = { };
7192                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7193                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7194                         field.setFromData(sd);
7195                         
7196                     } else {
7197                         field.setValue(values[id]);
7198                     }
7199                     
7200                     
7201                     if(this.trackResetOnLoad){
7202                         field.originalValue = field.getValue();
7203                     }
7204                 }
7205             }
7206         }
7207          
7208         //Roo.each(this.childForms || [], function (f) {
7209         //    f.setValues(values);
7210         //});
7211                 
7212         return this;
7213     },
7214
7215     /**
7216      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7217      * they are returned as an array.
7218      * @param {Boolean} asString
7219      * @return {Object}
7220      */
7221     getValues : function(asString){
7222         //if (this.childForms) {
7223             // copy values from the child forms
7224         //    Roo.each(this.childForms, function (f) {
7225         //        this.setValues(f.getValues());
7226         //    }, this);
7227         //}
7228         
7229         
7230         
7231         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7232         if(asString === true){
7233             return fs;
7234         }
7235         return Roo.urlDecode(fs);
7236     },
7237     
7238     /**
7239      * Returns the fields in this form as an object with key/value pairs. 
7240      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7241      * @return {Object}
7242      */
7243     getFieldValues : function(with_hidden)
7244     {
7245         var items = this.getItems();
7246         var ret = {};
7247         items.each(function(f){
7248             if (!f.getName()) {
7249                 return;
7250             }
7251             var v = f.getValue();
7252             if (f.inputType =='radio') {
7253                 if (typeof(ret[f.getName()]) == 'undefined') {
7254                     ret[f.getName()] = ''; // empty..
7255                 }
7256                 
7257                 if (!f.el.dom.checked) {
7258                     return;
7259                     
7260                 }
7261                 v = f.el.dom.value;
7262                 
7263             }
7264             
7265             // not sure if this supported any more..
7266             if ((typeof(v) == 'object') && f.getRawValue) {
7267                 v = f.getRawValue() ; // dates..
7268             }
7269             // combo boxes where name != hiddenName...
7270             if (f.name != f.getName()) {
7271                 ret[f.name] = f.getRawValue();
7272             }
7273             ret[f.getName()] = v;
7274         });
7275         
7276         return ret;
7277     },
7278
7279     /**
7280      * Clears all invalid messages in this form.
7281      * @return {BasicForm} this
7282      */
7283     clearInvalid : function(){
7284         var items = this.getItems();
7285         
7286         items.each(function(f){
7287            f.clearInvalid();
7288         });
7289         
7290         
7291         
7292         return this;
7293     },
7294
7295     /**
7296      * Resets this form.
7297      * @return {BasicForm} this
7298      */
7299     reset : function(){
7300         var items = this.getItems();
7301         items.each(function(f){
7302             f.reset();
7303         });
7304         
7305         Roo.each(this.childForms || [], function (f) {
7306             f.reset();
7307         });
7308        
7309         
7310         return this;
7311     },
7312     getItems : function()
7313     {
7314         var r=new Roo.util.MixedCollection(false, function(o){
7315             return o.id || (o.id = Roo.id());
7316         });
7317         var iter = function(el) {
7318             if (el.inputEl) {
7319                 r.add(el);
7320             }
7321             if (!el.items) {
7322                 return;
7323             }
7324             Roo.each(el.items,function(e) {
7325                 iter(e);
7326             });
7327             
7328             
7329         };
7330         
7331         iter(this);
7332         return r;
7333         
7334         
7335         
7336         
7337     }
7338     
7339 });
7340
7341  
7342 /*
7343  * Based on:
7344  * Ext JS Library 1.1.1
7345  * Copyright(c) 2006-2007, Ext JS, LLC.
7346  *
7347  * Originally Released Under LGPL - original licence link has changed is not relivant.
7348  *
7349  * Fork - LGPL
7350  * <script type="text/javascript">
7351  */
7352 /**
7353  * @class Roo.form.VTypes
7354  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7355  * @singleton
7356  */
7357 Roo.form.VTypes = function(){
7358     // closure these in so they are only created once.
7359     var alpha = /^[a-zA-Z_]+$/;
7360     var alphanum = /^[a-zA-Z0-9_]+$/;
7361     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7362     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7363
7364     // All these messages and functions are configurable
7365     return {
7366         /**
7367          * The function used to validate email addresses
7368          * @param {String} value The email address
7369          */
7370         'email' : function(v){
7371             return email.test(v);
7372         },
7373         /**
7374          * The error text to display when the email validation function returns false
7375          * @type String
7376          */
7377         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7378         /**
7379          * The keystroke filter mask to be applied on email input
7380          * @type RegExp
7381          */
7382         'emailMask' : /[a-z0-9_\.\-@]/i,
7383
7384         /**
7385          * The function used to validate URLs
7386          * @param {String} value The URL
7387          */
7388         'url' : function(v){
7389             return url.test(v);
7390         },
7391         /**
7392          * The error text to display when the url validation function returns false
7393          * @type String
7394          */
7395         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7396         
7397         /**
7398          * The function used to validate alpha values
7399          * @param {String} value The value
7400          */
7401         'alpha' : function(v){
7402             return alpha.test(v);
7403         },
7404         /**
7405          * The error text to display when the alpha validation function returns false
7406          * @type String
7407          */
7408         'alphaText' : 'This field should only contain letters and _',
7409         /**
7410          * The keystroke filter mask to be applied on alpha input
7411          * @type RegExp
7412          */
7413         'alphaMask' : /[a-z_]/i,
7414
7415         /**
7416          * The function used to validate alphanumeric values
7417          * @param {String} value The value
7418          */
7419         'alphanum' : function(v){
7420             return alphanum.test(v);
7421         },
7422         /**
7423          * The error text to display when the alphanumeric validation function returns false
7424          * @type String
7425          */
7426         'alphanumText' : 'This field should only contain letters, numbers and _',
7427         /**
7428          * The keystroke filter mask to be applied on alphanumeric input
7429          * @type RegExp
7430          */
7431         'alphanumMask' : /[a-z0-9_]/i
7432     };
7433 }();/*
7434  * - LGPL
7435  *
7436  * Input
7437  * 
7438  */
7439
7440 /**
7441  * @class Roo.bootstrap.Input
7442  * @extends Roo.bootstrap.Component
7443  * Bootstrap Input class
7444  * @cfg {Boolean} disabled is it disabled
7445  * @cfg {String} fieldLabel - the label associated
7446  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7447  * @cfg {String} name name of the input
7448  * @cfg {string} fieldLabel - the label associated
7449  * @cfg {string}  inputType - input / file submit ...
7450  * @cfg {string} placeholder - placeholder to put in text.
7451  * @cfg {string}  before - input group add on before
7452  * @cfg {string} after - input group add on after
7453  * @cfg {string} size - (lg|sm) or leave empty..
7454  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7455  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7456  * @cfg {Number} md colspan out of 12 for computer-sized screens
7457  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7458  * @cfg {string} value default value of the input
7459  * @cfg {Number} labelWidth set the width of label (0-12)
7460  * @cfg {String} labelAlign (top|left)
7461  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7462  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7463
7464  * @cfg {String} align (left|center|right) Default left
7465  * 
7466  * 
7467  * 
7468  * @constructor
7469  * Create a new Input
7470  * @param {Object} config The config object
7471  */
7472
7473 Roo.bootstrap.Input = function(config){
7474     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7475    
7476         this.addEvents({
7477             /**
7478              * @event focus
7479              * Fires when this field receives input focus.
7480              * @param {Roo.form.Field} this
7481              */
7482             focus : true,
7483             /**
7484              * @event blur
7485              * Fires when this field loses input focus.
7486              * @param {Roo.form.Field} this
7487              */
7488             blur : true,
7489             /**
7490              * @event specialkey
7491              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7492              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7493              * @param {Roo.form.Field} this
7494              * @param {Roo.EventObject} e The event object
7495              */
7496             specialkey : true,
7497             /**
7498              * @event change
7499              * Fires just before the field blurs if the field value has changed.
7500              * @param {Roo.form.Field} this
7501              * @param {Mixed} newValue The new value
7502              * @param {Mixed} oldValue The original value
7503              */
7504             change : true,
7505             /**
7506              * @event invalid
7507              * Fires after the field has been marked as invalid.
7508              * @param {Roo.form.Field} this
7509              * @param {String} msg The validation message
7510              */
7511             invalid : true,
7512             /**
7513              * @event valid
7514              * Fires after the field has been validated with no errors.
7515              * @param {Roo.form.Field} this
7516              */
7517             valid : true,
7518              /**
7519              * @event keyup
7520              * Fires after the key up
7521              * @param {Roo.form.Field} this
7522              * @param {Roo.EventObject}  e The event Object
7523              */
7524             keyup : true
7525         });
7526 };
7527
7528 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7529      /**
7530      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7531       automatic validation (defaults to "keyup").
7532      */
7533     validationEvent : "keyup",
7534      /**
7535      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7536      */
7537     validateOnBlur : true,
7538     /**
7539      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7540      */
7541     validationDelay : 250,
7542      /**
7543      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7544      */
7545     focusClass : "x-form-focus",  // not needed???
7546     
7547        
7548     /**
7549      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7550      */
7551     invalidClass : "has-warning",
7552     
7553     /**
7554      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7555      */
7556     validClass : "has-success",
7557     
7558     /**
7559      * @cfg {Boolean} hasFeedback (true|false) default true
7560      */
7561     hasFeedback : true,
7562     
7563     /**
7564      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7565      */
7566     invalidFeedbackClass : "glyphicon-warning-sign",
7567     
7568     /**
7569      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7570      */
7571     validFeedbackClass : "glyphicon-ok",
7572     
7573     /**
7574      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7575      */
7576     selectOnFocus : false,
7577     
7578      /**
7579      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7580      */
7581     maskRe : null,
7582        /**
7583      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7584      */
7585     vtype : null,
7586     
7587       /**
7588      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7589      */
7590     disableKeyFilter : false,
7591     
7592        /**
7593      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7594      */
7595     disabled : false,
7596      /**
7597      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7598      */
7599     allowBlank : true,
7600     /**
7601      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7602      */
7603     blankText : "This field is required",
7604     
7605      /**
7606      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7607      */
7608     minLength : 0,
7609     /**
7610      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7611      */
7612     maxLength : Number.MAX_VALUE,
7613     /**
7614      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7615      */
7616     minLengthText : "The minimum length for this field is {0}",
7617     /**
7618      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7619      */
7620     maxLengthText : "The maximum length for this field is {0}",
7621   
7622     
7623     /**
7624      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7625      * If available, this function will be called only after the basic validators all return true, and will be passed the
7626      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7627      */
7628     validator : null,
7629     /**
7630      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7631      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7632      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7633      */
7634     regex : null,
7635     /**
7636      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7637      */
7638     regexText : "",
7639     
7640     autocomplete: false,
7641     
7642     
7643     fieldLabel : '',
7644     inputType : 'text',
7645     
7646     name : false,
7647     placeholder: false,
7648     before : false,
7649     after : false,
7650     size : false,
7651     hasFocus : false,
7652     preventMark: false,
7653     isFormField : true,
7654     value : '',
7655     labelWidth : 2,
7656     labelAlign : false,
7657     readOnly : false,
7658     align : false,
7659     formatedValue : false,
7660     
7661     parentLabelAlign : function()
7662     {
7663         var parent = this;
7664         while (parent.parent()) {
7665             parent = parent.parent();
7666             if (typeof(parent.labelAlign) !='undefined') {
7667                 return parent.labelAlign;
7668             }
7669         }
7670         return 'left';
7671         
7672     },
7673     
7674     getAutoCreate : function(){
7675         
7676         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7677         
7678         var id = Roo.id();
7679         
7680         var cfg = {};
7681         
7682         if(this.inputType != 'hidden'){
7683             cfg.cls = 'form-group' //input-group
7684         }
7685         
7686         var input =  {
7687             tag: 'input',
7688             id : id,
7689             type : this.inputType,
7690             value : this.value,
7691             cls : 'form-control',
7692             placeholder : this.placeholder || '',
7693             autocomplete : this.autocomplete || 'new-password'
7694         };
7695         
7696         
7697         if(this.align){
7698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7699         }
7700         
7701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7702             input.maxLength = this.maxLength;
7703         }
7704         
7705         if (this.disabled) {
7706             input.disabled=true;
7707         }
7708         
7709         if (this.readOnly) {
7710             input.readonly=true;
7711         }
7712         
7713         if (this.name) {
7714             input.name = this.name;
7715         }
7716         if (this.size) {
7717             input.cls += ' input-' + this.size;
7718         }
7719         var settings=this;
7720         ['xs','sm','md','lg'].map(function(size){
7721             if (settings[size]) {
7722                 cfg.cls += ' col-' + size + '-' + settings[size];
7723             }
7724         });
7725         
7726         var inputblock = input;
7727         
7728         var feedback = {
7729             tag: 'span',
7730             cls: 'glyphicon form-control-feedback'
7731         };
7732             
7733         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7734             
7735             inputblock = {
7736                 cls : 'has-feedback',
7737                 cn :  [
7738                     input,
7739                     feedback
7740                 ] 
7741             };  
7742         }
7743         
7744         if (this.before || this.after) {
7745             
7746             inputblock = {
7747                 cls : 'input-group',
7748                 cn :  [] 
7749             };
7750             
7751             if (this.before && typeof(this.before) == 'string') {
7752                 
7753                 inputblock.cn.push({
7754                     tag :'span',
7755                     cls : 'roo-input-before input-group-addon',
7756                     html : this.before
7757                 });
7758             }
7759             if (this.before && typeof(this.before) == 'object') {
7760                 this.before = Roo.factory(this.before);
7761                 Roo.log(this.before);
7762                 inputblock.cn.push({
7763                     tag :'span',
7764                     cls : 'roo-input-before input-group-' +
7765                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7766                 });
7767             }
7768             
7769             inputblock.cn.push(input);
7770             
7771             if (this.after && typeof(this.after) == 'string') {
7772                 inputblock.cn.push({
7773                     tag :'span',
7774                     cls : 'roo-input-after input-group-addon',
7775                     html : this.after
7776                 });
7777             }
7778             if (this.after && typeof(this.after) == 'object') {
7779                 this.after = Roo.factory(this.after);
7780                 Roo.log(this.after);
7781                 inputblock.cn.push({
7782                     tag :'span',
7783                     cls : 'roo-input-after input-group-' +
7784                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7785                 });
7786             }
7787             
7788             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7789                 inputblock.cls += ' has-feedback';
7790                 inputblock.cn.push(feedback);
7791             }
7792         };
7793         
7794         if (align ==='left' && this.fieldLabel.length) {
7795                 Roo.log("left and has label");
7796                 cfg.cn = [
7797                     
7798                     {
7799                         tag: 'label',
7800                         'for' :  id,
7801                         cls : 'control-label col-sm-' + this.labelWidth,
7802                         html : this.fieldLabel
7803                         
7804                     },
7805                     {
7806                         cls : "col-sm-" + (12 - this.labelWidth), 
7807                         cn: [
7808                             inputblock
7809                         ]
7810                     }
7811                     
7812                 ];
7813         } else if ( this.fieldLabel.length) {
7814                 Roo.log(" label");
7815                  cfg.cn = [
7816                    
7817                     {
7818                         tag: 'label',
7819                         //cls : 'input-group-addon',
7820                         html : this.fieldLabel
7821                         
7822                     },
7823                     
7824                     inputblock
7825                     
7826                 ];
7827
7828         } else {
7829             
7830                 Roo.log(" no label && no align");
7831                 cfg.cn = [
7832                     
7833                         inputblock
7834                     
7835                 ];
7836                 
7837                 
7838         };
7839         Roo.log('input-parentType: ' + this.parentType);
7840         
7841         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7842            cfg.cls += ' navbar-form';
7843            Roo.log(cfg);
7844         }
7845         
7846         return cfg;
7847         
7848     },
7849     /**
7850      * return the real input element.
7851      */
7852     inputEl: function ()
7853     {
7854         return this.el.select('input.form-control',true).first();
7855     },
7856     
7857     tooltipEl : function()
7858     {
7859         return this.inputEl();
7860     },
7861     
7862     setDisabled : function(v)
7863     {
7864         var i  = this.inputEl().dom;
7865         if (!v) {
7866             i.removeAttribute('disabled');
7867             return;
7868             
7869         }
7870         i.setAttribute('disabled','true');
7871     },
7872     initEvents : function()
7873     {
7874           
7875         this.inputEl().on("keydown" , this.fireKey,  this);
7876         this.inputEl().on("focus", this.onFocus,  this);
7877         this.inputEl().on("blur", this.onBlur,  this);
7878         
7879         this.inputEl().relayEvent('keyup', this);
7880
7881         // reference to original value for reset
7882         this.originalValue = this.getValue();
7883         //Roo.form.TextField.superclass.initEvents.call(this);
7884         if(this.validationEvent == 'keyup'){
7885             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7886             this.inputEl().on('keyup', this.filterValidation, this);
7887         }
7888         else if(this.validationEvent !== false){
7889             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7890         }
7891         
7892         if(this.selectOnFocus){
7893             this.on("focus", this.preFocus, this);
7894             
7895         }
7896         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7897             this.inputEl().on("keypress", this.filterKeys, this);
7898         }
7899        /* if(this.grow){
7900             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7901             this.el.on("click", this.autoSize,  this);
7902         }
7903         */
7904         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7905             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7906         }
7907         
7908         if (typeof(this.before) == 'object') {
7909             this.before.render(this.el.select('.roo-input-before',true).first());
7910         }
7911         if (typeof(this.after) == 'object') {
7912             this.after.render(this.el.select('.roo-input-after',true).first());
7913         }
7914         
7915         
7916     },
7917     filterValidation : function(e){
7918         if(!e.isNavKeyPress()){
7919             this.validationTask.delay(this.validationDelay);
7920         }
7921     },
7922      /**
7923      * Validates the field value
7924      * @return {Boolean} True if the value is valid, else false
7925      */
7926     validate : function(){
7927         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7928         if(this.disabled || this.validateValue(this.getRawValue())){
7929             this.markValid();
7930             return true;
7931         }
7932         
7933         this.markInvalid();
7934         return false;
7935     },
7936     
7937     
7938     /**
7939      * Validates a value according to the field's validation rules and marks the field as invalid
7940      * if the validation fails
7941      * @param {Mixed} value The value to validate
7942      * @return {Boolean} True if the value is valid, else false
7943      */
7944     validateValue : function(value){
7945         if(value.length < 1)  { // if it's blank
7946             if(this.allowBlank){
7947                 return true;
7948             }
7949             return false;
7950         }
7951         
7952         if(value.length < this.minLength){
7953             return false;
7954         }
7955         if(value.length > this.maxLength){
7956             return false;
7957         }
7958         if(this.vtype){
7959             var vt = Roo.form.VTypes;
7960             if(!vt[this.vtype](value, this)){
7961                 return false;
7962             }
7963         }
7964         if(typeof this.validator == "function"){
7965             var msg = this.validator(value);
7966             if(msg !== true){
7967                 return false;
7968             }
7969         }
7970         
7971         if(this.regex && !this.regex.test(value)){
7972             return false;
7973         }
7974         
7975         return true;
7976     },
7977
7978     
7979     
7980      // private
7981     fireKey : function(e){
7982         //Roo.log('field ' + e.getKey());
7983         if(e.isNavKeyPress()){
7984             this.fireEvent("specialkey", this, e);
7985         }
7986     },
7987     focus : function (selectText){
7988         if(this.rendered){
7989             this.inputEl().focus();
7990             if(selectText === true){
7991                 this.inputEl().dom.select();
7992             }
7993         }
7994         return this;
7995     } ,
7996     
7997     onFocus : function(){
7998         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7999            // this.el.addClass(this.focusClass);
8000         }
8001         if(!this.hasFocus){
8002             this.hasFocus = true;
8003             this.startValue = this.getValue();
8004             this.fireEvent("focus", this);
8005         }
8006     },
8007     
8008     beforeBlur : Roo.emptyFn,
8009
8010     
8011     // private
8012     onBlur : function(){
8013         this.beforeBlur();
8014         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8015             //this.el.removeClass(this.focusClass);
8016         }
8017         this.hasFocus = false;
8018         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8019             this.validate();
8020         }
8021         var v = this.getValue();
8022         if(String(v) !== String(this.startValue)){
8023             this.fireEvent('change', this, v, this.startValue);
8024         }
8025         this.fireEvent("blur", this);
8026     },
8027     
8028     /**
8029      * Resets the current field value to the originally loaded value and clears any validation messages
8030      */
8031     reset : function(){
8032         this.setValue(this.originalValue);
8033         this.validate();
8034     },
8035      /**
8036      * Returns the name of the field
8037      * @return {Mixed} name The name field
8038      */
8039     getName: function(){
8040         return this.name;
8041     },
8042      /**
8043      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8044      * @return {Mixed} value The field value
8045      */
8046     getValue : function(){
8047         
8048         var v = this.inputEl().getValue();
8049         
8050         return v;
8051     },
8052     /**
8053      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8054      * @return {Mixed} value The field value
8055      */
8056     getRawValue : function(){
8057         var v = this.inputEl().getValue();
8058         
8059         return v;
8060     },
8061     
8062     /**
8063      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8064      * @param {Mixed} value The value to set
8065      */
8066     setRawValue : function(v){
8067         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8068     },
8069     
8070     selectText : function(start, end){
8071         var v = this.getRawValue();
8072         if(v.length > 0){
8073             start = start === undefined ? 0 : start;
8074             end = end === undefined ? v.length : end;
8075             var d = this.inputEl().dom;
8076             if(d.setSelectionRange){
8077                 d.setSelectionRange(start, end);
8078             }else if(d.createTextRange){
8079                 var range = d.createTextRange();
8080                 range.moveStart("character", start);
8081                 range.moveEnd("character", v.length-end);
8082                 range.select();
8083             }
8084         }
8085     },
8086     
8087     /**
8088      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8089      * @param {Mixed} value The value to set
8090      */
8091     setValue : function(v){
8092         this.value = v;
8093         if(this.rendered){
8094             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8095             this.validate();
8096         }
8097     },
8098     
8099     /*
8100     processValue : function(value){
8101         if(this.stripCharsRe){
8102             var newValue = value.replace(this.stripCharsRe, '');
8103             if(newValue !== value){
8104                 this.setRawValue(newValue);
8105                 return newValue;
8106             }
8107         }
8108         return value;
8109     },
8110   */
8111     preFocus : function(){
8112         
8113         if(this.selectOnFocus){
8114             this.inputEl().dom.select();
8115         }
8116     },
8117     filterKeys : function(e){
8118         var k = e.getKey();
8119         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8120             return;
8121         }
8122         var c = e.getCharCode(), cc = String.fromCharCode(c);
8123         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8124             return;
8125         }
8126         if(!this.maskRe.test(cc)){
8127             e.stopEvent();
8128         }
8129     },
8130      /**
8131      * Clear any invalid styles/messages for this field
8132      */
8133     clearInvalid : function(){
8134         
8135         if(!this.el || this.preventMark){ // not rendered
8136             return;
8137         }
8138         this.el.removeClass(this.invalidClass);
8139         
8140         this.fireEvent('valid', this);
8141     },
8142     
8143      /**
8144      * Mark this field as valid
8145      */
8146     markValid : function(){
8147         if(!this.el  || this.preventMark){ // not rendered
8148             return;
8149         }
8150         
8151         this.el.removeClass([this.invalidClass, this.validClass]);
8152         
8153         if(this.disabled || this.allowBlank){
8154             return;
8155         }
8156         
8157         this.el.addClass(this.validClass);
8158         
8159         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8160             
8161             var feedback = this.el.select('.form-control-feedback', true).first();
8162             
8163             if(feedback){
8164                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8165                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8166             }
8167             
8168         }
8169         
8170         this.fireEvent('valid', this);
8171     },
8172     
8173      /**
8174      * Mark this field as invalid
8175      * @param {String} msg The validation message
8176      */
8177     markInvalid : function(msg){
8178         if(!this.el  || this.preventMark){ // not rendered
8179             return;
8180         }
8181         
8182         this.el.removeClass([this.invalidClass, this.validClass]);
8183         
8184         if(this.disabled || this.allowBlank){
8185             return;
8186         }
8187         
8188         this.el.addClass(this.invalidClass);
8189         
8190         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8191             
8192             var feedback = this.el.select('.form-control-feedback', true).first();
8193             
8194             if(feedback){
8195                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8196                 
8197                 if(this.getValue().length){
8198                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8199                 }
8200                 
8201             }
8202             
8203         }
8204         
8205         this.fireEvent('invalid', this, msg);
8206     },
8207     // private
8208     SafariOnKeyDown : function(event)
8209     {
8210         // this is a workaround for a password hang bug on chrome/ webkit.
8211         
8212         var isSelectAll = false;
8213         
8214         if(this.inputEl().dom.selectionEnd > 0){
8215             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8216         }
8217         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8218             event.preventDefault();
8219             this.setValue('');
8220             return;
8221         }
8222         
8223         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8224             
8225             event.preventDefault();
8226             // this is very hacky as keydown always get's upper case.
8227             //
8228             var cc = String.fromCharCode(event.getCharCode());
8229             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8230             
8231         }
8232     },
8233     adjustWidth : function(tag, w){
8234         tag = tag.toLowerCase();
8235         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8236             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8237                 if(tag == 'input'){
8238                     return w + 2;
8239                 }
8240                 if(tag == 'textarea'){
8241                     return w-2;
8242                 }
8243             }else if(Roo.isOpera){
8244                 if(tag == 'input'){
8245                     return w + 2;
8246                 }
8247                 if(tag == 'textarea'){
8248                     return w-2;
8249                 }
8250             }
8251         }
8252         return w;
8253     }
8254     
8255 });
8256
8257  
8258 /*
8259  * - LGPL
8260  *
8261  * Input
8262  * 
8263  */
8264
8265 /**
8266  * @class Roo.bootstrap.TextArea
8267  * @extends Roo.bootstrap.Input
8268  * Bootstrap TextArea class
8269  * @cfg {Number} cols Specifies the visible width of a text area
8270  * @cfg {Number} rows Specifies the visible number of lines in a text area
8271  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8272  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8273  * @cfg {string} html text
8274  * 
8275  * @constructor
8276  * Create a new TextArea
8277  * @param {Object} config The config object
8278  */
8279
8280 Roo.bootstrap.TextArea = function(config){
8281     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8282    
8283 };
8284
8285 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8286      
8287     cols : false,
8288     rows : 5,
8289     readOnly : false,
8290     warp : 'soft',
8291     resize : false,
8292     value: false,
8293     html: false,
8294     
8295     getAutoCreate : function(){
8296         
8297         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8298         
8299         var id = Roo.id();
8300         
8301         var cfg = {};
8302         
8303         var input =  {
8304             tag: 'textarea',
8305             id : id,
8306             warp : this.warp,
8307             rows : this.rows,
8308             value : this.value || '',
8309             html: this.html || '',
8310             cls : 'form-control',
8311             placeholder : this.placeholder || '' 
8312             
8313         };
8314         
8315         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8316             input.maxLength = this.maxLength;
8317         }
8318         
8319         if(this.resize){
8320             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8321         }
8322         
8323         if(this.cols){
8324             input.cols = this.cols;
8325         }
8326         
8327         if (this.readOnly) {
8328             input.readonly = true;
8329         }
8330         
8331         if (this.name) {
8332             input.name = this.name;
8333         }
8334         
8335         if (this.size) {
8336             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8337         }
8338         
8339         var settings=this;
8340         ['xs','sm','md','lg'].map(function(size){
8341             if (settings[size]) {
8342                 cfg.cls += ' col-' + size + '-' + settings[size];
8343             }
8344         });
8345         
8346         var inputblock = input;
8347         
8348         if(this.hasFeedback && !this.allowBlank){
8349             
8350             var feedback = {
8351                 tag: 'span',
8352                 cls: 'glyphicon form-control-feedback'
8353             };
8354
8355             inputblock = {
8356                 cls : 'has-feedback',
8357                 cn :  [
8358                     input,
8359                     feedback
8360                 ] 
8361             };  
8362         }
8363         
8364         
8365         if (this.before || this.after) {
8366             
8367             inputblock = {
8368                 cls : 'input-group',
8369                 cn :  [] 
8370             };
8371             if (this.before) {
8372                 inputblock.cn.push({
8373                     tag :'span',
8374                     cls : 'input-group-addon',
8375                     html : this.before
8376                 });
8377             }
8378             
8379             inputblock.cn.push(input);
8380             
8381             if(this.hasFeedback && !this.allowBlank){
8382                 inputblock.cls += ' has-feedback';
8383                 inputblock.cn.push(feedback);
8384             }
8385             
8386             if (this.after) {
8387                 inputblock.cn.push({
8388                     tag :'span',
8389                     cls : 'input-group-addon',
8390                     html : this.after
8391                 });
8392             }
8393             
8394         }
8395         
8396         if (align ==='left' && this.fieldLabel.length) {
8397                 Roo.log("left and has label");
8398                 cfg.cn = [
8399                     
8400                     {
8401                         tag: 'label',
8402                         'for' :  id,
8403                         cls : 'control-label col-sm-' + this.labelWidth,
8404                         html : this.fieldLabel
8405                         
8406                     },
8407                     {
8408                         cls : "col-sm-" + (12 - this.labelWidth), 
8409                         cn: [
8410                             inputblock
8411                         ]
8412                     }
8413                     
8414                 ];
8415         } else if ( this.fieldLabel.length) {
8416                 Roo.log(" label");
8417                  cfg.cn = [
8418                    
8419                     {
8420                         tag: 'label',
8421                         //cls : 'input-group-addon',
8422                         html : this.fieldLabel
8423                         
8424                     },
8425                     
8426                     inputblock
8427                     
8428                 ];
8429
8430         } else {
8431             
8432                    Roo.log(" no label && no align");
8433                 cfg.cn = [
8434                     
8435                         inputblock
8436                     
8437                 ];
8438                 
8439                 
8440         }
8441         
8442         if (this.disabled) {
8443             input.disabled=true;
8444         }
8445         
8446         return cfg;
8447         
8448     },
8449     /**
8450      * return the real textarea element.
8451      */
8452     inputEl: function ()
8453     {
8454         return this.el.select('textarea.form-control',true).first();
8455     }
8456 });
8457
8458  
8459 /*
8460  * - LGPL
8461  *
8462  * trigger field - base class for combo..
8463  * 
8464  */
8465  
8466 /**
8467  * @class Roo.bootstrap.TriggerField
8468  * @extends Roo.bootstrap.Input
8469  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8470  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8471  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8472  * for which you can provide a custom implementation.  For example:
8473  * <pre><code>
8474 var trigger = new Roo.bootstrap.TriggerField();
8475 trigger.onTriggerClick = myTriggerFn;
8476 trigger.applyTo('my-field');
8477 </code></pre>
8478  *
8479  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8480  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8481  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8482  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8483  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8484
8485  * @constructor
8486  * Create a new TriggerField.
8487  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8488  * to the base TextField)
8489  */
8490 Roo.bootstrap.TriggerField = function(config){
8491     this.mimicing = false;
8492     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8493 };
8494
8495 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8496     /**
8497      * @cfg {String} triggerClass A CSS class to apply to the trigger
8498      */
8499      /**
8500      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8501      */
8502     hideTrigger:false,
8503
8504     /**
8505      * @cfg {Boolean} removable (true|false) special filter default false
8506      */
8507     removable : false,
8508     
8509     /** @cfg {Boolean} grow @hide */
8510     /** @cfg {Number} growMin @hide */
8511     /** @cfg {Number} growMax @hide */
8512
8513     /**
8514      * @hide 
8515      * @method
8516      */
8517     autoSize: Roo.emptyFn,
8518     // private
8519     monitorTab : true,
8520     // private
8521     deferHeight : true,
8522
8523     
8524     actionMode : 'wrap',
8525     
8526     caret : false,
8527     
8528     
8529     getAutoCreate : function(){
8530        
8531         var align = this.labelAlign || this.parentLabelAlign();
8532         
8533         var id = Roo.id();
8534         
8535         var cfg = {
8536             cls: 'form-group' //input-group
8537         };
8538         
8539         
8540         var input =  {
8541             tag: 'input',
8542             id : id,
8543             type : this.inputType,
8544             cls : 'form-control',
8545             autocomplete: 'new-password',
8546             placeholder : this.placeholder || '' 
8547             
8548         };
8549         if (this.name) {
8550             input.name = this.name;
8551         }
8552         if (this.size) {
8553             input.cls += ' input-' + this.size;
8554         }
8555         
8556         if (this.disabled) {
8557             input.disabled=true;
8558         }
8559         
8560         var inputblock = input;
8561         
8562         if(this.hasFeedback && !this.allowBlank){
8563             
8564             var feedback = {
8565                 tag: 'span',
8566                 cls: 'glyphicon form-control-feedback'
8567             };
8568             
8569             if(this.removable && !this.editable && !this.tickable){
8570                 inputblock = {
8571                     cls : 'has-feedback',
8572                     cn :  [
8573                         inputblock,
8574                         {
8575                             tag: 'button',
8576                             html : 'x',
8577                             cls : 'roo-combo-removable-btn close'
8578                         },
8579                         feedback
8580                     ] 
8581                 };
8582             } else {
8583                 inputblock = {
8584                     cls : 'has-feedback',
8585                     cn :  [
8586                         inputblock,
8587                         feedback
8588                     ] 
8589                 };
8590             }
8591
8592         } else {
8593             if(this.removable && !this.editable && !this.tickable){
8594                 inputblock = {
8595                     cls : 'roo-removable',
8596                     cn :  [
8597                         inputblock,
8598                         {
8599                             tag: 'button',
8600                             html : 'x',
8601                             cls : 'roo-combo-removable-btn close'
8602                         }
8603                     ] 
8604                 };
8605             }
8606         }
8607         
8608         if (this.before || this.after) {
8609             
8610             inputblock = {
8611                 cls : 'input-group',
8612                 cn :  [] 
8613             };
8614             if (this.before) {
8615                 inputblock.cn.push({
8616                     tag :'span',
8617                     cls : 'input-group-addon',
8618                     html : this.before
8619                 });
8620             }
8621             
8622             inputblock.cn.push(input);
8623             
8624             if(this.hasFeedback && !this.allowBlank){
8625                 inputblock.cls += ' has-feedback';
8626                 inputblock.cn.push(feedback);
8627             }
8628             
8629             if (this.after) {
8630                 inputblock.cn.push({
8631                     tag :'span',
8632                     cls : 'input-group-addon',
8633                     html : this.after
8634                 });
8635             }
8636             
8637         };
8638         
8639         var box = {
8640             tag: 'div',
8641             cn: [
8642                 {
8643                     tag: 'input',
8644                     type : 'hidden',
8645                     cls: 'form-hidden-field'
8646                 },
8647                 inputblock
8648             ]
8649             
8650         };
8651         
8652         if(this.multiple){
8653             Roo.log('multiple');
8654             
8655             box = {
8656                 tag: 'div',
8657                 cn: [
8658                     {
8659                         tag: 'input',
8660                         type : 'hidden',
8661                         cls: 'form-hidden-field'
8662                     },
8663                     {
8664                         tag: 'ul',
8665                         cls: 'select2-choices',
8666                         cn:[
8667                             {
8668                                 tag: 'li',
8669                                 cls: 'select2-search-field',
8670                                 cn: [
8671
8672                                     inputblock
8673                                 ]
8674                             }
8675                         ]
8676                     }
8677                 ]
8678             }
8679         };
8680         
8681         var combobox = {
8682             cls: 'select2-container input-group',
8683             cn: [
8684                 box
8685 //                {
8686 //                    tag: 'ul',
8687 //                    cls: 'typeahead typeahead-long dropdown-menu',
8688 //                    style: 'display:none'
8689 //                }
8690             ]
8691         };
8692         
8693         if(!this.multiple && this.showToggleBtn){
8694             
8695             var caret = {
8696                         tag: 'span',
8697                         cls: 'caret'
8698              };
8699             if (this.caret != false) {
8700                 caret = {
8701                      tag: 'i',
8702                      cls: 'fa fa-' + this.caret
8703                 };
8704                 
8705             }
8706             
8707             combobox.cn.push({
8708                 tag :'span',
8709                 cls : 'input-group-addon btn dropdown-toggle',
8710                 cn : [
8711                     caret,
8712                     {
8713                         tag: 'span',
8714                         cls: 'combobox-clear',
8715                         cn  : [
8716                             {
8717                                 tag : 'i',
8718                                 cls: 'icon-remove'
8719                             }
8720                         ]
8721                     }
8722                 ]
8723
8724             })
8725         }
8726         
8727         if(this.multiple){
8728             combobox.cls += ' select2-container-multi';
8729         }
8730         
8731         if (align ==='left' && this.fieldLabel.length) {
8732             
8733                 Roo.log("left and has label");
8734                 cfg.cn = [
8735                     
8736                     {
8737                         tag: 'label',
8738                         'for' :  id,
8739                         cls : 'control-label col-sm-' + this.labelWidth,
8740                         html : this.fieldLabel
8741                         
8742                     },
8743                     {
8744                         cls : "col-sm-" + (12 - this.labelWidth), 
8745                         cn: [
8746                             combobox
8747                         ]
8748                     }
8749                     
8750                 ];
8751         } else if ( this.fieldLabel.length) {
8752                 Roo.log(" label");
8753                  cfg.cn = [
8754                    
8755                     {
8756                         tag: 'label',
8757                         //cls : 'input-group-addon',
8758                         html : this.fieldLabel
8759                         
8760                     },
8761                     
8762                     combobox
8763                     
8764                 ];
8765
8766         } else {
8767             
8768                 Roo.log(" no label && no align");
8769                 cfg = combobox
8770                      
8771                 
8772         }
8773          
8774         var settings=this;
8775         ['xs','sm','md','lg'].map(function(size){
8776             if (settings[size]) {
8777                 cfg.cls += ' col-' + size + '-' + settings[size];
8778             }
8779         });
8780         Roo.log(cfg);
8781         return cfg;
8782         
8783     },
8784     
8785     
8786     
8787     // private
8788     onResize : function(w, h){
8789 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8790 //        if(typeof w == 'number'){
8791 //            var x = w - this.trigger.getWidth();
8792 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8793 //            this.trigger.setStyle('left', x+'px');
8794 //        }
8795     },
8796
8797     // private
8798     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8799
8800     // private
8801     getResizeEl : function(){
8802         return this.inputEl();
8803     },
8804
8805     // private
8806     getPositionEl : function(){
8807         return this.inputEl();
8808     },
8809
8810     // private
8811     alignErrorIcon : function(){
8812         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8813     },
8814
8815     // private
8816     initEvents : function(){
8817         
8818         this.createList();
8819         
8820         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8821         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8822         if(!this.multiple && this.showToggleBtn){
8823             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8824             if(this.hideTrigger){
8825                 this.trigger.setDisplayed(false);
8826             }
8827             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8828         }
8829         
8830         if(this.multiple){
8831             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8832         }
8833         
8834         if(this.removable && !this.editable && !this.tickable){
8835             var close = this.closeTriggerEl();
8836             
8837             if(close){
8838                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8839                 close.on('click', this.removeBtnClick, this, close);
8840             }
8841         }
8842         
8843         //this.trigger.addClassOnOver('x-form-trigger-over');
8844         //this.trigger.addClassOnClick('x-form-trigger-click');
8845         
8846         //if(!this.width){
8847         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8848         //}
8849     },
8850     
8851     closeTriggerEl : function()
8852     {
8853         var close = this.el.select('.roo-combo-removable-btn', true).first();
8854         return close ? close : false;
8855     },
8856     
8857     removeBtnClick : function(e, h, el)
8858     {
8859         e.preventDefault();
8860         
8861         if(this.fireEvent("remove", this) !== false){
8862             this.reset();
8863         }
8864     },
8865     
8866     createList : function()
8867     {
8868         this.list = Roo.get(document.body).createChild({
8869             tag: 'ul',
8870             cls: 'typeahead typeahead-long dropdown-menu',
8871             style: 'display:none'
8872         });
8873         
8874         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8875         
8876     },
8877
8878     // private
8879     initTrigger : function(){
8880        
8881     },
8882
8883     // private
8884     onDestroy : function(){
8885         if(this.trigger){
8886             this.trigger.removeAllListeners();
8887           //  this.trigger.remove();
8888         }
8889         //if(this.wrap){
8890         //    this.wrap.remove();
8891         //}
8892         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8893     },
8894
8895     // private
8896     onFocus : function(){
8897         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8898         /*
8899         if(!this.mimicing){
8900             this.wrap.addClass('x-trigger-wrap-focus');
8901             this.mimicing = true;
8902             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8903             if(this.monitorTab){
8904                 this.el.on("keydown", this.checkTab, this);
8905             }
8906         }
8907         */
8908     },
8909
8910     // private
8911     checkTab : function(e){
8912         if(e.getKey() == e.TAB){
8913             this.triggerBlur();
8914         }
8915     },
8916
8917     // private
8918     onBlur : function(){
8919         // do nothing
8920     },
8921
8922     // private
8923     mimicBlur : function(e, t){
8924         /*
8925         if(!this.wrap.contains(t) && this.validateBlur()){
8926             this.triggerBlur();
8927         }
8928         */
8929     },
8930
8931     // private
8932     triggerBlur : function(){
8933         this.mimicing = false;
8934         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8935         if(this.monitorTab){
8936             this.el.un("keydown", this.checkTab, this);
8937         }
8938         //this.wrap.removeClass('x-trigger-wrap-focus');
8939         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8940     },
8941
8942     // private
8943     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8944     validateBlur : function(e, t){
8945         return true;
8946     },
8947
8948     // private
8949     onDisable : function(){
8950         this.inputEl().dom.disabled = true;
8951         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8952         //if(this.wrap){
8953         //    this.wrap.addClass('x-item-disabled');
8954         //}
8955     },
8956
8957     // private
8958     onEnable : function(){
8959         this.inputEl().dom.disabled = false;
8960         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8961         //if(this.wrap){
8962         //    this.el.removeClass('x-item-disabled');
8963         //}
8964     },
8965
8966     // private
8967     onShow : function(){
8968         var ae = this.getActionEl();
8969         
8970         if(ae){
8971             ae.dom.style.display = '';
8972             ae.dom.style.visibility = 'visible';
8973         }
8974     },
8975
8976     // private
8977     
8978     onHide : function(){
8979         var ae = this.getActionEl();
8980         ae.dom.style.display = 'none';
8981     },
8982
8983     /**
8984      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8985      * by an implementing function.
8986      * @method
8987      * @param {EventObject} e
8988      */
8989     onTriggerClick : Roo.emptyFn
8990 });
8991  /*
8992  * Based on:
8993  * Ext JS Library 1.1.1
8994  * Copyright(c) 2006-2007, Ext JS, LLC.
8995  *
8996  * Originally Released Under LGPL - original licence link has changed is not relivant.
8997  *
8998  * Fork - LGPL
8999  * <script type="text/javascript">
9000  */
9001
9002
9003 /**
9004  * @class Roo.data.SortTypes
9005  * @singleton
9006  * Defines the default sorting (casting?) comparison functions used when sorting data.
9007  */
9008 Roo.data.SortTypes = {
9009     /**
9010      * Default sort that does nothing
9011      * @param {Mixed} s The value being converted
9012      * @return {Mixed} The comparison value
9013      */
9014     none : function(s){
9015         return s;
9016     },
9017     
9018     /**
9019      * The regular expression used to strip tags
9020      * @type {RegExp}
9021      * @property
9022      */
9023     stripTagsRE : /<\/?[^>]+>/gi,
9024     
9025     /**
9026      * Strips all HTML tags to sort on text only
9027      * @param {Mixed} s The value being converted
9028      * @return {String} The comparison value
9029      */
9030     asText : function(s){
9031         return String(s).replace(this.stripTagsRE, "");
9032     },
9033     
9034     /**
9035      * Strips all HTML tags to sort on text only - Case insensitive
9036      * @param {Mixed} s The value being converted
9037      * @return {String} The comparison value
9038      */
9039     asUCText : function(s){
9040         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9041     },
9042     
9043     /**
9044      * Case insensitive string
9045      * @param {Mixed} s The value being converted
9046      * @return {String} The comparison value
9047      */
9048     asUCString : function(s) {
9049         return String(s).toUpperCase();
9050     },
9051     
9052     /**
9053      * Date sorting
9054      * @param {Mixed} s The value being converted
9055      * @return {Number} The comparison value
9056      */
9057     asDate : function(s) {
9058         if(!s){
9059             return 0;
9060         }
9061         if(s instanceof Date){
9062             return s.getTime();
9063         }
9064         return Date.parse(String(s));
9065     },
9066     
9067     /**
9068      * Float sorting
9069      * @param {Mixed} s The value being converted
9070      * @return {Float} The comparison value
9071      */
9072     asFloat : function(s) {
9073         var val = parseFloat(String(s).replace(/,/g, ""));
9074         if(isNaN(val)) val = 0;
9075         return val;
9076     },
9077     
9078     /**
9079      * Integer sorting
9080      * @param {Mixed} s The value being converted
9081      * @return {Number} The comparison value
9082      */
9083     asInt : function(s) {
9084         var val = parseInt(String(s).replace(/,/g, ""));
9085         if(isNaN(val)) val = 0;
9086         return val;
9087     }
9088 };/*
9089  * Based on:
9090  * Ext JS Library 1.1.1
9091  * Copyright(c) 2006-2007, Ext JS, LLC.
9092  *
9093  * Originally Released Under LGPL - original licence link has changed is not relivant.
9094  *
9095  * Fork - LGPL
9096  * <script type="text/javascript">
9097  */
9098
9099 /**
9100 * @class Roo.data.Record
9101  * Instances of this class encapsulate both record <em>definition</em> information, and record
9102  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9103  * to access Records cached in an {@link Roo.data.Store} object.<br>
9104  * <p>
9105  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9106  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9107  * objects.<br>
9108  * <p>
9109  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9110  * @constructor
9111  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9112  * {@link #create}. The parameters are the same.
9113  * @param {Array} data An associative Array of data values keyed by the field name.
9114  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9115  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9116  * not specified an integer id is generated.
9117  */
9118 Roo.data.Record = function(data, id){
9119     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9120     this.data = data;
9121 };
9122
9123 /**
9124  * Generate a constructor for a specific record layout.
9125  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9126  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9127  * Each field definition object may contain the following properties: <ul>
9128  * <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,
9129  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9130  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9131  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9132  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9133  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9134  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9135  * this may be omitted.</p></li>
9136  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9137  * <ul><li>auto (Default, implies no conversion)</li>
9138  * <li>string</li>
9139  * <li>int</li>
9140  * <li>float</li>
9141  * <li>boolean</li>
9142  * <li>date</li></ul></p></li>
9143  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9144  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9145  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9146  * by the Reader into an object that will be stored in the Record. It is passed the
9147  * following parameters:<ul>
9148  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9149  * </ul></p></li>
9150  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9151  * </ul>
9152  * <br>usage:<br><pre><code>
9153 var TopicRecord = Roo.data.Record.create(
9154     {name: 'title', mapping: 'topic_title'},
9155     {name: 'author', mapping: 'username'},
9156     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9157     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9158     {name: 'lastPoster', mapping: 'user2'},
9159     {name: 'excerpt', mapping: 'post_text'}
9160 );
9161
9162 var myNewRecord = new TopicRecord({
9163     title: 'Do my job please',
9164     author: 'noobie',
9165     totalPosts: 1,
9166     lastPost: new Date(),
9167     lastPoster: 'Animal',
9168     excerpt: 'No way dude!'
9169 });
9170 myStore.add(myNewRecord);
9171 </code></pre>
9172  * @method create
9173  * @static
9174  */
9175 Roo.data.Record.create = function(o){
9176     var f = function(){
9177         f.superclass.constructor.apply(this, arguments);
9178     };
9179     Roo.extend(f, Roo.data.Record);
9180     var p = f.prototype;
9181     p.fields = new Roo.util.MixedCollection(false, function(field){
9182         return field.name;
9183     });
9184     for(var i = 0, len = o.length; i < len; i++){
9185         p.fields.add(new Roo.data.Field(o[i]));
9186     }
9187     f.getField = function(name){
9188         return p.fields.get(name);  
9189     };
9190     return f;
9191 };
9192
9193 Roo.data.Record.AUTO_ID = 1000;
9194 Roo.data.Record.EDIT = 'edit';
9195 Roo.data.Record.REJECT = 'reject';
9196 Roo.data.Record.COMMIT = 'commit';
9197
9198 Roo.data.Record.prototype = {
9199     /**
9200      * Readonly flag - true if this record has been modified.
9201      * @type Boolean
9202      */
9203     dirty : false,
9204     editing : false,
9205     error: null,
9206     modified: null,
9207
9208     // private
9209     join : function(store){
9210         this.store = store;
9211     },
9212
9213     /**
9214      * Set the named field to the specified value.
9215      * @param {String} name The name of the field to set.
9216      * @param {Object} value The value to set the field to.
9217      */
9218     set : function(name, value){
9219         if(this.data[name] == value){
9220             return;
9221         }
9222         this.dirty = true;
9223         if(!this.modified){
9224             this.modified = {};
9225         }
9226         if(typeof this.modified[name] == 'undefined'){
9227             this.modified[name] = this.data[name];
9228         }
9229         this.data[name] = value;
9230         if(!this.editing && this.store){
9231             this.store.afterEdit(this);
9232         }       
9233     },
9234
9235     /**
9236      * Get the value of the named field.
9237      * @param {String} name The name of the field to get the value of.
9238      * @return {Object} The value of the field.
9239      */
9240     get : function(name){
9241         return this.data[name]; 
9242     },
9243
9244     // private
9245     beginEdit : function(){
9246         this.editing = true;
9247         this.modified = {}; 
9248     },
9249
9250     // private
9251     cancelEdit : function(){
9252         this.editing = false;
9253         delete this.modified;
9254     },
9255
9256     // private
9257     endEdit : function(){
9258         this.editing = false;
9259         if(this.dirty && this.store){
9260             this.store.afterEdit(this);
9261         }
9262     },
9263
9264     /**
9265      * Usually called by the {@link Roo.data.Store} which owns the Record.
9266      * Rejects all changes made to the Record since either creation, or the last commit operation.
9267      * Modified fields are reverted to their original values.
9268      * <p>
9269      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9270      * of reject operations.
9271      */
9272     reject : function(){
9273         var m = this.modified;
9274         for(var n in m){
9275             if(typeof m[n] != "function"){
9276                 this.data[n] = m[n];
9277             }
9278         }
9279         this.dirty = false;
9280         delete this.modified;
9281         this.editing = false;
9282         if(this.store){
9283             this.store.afterReject(this);
9284         }
9285     },
9286
9287     /**
9288      * Usually called by the {@link Roo.data.Store} which owns the Record.
9289      * Commits all changes made to the Record since either creation, or the last commit operation.
9290      * <p>
9291      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9292      * of commit operations.
9293      */
9294     commit : function(){
9295         this.dirty = false;
9296         delete this.modified;
9297         this.editing = false;
9298         if(this.store){
9299             this.store.afterCommit(this);
9300         }
9301     },
9302
9303     // private
9304     hasError : function(){
9305         return this.error != null;
9306     },
9307
9308     // private
9309     clearError : function(){
9310         this.error = null;
9311     },
9312
9313     /**
9314      * Creates a copy of this record.
9315      * @param {String} id (optional) A new record id if you don't want to use this record's id
9316      * @return {Record}
9317      */
9318     copy : function(newId) {
9319         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9320     }
9321 };/*
9322  * Based on:
9323  * Ext JS Library 1.1.1
9324  * Copyright(c) 2006-2007, Ext JS, LLC.
9325  *
9326  * Originally Released Under LGPL - original licence link has changed is not relivant.
9327  *
9328  * Fork - LGPL
9329  * <script type="text/javascript">
9330  */
9331
9332
9333
9334 /**
9335  * @class Roo.data.Store
9336  * @extends Roo.util.Observable
9337  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9338  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9339  * <p>
9340  * 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
9341  * has no knowledge of the format of the data returned by the Proxy.<br>
9342  * <p>
9343  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9344  * instances from the data object. These records are cached and made available through accessor functions.
9345  * @constructor
9346  * Creates a new Store.
9347  * @param {Object} config A config object containing the objects needed for the Store to access data,
9348  * and read the data into Records.
9349  */
9350 Roo.data.Store = function(config){
9351     this.data = new Roo.util.MixedCollection(false);
9352     this.data.getKey = function(o){
9353         return o.id;
9354     };
9355     this.baseParams = {};
9356     // private
9357     this.paramNames = {
9358         "start" : "start",
9359         "limit" : "limit",
9360         "sort" : "sort",
9361         "dir" : "dir",
9362         "multisort" : "_multisort"
9363     };
9364
9365     if(config && config.data){
9366         this.inlineData = config.data;
9367         delete config.data;
9368     }
9369
9370     Roo.apply(this, config);
9371     
9372     if(this.reader){ // reader passed
9373         this.reader = Roo.factory(this.reader, Roo.data);
9374         this.reader.xmodule = this.xmodule || false;
9375         if(!this.recordType){
9376             this.recordType = this.reader.recordType;
9377         }
9378         if(this.reader.onMetaChange){
9379             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9380         }
9381     }
9382
9383     if(this.recordType){
9384         this.fields = this.recordType.prototype.fields;
9385     }
9386     this.modified = [];
9387
9388     this.addEvents({
9389         /**
9390          * @event datachanged
9391          * Fires when the data cache has changed, and a widget which is using this Store
9392          * as a Record cache should refresh its view.
9393          * @param {Store} this
9394          */
9395         datachanged : true,
9396         /**
9397          * @event metachange
9398          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9399          * @param {Store} this
9400          * @param {Object} meta The JSON metadata
9401          */
9402         metachange : true,
9403         /**
9404          * @event add
9405          * Fires when Records have been added to the Store
9406          * @param {Store} this
9407          * @param {Roo.data.Record[]} records The array of Records added
9408          * @param {Number} index The index at which the record(s) were added
9409          */
9410         add : true,
9411         /**
9412          * @event remove
9413          * Fires when a Record has been removed from the Store
9414          * @param {Store} this
9415          * @param {Roo.data.Record} record The Record that was removed
9416          * @param {Number} index The index at which the record was removed
9417          */
9418         remove : true,
9419         /**
9420          * @event update
9421          * Fires when a Record has been updated
9422          * @param {Store} this
9423          * @param {Roo.data.Record} record The Record that was updated
9424          * @param {String} operation The update operation being performed.  Value may be one of:
9425          * <pre><code>
9426  Roo.data.Record.EDIT
9427  Roo.data.Record.REJECT
9428  Roo.data.Record.COMMIT
9429          * </code></pre>
9430          */
9431         update : true,
9432         /**
9433          * @event clear
9434          * Fires when the data cache has been cleared.
9435          * @param {Store} this
9436          */
9437         clear : true,
9438         /**
9439          * @event beforeload
9440          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9441          * the load action will be canceled.
9442          * @param {Store} this
9443          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9444          */
9445         beforeload : true,
9446         /**
9447          * @event beforeloadadd
9448          * Fires after a new set of Records has been loaded.
9449          * @param {Store} this
9450          * @param {Roo.data.Record[]} records The Records that were loaded
9451          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9452          */
9453         beforeloadadd : true,
9454         /**
9455          * @event load
9456          * Fires after a new set of Records has been loaded, before they are added to the store.
9457          * @param {Store} this
9458          * @param {Roo.data.Record[]} records The Records that were loaded
9459          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9460          * @params {Object} return from reader
9461          */
9462         load : true,
9463         /**
9464          * @event loadexception
9465          * Fires if an exception occurs in the Proxy during loading.
9466          * Called with the signature of the Proxy's "loadexception" event.
9467          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9468          * 
9469          * @param {Proxy} 
9470          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9471          * @param {Object} load options 
9472          * @param {Object} jsonData from your request (normally this contains the Exception)
9473          */
9474         loadexception : true
9475     });
9476     
9477     if(this.proxy){
9478         this.proxy = Roo.factory(this.proxy, Roo.data);
9479         this.proxy.xmodule = this.xmodule || false;
9480         this.relayEvents(this.proxy,  ["loadexception"]);
9481     }
9482     this.sortToggle = {};
9483     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9484
9485     Roo.data.Store.superclass.constructor.call(this);
9486
9487     if(this.inlineData){
9488         this.loadData(this.inlineData);
9489         delete this.inlineData;
9490     }
9491 };
9492
9493 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9494      /**
9495     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9496     * without a remote query - used by combo/forms at present.
9497     */
9498     
9499     /**
9500     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9501     */
9502     /**
9503     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9504     */
9505     /**
9506     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9507     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9508     */
9509     /**
9510     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9511     * on any HTTP request
9512     */
9513     /**
9514     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9515     */
9516     /**
9517     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9518     */
9519     multiSort: false,
9520     /**
9521     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9522     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9523     */
9524     remoteSort : false,
9525
9526     /**
9527     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9528      * loaded or when a record is removed. (defaults to false).
9529     */
9530     pruneModifiedRecords : false,
9531
9532     // private
9533     lastOptions : null,
9534
9535     /**
9536      * Add Records to the Store and fires the add event.
9537      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9538      */
9539     add : function(records){
9540         records = [].concat(records);
9541         for(var i = 0, len = records.length; i < len; i++){
9542             records[i].join(this);
9543         }
9544         var index = this.data.length;
9545         this.data.addAll(records);
9546         this.fireEvent("add", this, records, index);
9547     },
9548
9549     /**
9550      * Remove a Record from the Store and fires the remove event.
9551      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9552      */
9553     remove : function(record){
9554         var index = this.data.indexOf(record);
9555         this.data.removeAt(index);
9556         if(this.pruneModifiedRecords){
9557             this.modified.remove(record);
9558         }
9559         this.fireEvent("remove", this, record, index);
9560     },
9561
9562     /**
9563      * Remove all Records from the Store and fires the clear event.
9564      */
9565     removeAll : function(){
9566         this.data.clear();
9567         if(this.pruneModifiedRecords){
9568             this.modified = [];
9569         }
9570         this.fireEvent("clear", this);
9571     },
9572
9573     /**
9574      * Inserts Records to the Store at the given index and fires the add event.
9575      * @param {Number} index The start index at which to insert the passed Records.
9576      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9577      */
9578     insert : function(index, records){
9579         records = [].concat(records);
9580         for(var i = 0, len = records.length; i < len; i++){
9581             this.data.insert(index, records[i]);
9582             records[i].join(this);
9583         }
9584         this.fireEvent("add", this, records, index);
9585     },
9586
9587     /**
9588      * Get the index within the cache of the passed Record.
9589      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9590      * @return {Number} The index of the passed Record. Returns -1 if not found.
9591      */
9592     indexOf : function(record){
9593         return this.data.indexOf(record);
9594     },
9595
9596     /**
9597      * Get the index within the cache of the Record with the passed id.
9598      * @param {String} id The id of the Record to find.
9599      * @return {Number} The index of the Record. Returns -1 if not found.
9600      */
9601     indexOfId : function(id){
9602         return this.data.indexOfKey(id);
9603     },
9604
9605     /**
9606      * Get the Record with the specified id.
9607      * @param {String} id The id of the Record to find.
9608      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9609      */
9610     getById : function(id){
9611         return this.data.key(id);
9612     },
9613
9614     /**
9615      * Get the Record at the specified index.
9616      * @param {Number} index The index of the Record to find.
9617      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9618      */
9619     getAt : function(index){
9620         return this.data.itemAt(index);
9621     },
9622
9623     /**
9624      * Returns a range of Records between specified indices.
9625      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9626      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9627      * @return {Roo.data.Record[]} An array of Records
9628      */
9629     getRange : function(start, end){
9630         return this.data.getRange(start, end);
9631     },
9632
9633     // private
9634     storeOptions : function(o){
9635         o = Roo.apply({}, o);
9636         delete o.callback;
9637         delete o.scope;
9638         this.lastOptions = o;
9639     },
9640
9641     /**
9642      * Loads the Record cache from the configured Proxy using the configured Reader.
9643      * <p>
9644      * If using remote paging, then the first load call must specify the <em>start</em>
9645      * and <em>limit</em> properties in the options.params property to establish the initial
9646      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9647      * <p>
9648      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9649      * and this call will return before the new data has been loaded. Perform any post-processing
9650      * in a callback function, or in a "load" event handler.</strong>
9651      * <p>
9652      * @param {Object} options An object containing properties which control loading options:<ul>
9653      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9654      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9655      * passed the following arguments:<ul>
9656      * <li>r : Roo.data.Record[]</li>
9657      * <li>options: Options object from the load call</li>
9658      * <li>success: Boolean success indicator</li></ul></li>
9659      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9660      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9661      * </ul>
9662      */
9663     load : function(options){
9664         options = options || {};
9665         if(this.fireEvent("beforeload", this, options) !== false){
9666             this.storeOptions(options);
9667             var p = Roo.apply(options.params || {}, this.baseParams);
9668             // if meta was not loaded from remote source.. try requesting it.
9669             if (!this.reader.metaFromRemote) {
9670                 p._requestMeta = 1;
9671             }
9672             if(this.sortInfo && this.remoteSort){
9673                 var pn = this.paramNames;
9674                 p[pn["sort"]] = this.sortInfo.field;
9675                 p[pn["dir"]] = this.sortInfo.direction;
9676             }
9677             if (this.multiSort) {
9678                 var pn = this.paramNames;
9679                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9680             }
9681             
9682             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9683         }
9684     },
9685
9686     /**
9687      * Reloads the Record cache from the configured Proxy using the configured Reader and
9688      * the options from the last load operation performed.
9689      * @param {Object} options (optional) An object containing properties which may override the options
9690      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9691      * the most recently used options are reused).
9692      */
9693     reload : function(options){
9694         this.load(Roo.applyIf(options||{}, this.lastOptions));
9695     },
9696
9697     // private
9698     // Called as a callback by the Reader during a load operation.
9699     loadRecords : function(o, options, success){
9700         if(!o || success === false){
9701             if(success !== false){
9702                 this.fireEvent("load", this, [], options, o);
9703             }
9704             if(options.callback){
9705                 options.callback.call(options.scope || this, [], options, false);
9706             }
9707             return;
9708         }
9709         // if data returned failure - throw an exception.
9710         if (o.success === false) {
9711             // show a message if no listener is registered.
9712             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9713                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9714             }
9715             // loadmask wil be hooked into this..
9716             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9717             return;
9718         }
9719         var r = o.records, t = o.totalRecords || r.length;
9720         
9721         this.fireEvent("beforeloadadd", this, r, options, o);
9722         
9723         if(!options || options.add !== true){
9724             if(this.pruneModifiedRecords){
9725                 this.modified = [];
9726             }
9727             for(var i = 0, len = r.length; i < len; i++){
9728                 r[i].join(this);
9729             }
9730             if(this.snapshot){
9731                 this.data = this.snapshot;
9732                 delete this.snapshot;
9733             }
9734             this.data.clear();
9735             this.data.addAll(r);
9736             this.totalLength = t;
9737             this.applySort();
9738             this.fireEvent("datachanged", this);
9739         }else{
9740             this.totalLength = Math.max(t, this.data.length+r.length);
9741             this.add(r);
9742         }
9743         this.fireEvent("load", this, r, options, o);
9744         if(options.callback){
9745             options.callback.call(options.scope || this, r, options, true);
9746         }
9747     },
9748
9749
9750     /**
9751      * Loads data from a passed data block. A Reader which understands the format of the data
9752      * must have been configured in the constructor.
9753      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9754      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9755      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9756      */
9757     loadData : function(o, append){
9758         var r = this.reader.readRecords(o);
9759         this.loadRecords(r, {add: append}, true);
9760     },
9761
9762     /**
9763      * Gets the number of cached records.
9764      * <p>
9765      * <em>If using paging, this may not be the total size of the dataset. If the data object
9766      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9767      * the data set size</em>
9768      */
9769     getCount : function(){
9770         return this.data.length || 0;
9771     },
9772
9773     /**
9774      * Gets the total number of records in the dataset as returned by the server.
9775      * <p>
9776      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9777      * the dataset size</em>
9778      */
9779     getTotalCount : function(){
9780         return this.totalLength || 0;
9781     },
9782
9783     /**
9784      * Returns the sort state of the Store as an object with two properties:
9785      * <pre><code>
9786  field {String} The name of the field by which the Records are sorted
9787  direction {String} The sort order, "ASC" or "DESC"
9788      * </code></pre>
9789      */
9790     getSortState : function(){
9791         return this.sortInfo;
9792     },
9793
9794     // private
9795     applySort : function(){
9796         if(this.sortInfo && !this.remoteSort){
9797             var s = this.sortInfo, f = s.field;
9798             var st = this.fields.get(f).sortType;
9799             var fn = function(r1, r2){
9800                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9801                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9802             };
9803             this.data.sort(s.direction, fn);
9804             if(this.snapshot && this.snapshot != this.data){
9805                 this.snapshot.sort(s.direction, fn);
9806             }
9807         }
9808     },
9809
9810     /**
9811      * Sets the default sort column and order to be used by the next load operation.
9812      * @param {String} fieldName The name of the field to sort by.
9813      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9814      */
9815     setDefaultSort : function(field, dir){
9816         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9817     },
9818
9819     /**
9820      * Sort the Records.
9821      * If remote sorting is used, the sort is performed on the server, and the cache is
9822      * reloaded. If local sorting is used, the cache is sorted internally.
9823      * @param {String} fieldName The name of the field to sort by.
9824      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9825      */
9826     sort : function(fieldName, dir){
9827         var f = this.fields.get(fieldName);
9828         if(!dir){
9829             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9830             
9831             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9832                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9833             }else{
9834                 dir = f.sortDir;
9835             }
9836         }
9837         this.sortToggle[f.name] = dir;
9838         this.sortInfo = {field: f.name, direction: dir};
9839         if(!this.remoteSort){
9840             this.applySort();
9841             this.fireEvent("datachanged", this);
9842         }else{
9843             this.load(this.lastOptions);
9844         }
9845     },
9846
9847     /**
9848      * Calls the specified function for each of the Records in the cache.
9849      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9850      * Returning <em>false</em> aborts and exits the iteration.
9851      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9852      */
9853     each : function(fn, scope){
9854         this.data.each(fn, scope);
9855     },
9856
9857     /**
9858      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9859      * (e.g., during paging).
9860      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9861      */
9862     getModifiedRecords : function(){
9863         return this.modified;
9864     },
9865
9866     // private
9867     createFilterFn : function(property, value, anyMatch){
9868         if(!value.exec){ // not a regex
9869             value = String(value);
9870             if(value.length == 0){
9871                 return false;
9872             }
9873             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9874         }
9875         return function(r){
9876             return value.test(r.data[property]);
9877         };
9878     },
9879
9880     /**
9881      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9882      * @param {String} property A field on your records
9883      * @param {Number} start The record index to start at (defaults to 0)
9884      * @param {Number} end The last record index to include (defaults to length - 1)
9885      * @return {Number} The sum
9886      */
9887     sum : function(property, start, end){
9888         var rs = this.data.items, v = 0;
9889         start = start || 0;
9890         end = (end || end === 0) ? end : rs.length-1;
9891
9892         for(var i = start; i <= end; i++){
9893             v += (rs[i].data[property] || 0);
9894         }
9895         return v;
9896     },
9897
9898     /**
9899      * Filter the records by a specified property.
9900      * @param {String} field A field on your records
9901      * @param {String/RegExp} value Either a string that the field
9902      * should start with or a RegExp to test against the field
9903      * @param {Boolean} anyMatch True to match any part not just the beginning
9904      */
9905     filter : function(property, value, anyMatch){
9906         var fn = this.createFilterFn(property, value, anyMatch);
9907         return fn ? this.filterBy(fn) : this.clearFilter();
9908     },
9909
9910     /**
9911      * Filter by a function. The specified function will be called with each
9912      * record in this data source. If the function returns true the record is included,
9913      * otherwise it is filtered.
9914      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9915      * @param {Object} scope (optional) The scope of the function (defaults to this)
9916      */
9917     filterBy : function(fn, scope){
9918         this.snapshot = this.snapshot || this.data;
9919         this.data = this.queryBy(fn, scope||this);
9920         this.fireEvent("datachanged", this);
9921     },
9922
9923     /**
9924      * Query the records by a specified property.
9925      * @param {String} field A field on your records
9926      * @param {String/RegExp} value Either a string that the field
9927      * should start with or a RegExp to test against the field
9928      * @param {Boolean} anyMatch True to match any part not just the beginning
9929      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9930      */
9931     query : function(property, value, anyMatch){
9932         var fn = this.createFilterFn(property, value, anyMatch);
9933         return fn ? this.queryBy(fn) : this.data.clone();
9934     },
9935
9936     /**
9937      * Query by a function. The specified function will be called with each
9938      * record in this data source. If the function returns true the record is included
9939      * in the results.
9940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9941      * @param {Object} scope (optional) The scope of the function (defaults to this)
9942       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9943      **/
9944     queryBy : function(fn, scope){
9945         var data = this.snapshot || this.data;
9946         return data.filterBy(fn, scope||this);
9947     },
9948
9949     /**
9950      * Collects unique values for a particular dataIndex from this store.
9951      * @param {String} dataIndex The property to collect
9952      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9953      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9954      * @return {Array} An array of the unique values
9955      **/
9956     collect : function(dataIndex, allowNull, bypassFilter){
9957         var d = (bypassFilter === true && this.snapshot) ?
9958                 this.snapshot.items : this.data.items;
9959         var v, sv, r = [], l = {};
9960         for(var i = 0, len = d.length; i < len; i++){
9961             v = d[i].data[dataIndex];
9962             sv = String(v);
9963             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9964                 l[sv] = true;
9965                 r[r.length] = v;
9966             }
9967         }
9968         return r;
9969     },
9970
9971     /**
9972      * Revert to a view of the Record cache with no filtering applied.
9973      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9974      */
9975     clearFilter : function(suppressEvent){
9976         if(this.snapshot && this.snapshot != this.data){
9977             this.data = this.snapshot;
9978             delete this.snapshot;
9979             if(suppressEvent !== true){
9980                 this.fireEvent("datachanged", this);
9981             }
9982         }
9983     },
9984
9985     // private
9986     afterEdit : function(record){
9987         if(this.modified.indexOf(record) == -1){
9988             this.modified.push(record);
9989         }
9990         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9991     },
9992     
9993     // private
9994     afterReject : function(record){
9995         this.modified.remove(record);
9996         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9997     },
9998
9999     // private
10000     afterCommit : function(record){
10001         this.modified.remove(record);
10002         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10003     },
10004
10005     /**
10006      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10007      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10008      */
10009     commitChanges : function(){
10010         var m = this.modified.slice(0);
10011         this.modified = [];
10012         for(var i = 0, len = m.length; i < len; i++){
10013             m[i].commit();
10014         }
10015     },
10016
10017     /**
10018      * Cancel outstanding changes on all changed records.
10019      */
10020     rejectChanges : function(){
10021         var m = this.modified.slice(0);
10022         this.modified = [];
10023         for(var i = 0, len = m.length; i < len; i++){
10024             m[i].reject();
10025         }
10026     },
10027
10028     onMetaChange : function(meta, rtype, o){
10029         this.recordType = rtype;
10030         this.fields = rtype.prototype.fields;
10031         delete this.snapshot;
10032         this.sortInfo = meta.sortInfo || this.sortInfo;
10033         this.modified = [];
10034         this.fireEvent('metachange', this, this.reader.meta);
10035     },
10036     
10037     moveIndex : function(data, type)
10038     {
10039         var index = this.indexOf(data);
10040         
10041         var newIndex = index + type;
10042         
10043         this.remove(data);
10044         
10045         this.insert(newIndex, data);
10046         
10047     }
10048 });/*
10049  * Based on:
10050  * Ext JS Library 1.1.1
10051  * Copyright(c) 2006-2007, Ext JS, LLC.
10052  *
10053  * Originally Released Under LGPL - original licence link has changed is not relivant.
10054  *
10055  * Fork - LGPL
10056  * <script type="text/javascript">
10057  */
10058
10059 /**
10060  * @class Roo.data.SimpleStore
10061  * @extends Roo.data.Store
10062  * Small helper class to make creating Stores from Array data easier.
10063  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10064  * @cfg {Array} fields An array of field definition objects, or field name strings.
10065  * @cfg {Array} data The multi-dimensional array of data
10066  * @constructor
10067  * @param {Object} config
10068  */
10069 Roo.data.SimpleStore = function(config){
10070     Roo.data.SimpleStore.superclass.constructor.call(this, {
10071         isLocal : true,
10072         reader: new Roo.data.ArrayReader({
10073                 id: config.id
10074             },
10075             Roo.data.Record.create(config.fields)
10076         ),
10077         proxy : new Roo.data.MemoryProxy(config.data)
10078     });
10079     this.load();
10080 };
10081 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10082  * Based on:
10083  * Ext JS Library 1.1.1
10084  * Copyright(c) 2006-2007, Ext JS, LLC.
10085  *
10086  * Originally Released Under LGPL - original licence link has changed is not relivant.
10087  *
10088  * Fork - LGPL
10089  * <script type="text/javascript">
10090  */
10091
10092 /**
10093 /**
10094  * @extends Roo.data.Store
10095  * @class Roo.data.JsonStore
10096  * Small helper class to make creating Stores for JSON data easier. <br/>
10097 <pre><code>
10098 var store = new Roo.data.JsonStore({
10099     url: 'get-images.php',
10100     root: 'images',
10101     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10102 });
10103 </code></pre>
10104  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10105  * JsonReader and HttpProxy (unless inline data is provided).</b>
10106  * @cfg {Array} fields An array of field definition objects, or field name strings.
10107  * @constructor
10108  * @param {Object} config
10109  */
10110 Roo.data.JsonStore = function(c){
10111     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10112         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10113         reader: new Roo.data.JsonReader(c, c.fields)
10114     }));
10115 };
10116 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10117  * Based on:
10118  * Ext JS Library 1.1.1
10119  * Copyright(c) 2006-2007, Ext JS, LLC.
10120  *
10121  * Originally Released Under LGPL - original licence link has changed is not relivant.
10122  *
10123  * Fork - LGPL
10124  * <script type="text/javascript">
10125  */
10126
10127  
10128 Roo.data.Field = function(config){
10129     if(typeof config == "string"){
10130         config = {name: config};
10131     }
10132     Roo.apply(this, config);
10133     
10134     if(!this.type){
10135         this.type = "auto";
10136     }
10137     
10138     var st = Roo.data.SortTypes;
10139     // named sortTypes are supported, here we look them up
10140     if(typeof this.sortType == "string"){
10141         this.sortType = st[this.sortType];
10142     }
10143     
10144     // set default sortType for strings and dates
10145     if(!this.sortType){
10146         switch(this.type){
10147             case "string":
10148                 this.sortType = st.asUCString;
10149                 break;
10150             case "date":
10151                 this.sortType = st.asDate;
10152                 break;
10153             default:
10154                 this.sortType = st.none;
10155         }
10156     }
10157
10158     // define once
10159     var stripRe = /[\$,%]/g;
10160
10161     // prebuilt conversion function for this field, instead of
10162     // switching every time we're reading a value
10163     if(!this.convert){
10164         var cv, dateFormat = this.dateFormat;
10165         switch(this.type){
10166             case "":
10167             case "auto":
10168             case undefined:
10169                 cv = function(v){ return v; };
10170                 break;
10171             case "string":
10172                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10173                 break;
10174             case "int":
10175                 cv = function(v){
10176                     return v !== undefined && v !== null && v !== '' ?
10177                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10178                     };
10179                 break;
10180             case "float":
10181                 cv = function(v){
10182                     return v !== undefined && v !== null && v !== '' ?
10183                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10184                     };
10185                 break;
10186             case "bool":
10187             case "boolean":
10188                 cv = function(v){ return v === true || v === "true" || v == 1; };
10189                 break;
10190             case "date":
10191                 cv = function(v){
10192                     if(!v){
10193                         return '';
10194                     }
10195                     if(v instanceof Date){
10196                         return v;
10197                     }
10198                     if(dateFormat){
10199                         if(dateFormat == "timestamp"){
10200                             return new Date(v*1000);
10201                         }
10202                         return Date.parseDate(v, dateFormat);
10203                     }
10204                     var parsed = Date.parse(v);
10205                     return parsed ? new Date(parsed) : null;
10206                 };
10207              break;
10208             
10209         }
10210         this.convert = cv;
10211     }
10212 };
10213
10214 Roo.data.Field.prototype = {
10215     dateFormat: null,
10216     defaultValue: "",
10217     mapping: null,
10218     sortType : null,
10219     sortDir : "ASC"
10220 };/*
10221  * Based on:
10222  * Ext JS Library 1.1.1
10223  * Copyright(c) 2006-2007, Ext JS, LLC.
10224  *
10225  * Originally Released Under LGPL - original licence link has changed is not relivant.
10226  *
10227  * Fork - LGPL
10228  * <script type="text/javascript">
10229  */
10230  
10231 // Base class for reading structured data from a data source.  This class is intended to be
10232 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10233
10234 /**
10235  * @class Roo.data.DataReader
10236  * Base class for reading structured data from a data source.  This class is intended to be
10237  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10238  */
10239
10240 Roo.data.DataReader = function(meta, recordType){
10241     
10242     this.meta = meta;
10243     
10244     this.recordType = recordType instanceof Array ? 
10245         Roo.data.Record.create(recordType) : recordType;
10246 };
10247
10248 Roo.data.DataReader.prototype = {
10249      /**
10250      * Create an empty record
10251      * @param {Object} data (optional) - overlay some values
10252      * @return {Roo.data.Record} record created.
10253      */
10254     newRow :  function(d) {
10255         var da =  {};
10256         this.recordType.prototype.fields.each(function(c) {
10257             switch( c.type) {
10258                 case 'int' : da[c.name] = 0; break;
10259                 case 'date' : da[c.name] = new Date(); break;
10260                 case 'float' : da[c.name] = 0.0; break;
10261                 case 'boolean' : da[c.name] = false; break;
10262                 default : da[c.name] = ""; break;
10263             }
10264             
10265         });
10266         return new this.recordType(Roo.apply(da, d));
10267     }
10268     
10269 };/*
10270  * Based on:
10271  * Ext JS Library 1.1.1
10272  * Copyright(c) 2006-2007, Ext JS, LLC.
10273  *
10274  * Originally Released Under LGPL - original licence link has changed is not relivant.
10275  *
10276  * Fork - LGPL
10277  * <script type="text/javascript">
10278  */
10279
10280 /**
10281  * @class Roo.data.DataProxy
10282  * @extends Roo.data.Observable
10283  * This class is an abstract base class for implementations which provide retrieval of
10284  * unformatted data objects.<br>
10285  * <p>
10286  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10287  * (of the appropriate type which knows how to parse the data object) to provide a block of
10288  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10289  * <p>
10290  * Custom implementations must implement the load method as described in
10291  * {@link Roo.data.HttpProxy#load}.
10292  */
10293 Roo.data.DataProxy = function(){
10294     this.addEvents({
10295         /**
10296          * @event beforeload
10297          * Fires before a network request is made to retrieve a data object.
10298          * @param {Object} This DataProxy object.
10299          * @param {Object} params The params parameter to the load function.
10300          */
10301         beforeload : true,
10302         /**
10303          * @event load
10304          * Fires before the load method's callback is called.
10305          * @param {Object} This DataProxy object.
10306          * @param {Object} o The data object.
10307          * @param {Object} arg The callback argument object passed to the load function.
10308          */
10309         load : true,
10310         /**
10311          * @event loadexception
10312          * Fires if an Exception occurs during data retrieval.
10313          * @param {Object} This DataProxy object.
10314          * @param {Object} o The data object.
10315          * @param {Object} arg The callback argument object passed to the load function.
10316          * @param {Object} e The Exception.
10317          */
10318         loadexception : true
10319     });
10320     Roo.data.DataProxy.superclass.constructor.call(this);
10321 };
10322
10323 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10324
10325     /**
10326      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10327      */
10328 /*
10329  * Based on:
10330  * Ext JS Library 1.1.1
10331  * Copyright(c) 2006-2007, Ext JS, LLC.
10332  *
10333  * Originally Released Under LGPL - original licence link has changed is not relivant.
10334  *
10335  * Fork - LGPL
10336  * <script type="text/javascript">
10337  */
10338 /**
10339  * @class Roo.data.MemoryProxy
10340  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10341  * to the Reader when its load method is called.
10342  * @constructor
10343  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10344  */
10345 Roo.data.MemoryProxy = function(data){
10346     if (data.data) {
10347         data = data.data;
10348     }
10349     Roo.data.MemoryProxy.superclass.constructor.call(this);
10350     this.data = data;
10351 };
10352
10353 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10354     /**
10355      * Load data from the requested source (in this case an in-memory
10356      * data object passed to the constructor), read the data object into
10357      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10358      * process that block using the passed callback.
10359      * @param {Object} params This parameter is not used by the MemoryProxy class.
10360      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10361      * object into a block of Roo.data.Records.
10362      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10363      * The function must be passed <ul>
10364      * <li>The Record block object</li>
10365      * <li>The "arg" argument from the load function</li>
10366      * <li>A boolean success indicator</li>
10367      * </ul>
10368      * @param {Object} scope The scope in which to call the callback
10369      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10370      */
10371     load : function(params, reader, callback, scope, arg){
10372         params = params || {};
10373         var result;
10374         try {
10375             result = reader.readRecords(this.data);
10376         }catch(e){
10377             this.fireEvent("loadexception", this, arg, null, e);
10378             callback.call(scope, null, arg, false);
10379             return;
10380         }
10381         callback.call(scope, result, arg, true);
10382     },
10383     
10384     // private
10385     update : function(params, records){
10386         
10387     }
10388 });/*
10389  * Based on:
10390  * Ext JS Library 1.1.1
10391  * Copyright(c) 2006-2007, Ext JS, LLC.
10392  *
10393  * Originally Released Under LGPL - original licence link has changed is not relivant.
10394  *
10395  * Fork - LGPL
10396  * <script type="text/javascript">
10397  */
10398 /**
10399  * @class Roo.data.HttpProxy
10400  * @extends Roo.data.DataProxy
10401  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10402  * configured to reference a certain URL.<br><br>
10403  * <p>
10404  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10405  * from which the running page was served.<br><br>
10406  * <p>
10407  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10408  * <p>
10409  * Be aware that to enable the browser to parse an XML document, the server must set
10410  * the Content-Type header in the HTTP response to "text/xml".
10411  * @constructor
10412  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10413  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10414  * will be used to make the request.
10415  */
10416 Roo.data.HttpProxy = function(conn){
10417     Roo.data.HttpProxy.superclass.constructor.call(this);
10418     // is conn a conn config or a real conn?
10419     this.conn = conn;
10420     this.useAjax = !conn || !conn.events;
10421   
10422 };
10423
10424 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10425     // thse are take from connection...
10426     
10427     /**
10428      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10429      */
10430     /**
10431      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10432      * extra parameters to each request made by this object. (defaults to undefined)
10433      */
10434     /**
10435      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10436      *  to each request made by this object. (defaults to undefined)
10437      */
10438     /**
10439      * @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)
10440      */
10441     /**
10442      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10443      */
10444      /**
10445      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10446      * @type Boolean
10447      */
10448   
10449
10450     /**
10451      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10452      * @type Boolean
10453      */
10454     /**
10455      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10456      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10457      * a finer-grained basis than the DataProxy events.
10458      */
10459     getConnection : function(){
10460         return this.useAjax ? Roo.Ajax : this.conn;
10461     },
10462
10463     /**
10464      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10465      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10466      * process that block using the passed callback.
10467      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10468      * for the request to the remote server.
10469      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10470      * object into a block of Roo.data.Records.
10471      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10472      * The function must be passed <ul>
10473      * <li>The Record block object</li>
10474      * <li>The "arg" argument from the load function</li>
10475      * <li>A boolean success indicator</li>
10476      * </ul>
10477      * @param {Object} scope The scope in which to call the callback
10478      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10479      */
10480     load : function(params, reader, callback, scope, arg){
10481         if(this.fireEvent("beforeload", this, params) !== false){
10482             var  o = {
10483                 params : params || {},
10484                 request: {
10485                     callback : callback,
10486                     scope : scope,
10487                     arg : arg
10488                 },
10489                 reader: reader,
10490                 callback : this.loadResponse,
10491                 scope: this
10492             };
10493             if(this.useAjax){
10494                 Roo.applyIf(o, this.conn);
10495                 if(this.activeRequest){
10496                     Roo.Ajax.abort(this.activeRequest);
10497                 }
10498                 this.activeRequest = Roo.Ajax.request(o);
10499             }else{
10500                 this.conn.request(o);
10501             }
10502         }else{
10503             callback.call(scope||this, null, arg, false);
10504         }
10505     },
10506
10507     // private
10508     loadResponse : function(o, success, response){
10509         delete this.activeRequest;
10510         if(!success){
10511             this.fireEvent("loadexception", this, o, response);
10512             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10513             return;
10514         }
10515         var result;
10516         try {
10517             result = o.reader.read(response);
10518         }catch(e){
10519             this.fireEvent("loadexception", this, o, response, e);
10520             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10521             return;
10522         }
10523         
10524         this.fireEvent("load", this, o, o.request.arg);
10525         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10526     },
10527
10528     // private
10529     update : function(dataSet){
10530
10531     },
10532
10533     // private
10534     updateResponse : function(dataSet){
10535
10536     }
10537 });/*
10538  * Based on:
10539  * Ext JS Library 1.1.1
10540  * Copyright(c) 2006-2007, Ext JS, LLC.
10541  *
10542  * Originally Released Under LGPL - original licence link has changed is not relivant.
10543  *
10544  * Fork - LGPL
10545  * <script type="text/javascript">
10546  */
10547
10548 /**
10549  * @class Roo.data.ScriptTagProxy
10550  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10551  * other than the originating domain of the running page.<br><br>
10552  * <p>
10553  * <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
10554  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10555  * <p>
10556  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10557  * source code that is used as the source inside a &lt;script> tag.<br><br>
10558  * <p>
10559  * In order for the browser to process the returned data, the server must wrap the data object
10560  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10561  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10562  * depending on whether the callback name was passed:
10563  * <p>
10564  * <pre><code>
10565 boolean scriptTag = false;
10566 String cb = request.getParameter("callback");
10567 if (cb != null) {
10568     scriptTag = true;
10569     response.setContentType("text/javascript");
10570 } else {
10571     response.setContentType("application/x-json");
10572 }
10573 Writer out = response.getWriter();
10574 if (scriptTag) {
10575     out.write(cb + "(");
10576 }
10577 out.print(dataBlock.toJsonString());
10578 if (scriptTag) {
10579     out.write(");");
10580 }
10581 </pre></code>
10582  *
10583  * @constructor
10584  * @param {Object} config A configuration object.
10585  */
10586 Roo.data.ScriptTagProxy = function(config){
10587     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10588     Roo.apply(this, config);
10589     this.head = document.getElementsByTagName("head")[0];
10590 };
10591
10592 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10593
10594 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10595     /**
10596      * @cfg {String} url The URL from which to request the data object.
10597      */
10598     /**
10599      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10600      */
10601     timeout : 30000,
10602     /**
10603      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10604      * the server the name of the callback function set up by the load call to process the returned data object.
10605      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10606      * javascript output which calls this named function passing the data object as its only parameter.
10607      */
10608     callbackParam : "callback",
10609     /**
10610      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10611      * name to the request.
10612      */
10613     nocache : true,
10614
10615     /**
10616      * Load data from the configured URL, read the data object into
10617      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10618      * process that block using the passed callback.
10619      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10620      * for the request to the remote server.
10621      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10622      * object into a block of Roo.data.Records.
10623      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10624      * The function must be passed <ul>
10625      * <li>The Record block object</li>
10626      * <li>The "arg" argument from the load function</li>
10627      * <li>A boolean success indicator</li>
10628      * </ul>
10629      * @param {Object} scope The scope in which to call the callback
10630      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10631      */
10632     load : function(params, reader, callback, scope, arg){
10633         if(this.fireEvent("beforeload", this, params) !== false){
10634
10635             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10636
10637             var url = this.url;
10638             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10639             if(this.nocache){
10640                 url += "&_dc=" + (new Date().getTime());
10641             }
10642             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10643             var trans = {
10644                 id : transId,
10645                 cb : "stcCallback"+transId,
10646                 scriptId : "stcScript"+transId,
10647                 params : params,
10648                 arg : arg,
10649                 url : url,
10650                 callback : callback,
10651                 scope : scope,
10652                 reader : reader
10653             };
10654             var conn = this;
10655
10656             window[trans.cb] = function(o){
10657                 conn.handleResponse(o, trans);
10658             };
10659
10660             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10661
10662             if(this.autoAbort !== false){
10663                 this.abort();
10664             }
10665
10666             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10667
10668             var script = document.createElement("script");
10669             script.setAttribute("src", url);
10670             script.setAttribute("type", "text/javascript");
10671             script.setAttribute("id", trans.scriptId);
10672             this.head.appendChild(script);
10673
10674             this.trans = trans;
10675         }else{
10676             callback.call(scope||this, null, arg, false);
10677         }
10678     },
10679
10680     // private
10681     isLoading : function(){
10682         return this.trans ? true : false;
10683     },
10684
10685     /**
10686      * Abort the current server request.
10687      */
10688     abort : function(){
10689         if(this.isLoading()){
10690             this.destroyTrans(this.trans);
10691         }
10692     },
10693
10694     // private
10695     destroyTrans : function(trans, isLoaded){
10696         this.head.removeChild(document.getElementById(trans.scriptId));
10697         clearTimeout(trans.timeoutId);
10698         if(isLoaded){
10699             window[trans.cb] = undefined;
10700             try{
10701                 delete window[trans.cb];
10702             }catch(e){}
10703         }else{
10704             // if hasn't been loaded, wait for load to remove it to prevent script error
10705             window[trans.cb] = function(){
10706                 window[trans.cb] = undefined;
10707                 try{
10708                     delete window[trans.cb];
10709                 }catch(e){}
10710             };
10711         }
10712     },
10713
10714     // private
10715     handleResponse : function(o, trans){
10716         this.trans = false;
10717         this.destroyTrans(trans, true);
10718         var result;
10719         try {
10720             result = trans.reader.readRecords(o);
10721         }catch(e){
10722             this.fireEvent("loadexception", this, o, trans.arg, e);
10723             trans.callback.call(trans.scope||window, null, trans.arg, false);
10724             return;
10725         }
10726         this.fireEvent("load", this, o, trans.arg);
10727         trans.callback.call(trans.scope||window, result, trans.arg, true);
10728     },
10729
10730     // private
10731     handleFailure : function(trans){
10732         this.trans = false;
10733         this.destroyTrans(trans, false);
10734         this.fireEvent("loadexception", this, null, trans.arg);
10735         trans.callback.call(trans.scope||window, null, trans.arg, false);
10736     }
10737 });/*
10738  * Based on:
10739  * Ext JS Library 1.1.1
10740  * Copyright(c) 2006-2007, Ext JS, LLC.
10741  *
10742  * Originally Released Under LGPL - original licence link has changed is not relivant.
10743  *
10744  * Fork - LGPL
10745  * <script type="text/javascript">
10746  */
10747
10748 /**
10749  * @class Roo.data.JsonReader
10750  * @extends Roo.data.DataReader
10751  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10752  * based on mappings in a provided Roo.data.Record constructor.
10753  * 
10754  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10755  * in the reply previously. 
10756  * 
10757  * <p>
10758  * Example code:
10759  * <pre><code>
10760 var RecordDef = Roo.data.Record.create([
10761     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10762     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10763 ]);
10764 var myReader = new Roo.data.JsonReader({
10765     totalProperty: "results",    // The property which contains the total dataset size (optional)
10766     root: "rows",                // The property which contains an Array of row objects
10767     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10768 }, RecordDef);
10769 </code></pre>
10770  * <p>
10771  * This would consume a JSON file like this:
10772  * <pre><code>
10773 { 'results': 2, 'rows': [
10774     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10775     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10776 }
10777 </code></pre>
10778  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10779  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10780  * paged from the remote server.
10781  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10782  * @cfg {String} root name of the property which contains the Array of row objects.
10783  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10784  * @cfg {Array} fields Array of field definition objects
10785  * @constructor
10786  * Create a new JsonReader
10787  * @param {Object} meta Metadata configuration options
10788  * @param {Object} recordType Either an Array of field definition objects,
10789  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10790  */
10791 Roo.data.JsonReader = function(meta, recordType){
10792     
10793     meta = meta || {};
10794     // set some defaults:
10795     Roo.applyIf(meta, {
10796         totalProperty: 'total',
10797         successProperty : 'success',
10798         root : 'data',
10799         id : 'id'
10800     });
10801     
10802     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10803 };
10804 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10805     
10806     /**
10807      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10808      * Used by Store query builder to append _requestMeta to params.
10809      * 
10810      */
10811     metaFromRemote : false,
10812     /**
10813      * This method is only used by a DataProxy which has retrieved data from a remote server.
10814      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10815      * @return {Object} data A data block which is used by an Roo.data.Store object as
10816      * a cache of Roo.data.Records.
10817      */
10818     read : function(response){
10819         var json = response.responseText;
10820        
10821         var o = /* eval:var:o */ eval("("+json+")");
10822         if(!o) {
10823             throw {message: "JsonReader.read: Json object not found"};
10824         }
10825         
10826         if(o.metaData){
10827             
10828             delete this.ef;
10829             this.metaFromRemote = true;
10830             this.meta = o.metaData;
10831             this.recordType = Roo.data.Record.create(o.metaData.fields);
10832             this.onMetaChange(this.meta, this.recordType, o);
10833         }
10834         return this.readRecords(o);
10835     },
10836
10837     // private function a store will implement
10838     onMetaChange : function(meta, recordType, o){
10839
10840     },
10841
10842     /**
10843          * @ignore
10844          */
10845     simpleAccess: function(obj, subsc) {
10846         return obj[subsc];
10847     },
10848
10849         /**
10850          * @ignore
10851          */
10852     getJsonAccessor: function(){
10853         var re = /[\[\.]/;
10854         return function(expr) {
10855             try {
10856                 return(re.test(expr))
10857                     ? new Function("obj", "return obj." + expr)
10858                     : function(obj){
10859                         return obj[expr];
10860                     };
10861             } catch(e){}
10862             return Roo.emptyFn;
10863         };
10864     }(),
10865
10866     /**
10867      * Create a data block containing Roo.data.Records from an XML document.
10868      * @param {Object} o An object which contains an Array of row objects in the property specified
10869      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10870      * which contains the total size of the dataset.
10871      * @return {Object} data A data block which is used by an Roo.data.Store object as
10872      * a cache of Roo.data.Records.
10873      */
10874     readRecords : function(o){
10875         /**
10876          * After any data loads, the raw JSON data is available for further custom processing.
10877          * @type Object
10878          */
10879         this.o = o;
10880         var s = this.meta, Record = this.recordType,
10881             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10882
10883 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10884         if (!this.ef) {
10885             if(s.totalProperty) {
10886                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10887                 }
10888                 if(s.successProperty) {
10889                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10890                 }
10891                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10892                 if (s.id) {
10893                         var g = this.getJsonAccessor(s.id);
10894                         this.getId = function(rec) {
10895                                 var r = g(rec);  
10896                                 return (r === undefined || r === "") ? null : r;
10897                         };
10898                 } else {
10899                         this.getId = function(){return null;};
10900                 }
10901             this.ef = [];
10902             for(var jj = 0; jj < fl; jj++){
10903                 f = fi[jj];
10904                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10905                 this.ef[jj] = this.getJsonAccessor(map);
10906             }
10907         }
10908
10909         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10910         if(s.totalProperty){
10911             var vt = parseInt(this.getTotal(o), 10);
10912             if(!isNaN(vt)){
10913                 totalRecords = vt;
10914             }
10915         }
10916         if(s.successProperty){
10917             var vs = this.getSuccess(o);
10918             if(vs === false || vs === 'false'){
10919                 success = false;
10920             }
10921         }
10922         var records = [];
10923         for(var i = 0; i < c; i++){
10924                 var n = root[i];
10925             var values = {};
10926             var id = this.getId(n);
10927             for(var j = 0; j < fl; j++){
10928                 f = fi[j];
10929             var v = this.ef[j](n);
10930             if (!f.convert) {
10931                 Roo.log('missing convert for ' + f.name);
10932                 Roo.log(f);
10933                 continue;
10934             }
10935             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10936             }
10937             var record = new Record(values, id);
10938             record.json = n;
10939             records[i] = record;
10940         }
10941         return {
10942             raw : o,
10943             success : success,
10944             records : records,
10945             totalRecords : totalRecords
10946         };
10947     }
10948 });/*
10949  * Based on:
10950  * Ext JS Library 1.1.1
10951  * Copyright(c) 2006-2007, Ext JS, LLC.
10952  *
10953  * Originally Released Under LGPL - original licence link has changed is not relivant.
10954  *
10955  * Fork - LGPL
10956  * <script type="text/javascript">
10957  */
10958
10959 /**
10960  * @class Roo.data.ArrayReader
10961  * @extends Roo.data.DataReader
10962  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10963  * Each element of that Array represents a row of data fields. The
10964  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10965  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10966  * <p>
10967  * Example code:.
10968  * <pre><code>
10969 var RecordDef = Roo.data.Record.create([
10970     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10971     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10972 ]);
10973 var myReader = new Roo.data.ArrayReader({
10974     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10975 }, RecordDef);
10976 </code></pre>
10977  * <p>
10978  * This would consume an Array like this:
10979  * <pre><code>
10980 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10981   </code></pre>
10982  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10983  * @constructor
10984  * Create a new JsonReader
10985  * @param {Object} meta Metadata configuration options.
10986  * @param {Object} recordType Either an Array of field definition objects
10987  * as specified to {@link Roo.data.Record#create},
10988  * or an {@link Roo.data.Record} object
10989  * created using {@link Roo.data.Record#create}.
10990  */
10991 Roo.data.ArrayReader = function(meta, recordType){
10992     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10993 };
10994
10995 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10996     /**
10997      * Create a data block containing Roo.data.Records from an XML document.
10998      * @param {Object} o An Array of row objects which represents the dataset.
10999      * @return {Object} data A data block which is used by an Roo.data.Store object as
11000      * a cache of Roo.data.Records.
11001      */
11002     readRecords : function(o){
11003         var sid = this.meta ? this.meta.id : null;
11004         var recordType = this.recordType, fields = recordType.prototype.fields;
11005         var records = [];
11006         var root = o;
11007             for(var i = 0; i < root.length; i++){
11008                     var n = root[i];
11009                 var values = {};
11010                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11011                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11012                 var f = fields.items[j];
11013                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11014                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11015                 v = f.convert(v);
11016                 values[f.name] = v;
11017             }
11018                 var record = new recordType(values, id);
11019                 record.json = n;
11020                 records[records.length] = record;
11021             }
11022             return {
11023                 records : records,
11024                 totalRecords : records.length
11025             };
11026     }
11027 });/*
11028  * - LGPL
11029  * * 
11030  */
11031
11032 /**
11033  * @class Roo.bootstrap.ComboBox
11034  * @extends Roo.bootstrap.TriggerField
11035  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11036  * @cfg {Boolean} append (true|false) default false
11037  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11038  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11039  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11040  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11041  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11042  * @cfg {Boolean} animate default true
11043  * @cfg {Boolean} emptyResultText only for touch device
11044  * @constructor
11045  * Create a new ComboBox.
11046  * @param {Object} config Configuration options
11047  */
11048 Roo.bootstrap.ComboBox = function(config){
11049     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11050     this.addEvents({
11051         /**
11052          * @event expand
11053          * Fires when the dropdown list is expanded
11054              * @param {Roo.bootstrap.ComboBox} combo This combo box
11055              */
11056         'expand' : true,
11057         /**
11058          * @event collapse
11059          * Fires when the dropdown list is collapsed
11060              * @param {Roo.bootstrap.ComboBox} combo This combo box
11061              */
11062         'collapse' : true,
11063         /**
11064          * @event beforeselect
11065          * Fires before a list item is selected. Return false to cancel the selection.
11066              * @param {Roo.bootstrap.ComboBox} combo This combo box
11067              * @param {Roo.data.Record} record The data record returned from the underlying store
11068              * @param {Number} index The index of the selected item in the dropdown list
11069              */
11070         'beforeselect' : true,
11071         /**
11072          * @event select
11073          * Fires when a list item is selected
11074              * @param {Roo.bootstrap.ComboBox} combo This combo box
11075              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11076              * @param {Number} index The index of the selected item in the dropdown list
11077              */
11078         'select' : true,
11079         /**
11080          * @event beforequery
11081          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11082          * The event object passed has these properties:
11083              * @param {Roo.bootstrap.ComboBox} combo This combo box
11084              * @param {String} query The query
11085              * @param {Boolean} forceAll true to force "all" query
11086              * @param {Boolean} cancel true to cancel the query
11087              * @param {Object} e The query event object
11088              */
11089         'beforequery': true,
11090          /**
11091          * @event add
11092          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11093              * @param {Roo.bootstrap.ComboBox} combo This combo box
11094              */
11095         'add' : true,
11096         /**
11097          * @event edit
11098          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11099              * @param {Roo.bootstrap.ComboBox} combo This combo box
11100              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11101              */
11102         'edit' : true,
11103         /**
11104          * @event remove
11105          * Fires when the remove value from the combobox array
11106              * @param {Roo.bootstrap.ComboBox} combo This combo box
11107              */
11108         'remove' : true,
11109         /**
11110          * @event specialfilter
11111          * Fires when specialfilter
11112             * @param {Roo.bootstrap.ComboBox} combo This combo box
11113             */
11114         'specialfilter' : true
11115         
11116     });
11117     
11118     this.item = [];
11119     this.tickItems = [];
11120     
11121     this.selectedIndex = -1;
11122     if(this.mode == 'local'){
11123         if(config.queryDelay === undefined){
11124             this.queryDelay = 10;
11125         }
11126         if(config.minChars === undefined){
11127             this.minChars = 0;
11128         }
11129     }
11130 };
11131
11132 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11133      
11134     /**
11135      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11136      * rendering into an Roo.Editor, defaults to false)
11137      */
11138     /**
11139      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11140      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11141      */
11142     /**
11143      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11144      */
11145     /**
11146      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11147      * the dropdown list (defaults to undefined, with no header element)
11148      */
11149
11150      /**
11151      * @cfg {String/Roo.Template} tpl The template to use to render the output
11152      */
11153      
11154      /**
11155      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11156      */
11157     listWidth: undefined,
11158     /**
11159      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11160      * mode = 'remote' or 'text' if mode = 'local')
11161      */
11162     displayField: undefined,
11163     
11164     /**
11165      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11166      * mode = 'remote' or 'value' if mode = 'local'). 
11167      * Note: use of a valueField requires the user make a selection
11168      * in order for a value to be mapped.
11169      */
11170     valueField: undefined,
11171     
11172     
11173     /**
11174      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11175      * field's data value (defaults to the underlying DOM element's name)
11176      */
11177     hiddenName: undefined,
11178     /**
11179      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11180      */
11181     listClass: '',
11182     /**
11183      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11184      */
11185     selectedClass: 'active',
11186     
11187     /**
11188      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11189      */
11190     shadow:'sides',
11191     /**
11192      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11193      * anchor positions (defaults to 'tl-bl')
11194      */
11195     listAlign: 'tl-bl?',
11196     /**
11197      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11198      */
11199     maxHeight: 300,
11200     /**
11201      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11202      * query specified by the allQuery config option (defaults to 'query')
11203      */
11204     triggerAction: 'query',
11205     /**
11206      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11207      * (defaults to 4, does not apply if editable = false)
11208      */
11209     minChars : 4,
11210     /**
11211      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11212      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11213      */
11214     typeAhead: false,
11215     /**
11216      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11217      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11218      */
11219     queryDelay: 500,
11220     /**
11221      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11222      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11223      */
11224     pageSize: 0,
11225     /**
11226      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11227      * when editable = true (defaults to false)
11228      */
11229     selectOnFocus:false,
11230     /**
11231      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11232      */
11233     queryParam: 'query',
11234     /**
11235      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11236      * when mode = 'remote' (defaults to 'Loading...')
11237      */
11238     loadingText: 'Loading...',
11239     /**
11240      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11241      */
11242     resizable: false,
11243     /**
11244      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11245      */
11246     handleHeight : 8,
11247     /**
11248      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11249      * traditional select (defaults to true)
11250      */
11251     editable: true,
11252     /**
11253      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11254      */
11255     allQuery: '',
11256     /**
11257      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11258      */
11259     mode: 'remote',
11260     /**
11261      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11262      * listWidth has a higher value)
11263      */
11264     minListWidth : 70,
11265     /**
11266      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11267      * allow the user to set arbitrary text into the field (defaults to false)
11268      */
11269     forceSelection:false,
11270     /**
11271      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11272      * if typeAhead = true (defaults to 250)
11273      */
11274     typeAheadDelay : 250,
11275     /**
11276      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11277      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11278      */
11279     valueNotFoundText : undefined,
11280     /**
11281      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11282      */
11283     blockFocus : false,
11284     
11285     /**
11286      * @cfg {Boolean} disableClear Disable showing of clear button.
11287      */
11288     disableClear : false,
11289     /**
11290      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11291      */
11292     alwaysQuery : false,
11293     
11294     /**
11295      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11296      */
11297     multiple : false,
11298     
11299     /**
11300      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11301      */
11302     invalidClass : "has-warning",
11303     
11304     /**
11305      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11306      */
11307     validClass : "has-success",
11308     
11309     /**
11310      * @cfg {Boolean} specialFilter (true|false) special filter default false
11311      */
11312     specialFilter : false,
11313     
11314     /**
11315      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11316      */
11317     mobileTouchView : true,
11318     
11319     //private
11320     addicon : false,
11321     editicon: false,
11322     
11323     page: 0,
11324     hasQuery: false,
11325     append: false,
11326     loadNext: false,
11327     autoFocus : true,
11328     tickable : false,
11329     btnPosition : 'right',
11330     triggerList : true,
11331     showToggleBtn : true,
11332     animate : true,
11333     emptyResultText: 'Empty',
11334     // element that contains real text value.. (when hidden is used..)
11335     
11336     getAutoCreate : function()
11337     {
11338         var cfg = false;
11339         
11340         /*
11341          * Touch Devices
11342          */
11343         
11344         if(Roo.isTouch && this.mobileTouchView){
11345             cfg = this.getAutoCreateTouchView();
11346             return cfg;;
11347         }
11348         
11349         /*
11350          *  Normal ComboBox
11351          */
11352         if(!this.tickable){
11353             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11354             return cfg;
11355         }
11356         
11357         /*
11358          *  ComboBox with tickable selections
11359          */
11360              
11361         var align = this.labelAlign || this.parentLabelAlign();
11362         
11363         cfg = {
11364             cls : 'form-group roo-combobox-tickable' //input-group
11365         };
11366         
11367         var buttons = {
11368             tag : 'div',
11369             cls : 'tickable-buttons',
11370             cn : [
11371                 {
11372                     tag : 'button',
11373                     type : 'button',
11374                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11375                     html : 'Edit'
11376                 },
11377                 {
11378                     tag : 'button',
11379                     type : 'button',
11380                     name : 'ok',
11381                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11382                     html : 'Done'
11383                 },
11384                 {
11385                     tag : 'button',
11386                     type : 'button',
11387                     name : 'cancel',
11388                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11389                     html : 'Cancel'
11390                 }
11391             ]
11392         };
11393         
11394         if(this.editable){
11395             buttons.cn.unshift({
11396                 tag: 'input',
11397                 cls: 'select2-search-field-input'
11398             });
11399         }
11400         
11401         var _this = this;
11402         
11403         Roo.each(buttons.cn, function(c){
11404             if (_this.size) {
11405                 c.cls += ' btn-' + _this.size;
11406             }
11407
11408             if (_this.disabled) {
11409                 c.disabled = true;
11410             }
11411         });
11412         
11413         var box = {
11414             tag: 'div',
11415             cn: [
11416                 {
11417                     tag: 'input',
11418                     type : 'hidden',
11419                     cls: 'form-hidden-field'
11420                 },
11421                 {
11422                     tag: 'ul',
11423                     cls: 'select2-choices',
11424                     cn:[
11425                         {
11426                             tag: 'li',
11427                             cls: 'select2-search-field',
11428                             cn: [
11429
11430                                 buttons
11431                             ]
11432                         }
11433                     ]
11434                 }
11435             ]
11436         }
11437         
11438         var combobox = {
11439             cls: 'select2-container input-group select2-container-multi',
11440             cn: [
11441                 box
11442 //                {
11443 //                    tag: 'ul',
11444 //                    cls: 'typeahead typeahead-long dropdown-menu',
11445 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11446 //                }
11447             ]
11448         };
11449         
11450         if(this.hasFeedback && !this.allowBlank){
11451             
11452             var feedback = {
11453                 tag: 'span',
11454                 cls: 'glyphicon form-control-feedback'
11455             };
11456
11457             combobox.cn.push(feedback);
11458         }
11459         
11460         if (align ==='left' && this.fieldLabel.length) {
11461             
11462                 Roo.log("left and has label");
11463                 cfg.cn = [
11464                     
11465                     {
11466                         tag: 'label',
11467                         'for' :  id,
11468                         cls : 'control-label col-sm-' + this.labelWidth,
11469                         html : this.fieldLabel
11470                         
11471                     },
11472                     {
11473                         cls : "col-sm-" + (12 - this.labelWidth), 
11474                         cn: [
11475                             combobox
11476                         ]
11477                     }
11478                     
11479                 ];
11480         } else if ( this.fieldLabel.length) {
11481                 Roo.log(" label");
11482                  cfg.cn = [
11483                    
11484                     {
11485                         tag: 'label',
11486                         //cls : 'input-group-addon',
11487                         html : this.fieldLabel
11488                         
11489                     },
11490                     
11491                     combobox
11492                     
11493                 ];
11494
11495         } else {
11496             
11497                 Roo.log(" no label && no align");
11498                 cfg = combobox
11499                      
11500                 
11501         }
11502          
11503         var settings=this;
11504         ['xs','sm','md','lg'].map(function(size){
11505             if (settings[size]) {
11506                 cfg.cls += ' col-' + size + '-' + settings[size];
11507             }
11508         });
11509         
11510         return cfg;
11511         
11512     },
11513     
11514     // private
11515     initEvents: function()
11516     {
11517         
11518         if (!this.store) {
11519             throw "can not find store for combo";
11520         }
11521         
11522         this.store = Roo.factory(this.store, Roo.data);
11523         
11524         /*
11525          * Touch Devices
11526          */
11527         
11528         if(Roo.isTouch && this.mobileTouchView){
11529             this.initTouchView();
11530             return;
11531         }
11532         
11533         if(this.tickable){
11534             this.initTickableEvents();
11535             return;
11536         }
11537         
11538         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11539         
11540         if(this.hiddenName){
11541             
11542             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11543             
11544             this.hiddenField.dom.value =
11545                 this.hiddenValue !== undefined ? this.hiddenValue :
11546                 this.value !== undefined ? this.value : '';
11547
11548             // prevent input submission
11549             this.el.dom.removeAttribute('name');
11550             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11551              
11552              
11553         }
11554         //if(Roo.isGecko){
11555         //    this.el.dom.setAttribute('autocomplete', 'off');
11556         //}
11557         
11558         var cls = 'x-combo-list';
11559         
11560         //this.list = new Roo.Layer({
11561         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11562         //});
11563         
11564         var _this = this;
11565         
11566         (function(){
11567             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11568             _this.list.setWidth(lw);
11569         }).defer(100);
11570         
11571         this.list.on('mouseover', this.onViewOver, this);
11572         this.list.on('mousemove', this.onViewMove, this);
11573         
11574         this.list.on('scroll', this.onViewScroll, this);
11575         
11576         /*
11577         this.list.swallowEvent('mousewheel');
11578         this.assetHeight = 0;
11579
11580         if(this.title){
11581             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11582             this.assetHeight += this.header.getHeight();
11583         }
11584
11585         this.innerList = this.list.createChild({cls:cls+'-inner'});
11586         this.innerList.on('mouseover', this.onViewOver, this);
11587         this.innerList.on('mousemove', this.onViewMove, this);
11588         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11589         
11590         if(this.allowBlank && !this.pageSize && !this.disableClear){
11591             this.footer = this.list.createChild({cls:cls+'-ft'});
11592             this.pageTb = new Roo.Toolbar(this.footer);
11593            
11594         }
11595         if(this.pageSize){
11596             this.footer = this.list.createChild({cls:cls+'-ft'});
11597             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11598                     {pageSize: this.pageSize});
11599             
11600         }
11601         
11602         if (this.pageTb && this.allowBlank && !this.disableClear) {
11603             var _this = this;
11604             this.pageTb.add(new Roo.Toolbar.Fill(), {
11605                 cls: 'x-btn-icon x-btn-clear',
11606                 text: '&#160;',
11607                 handler: function()
11608                 {
11609                     _this.collapse();
11610                     _this.clearValue();
11611                     _this.onSelect(false, -1);
11612                 }
11613             });
11614         }
11615         if (this.footer) {
11616             this.assetHeight += this.footer.getHeight();
11617         }
11618         */
11619             
11620         if(!this.tpl){
11621             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11622         }
11623
11624         this.view = new Roo.View(this.list, this.tpl, {
11625             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11626         });
11627         //this.view.wrapEl.setDisplayed(false);
11628         this.view.on('click', this.onViewClick, this);
11629         
11630         
11631         
11632         this.store.on('beforeload', this.onBeforeLoad, this);
11633         this.store.on('load', this.onLoad, this);
11634         this.store.on('loadexception', this.onLoadException, this);
11635         /*
11636         if(this.resizable){
11637             this.resizer = new Roo.Resizable(this.list,  {
11638                pinned:true, handles:'se'
11639             });
11640             this.resizer.on('resize', function(r, w, h){
11641                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11642                 this.listWidth = w;
11643                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11644                 this.restrictHeight();
11645             }, this);
11646             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11647         }
11648         */
11649         if(!this.editable){
11650             this.editable = true;
11651             this.setEditable(false);
11652         }
11653         
11654         /*
11655         
11656         if (typeof(this.events.add.listeners) != 'undefined') {
11657             
11658             this.addicon = this.wrap.createChild(
11659                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11660        
11661             this.addicon.on('click', function(e) {
11662                 this.fireEvent('add', this);
11663             }, this);
11664         }
11665         if (typeof(this.events.edit.listeners) != 'undefined') {
11666             
11667             this.editicon = this.wrap.createChild(
11668                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11669             if (this.addicon) {
11670                 this.editicon.setStyle('margin-left', '40px');
11671             }
11672             this.editicon.on('click', function(e) {
11673                 
11674                 // we fire even  if inothing is selected..
11675                 this.fireEvent('edit', this, this.lastData );
11676                 
11677             }, this);
11678         }
11679         */
11680         
11681         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11682             "up" : function(e){
11683                 this.inKeyMode = true;
11684                 this.selectPrev();
11685             },
11686
11687             "down" : function(e){
11688                 if(!this.isExpanded()){
11689                     this.onTriggerClick();
11690                 }else{
11691                     this.inKeyMode = true;
11692                     this.selectNext();
11693                 }
11694             },
11695
11696             "enter" : function(e){
11697 //                this.onViewClick();
11698                 //return true;
11699                 this.collapse();
11700                 
11701                 if(this.fireEvent("specialkey", this, e)){
11702                     this.onViewClick(false);
11703                 }
11704                 
11705                 return true;
11706             },
11707
11708             "esc" : function(e){
11709                 this.collapse();
11710             },
11711
11712             "tab" : function(e){
11713                 this.collapse();
11714                 
11715                 if(this.fireEvent("specialkey", this, e)){
11716                     this.onViewClick(false);
11717                 }
11718                 
11719                 return true;
11720             },
11721
11722             scope : this,
11723
11724             doRelay : function(foo, bar, hname){
11725                 if(hname == 'down' || this.scope.isExpanded()){
11726                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11727                 }
11728                 return true;
11729             },
11730
11731             forceKeyDown: true
11732         });
11733         
11734         
11735         this.queryDelay = Math.max(this.queryDelay || 10,
11736                 this.mode == 'local' ? 10 : 250);
11737         
11738         
11739         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11740         
11741         if(this.typeAhead){
11742             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11743         }
11744         if(this.editable !== false){
11745             this.inputEl().on("keyup", this.onKeyUp, this);
11746         }
11747         if(this.forceSelection){
11748             this.inputEl().on('blur', this.doForce, this);
11749         }
11750         
11751         if(this.multiple){
11752             this.choices = this.el.select('ul.select2-choices', true).first();
11753             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11754         }
11755     },
11756     
11757     initTickableEvents: function()
11758     {   
11759         this.createList();
11760         
11761         if(this.hiddenName){
11762             
11763             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11764             
11765             this.hiddenField.dom.value =
11766                 this.hiddenValue !== undefined ? this.hiddenValue :
11767                 this.value !== undefined ? this.value : '';
11768
11769             // prevent input submission
11770             this.el.dom.removeAttribute('name');
11771             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11772              
11773              
11774         }
11775         
11776 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11777         
11778         this.choices = this.el.select('ul.select2-choices', true).first();
11779         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11780         if(this.triggerList){
11781             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11782         }
11783          
11784         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11785         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11786         
11787         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11788         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11789         
11790         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11791         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11792         
11793         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11794         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11795         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11796         
11797         this.okBtn.hide();
11798         this.cancelBtn.hide();
11799         
11800         var _this = this;
11801         
11802         (function(){
11803             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11804             _this.list.setWidth(lw);
11805         }).defer(100);
11806         
11807         this.list.on('mouseover', this.onViewOver, this);
11808         this.list.on('mousemove', this.onViewMove, this);
11809         
11810         this.list.on('scroll', this.onViewScroll, this);
11811         
11812         if(!this.tpl){
11813             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>';
11814         }
11815
11816         this.view = new Roo.View(this.list, this.tpl, {
11817             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11818         });
11819         
11820         //this.view.wrapEl.setDisplayed(false);
11821         this.view.on('click', this.onViewClick, this);
11822         
11823         
11824         
11825         this.store.on('beforeload', this.onBeforeLoad, this);
11826         this.store.on('load', this.onLoad, this);
11827         this.store.on('loadexception', this.onLoadException, this);
11828         
11829         if(this.editable){
11830             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11831                 "up" : function(e){
11832                     this.inKeyMode = true;
11833                     this.selectPrev();
11834                 },
11835
11836                 "down" : function(e){
11837                     this.inKeyMode = true;
11838                     this.selectNext();
11839                 },
11840
11841                 "enter" : function(e){
11842                     if(this.fireEvent("specialkey", this, e)){
11843                         this.onViewClick(false);
11844                     }
11845                     
11846                     return true;
11847                 },
11848
11849                 "esc" : function(e){
11850                     this.onTickableFooterButtonClick(e, false, false);
11851                 },
11852
11853                 "tab" : function(e){
11854                     this.fireEvent("specialkey", this, e);
11855                     
11856                     this.onTickableFooterButtonClick(e, false, false);
11857                     
11858                     return true;
11859                 },
11860
11861                 scope : this,
11862
11863                 doRelay : function(e, fn, key){
11864                     if(this.scope.isExpanded()){
11865                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11866                     }
11867                     return true;
11868                 },
11869
11870                 forceKeyDown: true
11871             });
11872         }
11873         
11874         this.queryDelay = Math.max(this.queryDelay || 10,
11875                 this.mode == 'local' ? 10 : 250);
11876         
11877         
11878         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11879         
11880         if(this.typeAhead){
11881             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11882         }
11883         
11884         if(this.editable !== false){
11885             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11886         }
11887         
11888     },
11889
11890     onDestroy : function(){
11891         if(this.view){
11892             this.view.setStore(null);
11893             this.view.el.removeAllListeners();
11894             this.view.el.remove();
11895             this.view.purgeListeners();
11896         }
11897         if(this.list){
11898             this.list.dom.innerHTML  = '';
11899         }
11900         
11901         if(this.store){
11902             this.store.un('beforeload', this.onBeforeLoad, this);
11903             this.store.un('load', this.onLoad, this);
11904             this.store.un('loadexception', this.onLoadException, this);
11905         }
11906         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11907     },
11908
11909     // private
11910     fireKey : function(e){
11911         if(e.isNavKeyPress() && !this.list.isVisible()){
11912             this.fireEvent("specialkey", this, e);
11913         }
11914     },
11915
11916     // private
11917     onResize: function(w, h){
11918 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11919 //        
11920 //        if(typeof w != 'number'){
11921 //            // we do not handle it!?!?
11922 //            return;
11923 //        }
11924 //        var tw = this.trigger.getWidth();
11925 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11926 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11927 //        var x = w - tw;
11928 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11929 //            
11930 //        //this.trigger.setStyle('left', x+'px');
11931 //        
11932 //        if(this.list && this.listWidth === undefined){
11933 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11934 //            this.list.setWidth(lw);
11935 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11936 //        }
11937         
11938     
11939         
11940     },
11941
11942     /**
11943      * Allow or prevent the user from directly editing the field text.  If false is passed,
11944      * the user will only be able to select from the items defined in the dropdown list.  This method
11945      * is the runtime equivalent of setting the 'editable' config option at config time.
11946      * @param {Boolean} value True to allow the user to directly edit the field text
11947      */
11948     setEditable : function(value){
11949         if(value == this.editable){
11950             return;
11951         }
11952         this.editable = value;
11953         if(!value){
11954             this.inputEl().dom.setAttribute('readOnly', true);
11955             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11956             this.inputEl().addClass('x-combo-noedit');
11957         }else{
11958             this.inputEl().dom.setAttribute('readOnly', false);
11959             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11960             this.inputEl().removeClass('x-combo-noedit');
11961         }
11962     },
11963
11964     // private
11965     
11966     onBeforeLoad : function(combo,opts){
11967         if(!this.hasFocus){
11968             return;
11969         }
11970          if (!opts.add) {
11971             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11972          }
11973         this.restrictHeight();
11974         this.selectedIndex = -1;
11975     },
11976
11977     // private
11978     onLoad : function(){
11979         
11980         this.hasQuery = false;
11981         
11982         if(!this.hasFocus){
11983             return;
11984         }
11985         
11986         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11987             this.loading.hide();
11988         }
11989              
11990         if(this.store.getCount() > 0){
11991             this.expand();
11992             this.restrictHeight();
11993             if(this.lastQuery == this.allQuery){
11994                 if(this.editable && !this.tickable){
11995                     this.inputEl().dom.select();
11996                 }
11997                 
11998                 if(
11999                     !this.selectByValue(this.value, true) &&
12000                     this.autoFocus && 
12001                     (
12002                         !this.store.lastOptions ||
12003                         typeof(this.store.lastOptions.add) == 'undefined' || 
12004                         this.store.lastOptions.add != true
12005                     )
12006                 ){
12007                     this.select(0, true);
12008                 }
12009             }else{
12010                 if(this.autoFocus){
12011                     this.selectNext();
12012                 }
12013                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12014                     this.taTask.delay(this.typeAheadDelay);
12015                 }
12016             }
12017         }else{
12018             this.onEmptyResults();
12019         }
12020         
12021         //this.el.focus();
12022     },
12023     // private
12024     onLoadException : function()
12025     {
12026         this.hasQuery = false;
12027         
12028         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12029             this.loading.hide();
12030         }
12031         
12032         if(this.tickable && this.editable){
12033             return;
12034         }
12035         
12036         this.collapse();
12037         
12038         Roo.log(this.store.reader.jsonData);
12039         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12040             // fixme
12041             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12042         }
12043         
12044         
12045     },
12046     // private
12047     onTypeAhead : function(){
12048         if(this.store.getCount() > 0){
12049             var r = this.store.getAt(0);
12050             var newValue = r.data[this.displayField];
12051             var len = newValue.length;
12052             var selStart = this.getRawValue().length;
12053             
12054             if(selStart != len){
12055                 this.setRawValue(newValue);
12056                 this.selectText(selStart, newValue.length);
12057             }
12058         }
12059     },
12060
12061     // private
12062     onSelect : function(record, index){
12063         
12064         if(this.fireEvent('beforeselect', this, record, index) !== false){
12065         
12066             this.setFromData(index > -1 ? record.data : false);
12067             
12068             this.collapse();
12069             this.fireEvent('select', this, record, index);
12070         }
12071     },
12072
12073     /**
12074      * Returns the currently selected field value or empty string if no value is set.
12075      * @return {String} value The selected value
12076      */
12077     getValue : function(){
12078         
12079         if(this.multiple){
12080             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12081         }
12082         
12083         if(this.valueField){
12084             return typeof this.value != 'undefined' ? this.value : '';
12085         }else{
12086             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12087         }
12088     },
12089
12090     /**
12091      * Clears any text/value currently set in the field
12092      */
12093     clearValue : function(){
12094         if(this.hiddenField){
12095             this.hiddenField.dom.value = '';
12096         }
12097         this.value = '';
12098         this.setRawValue('');
12099         this.lastSelectionText = '';
12100         this.lastData = false;
12101         
12102         var close = this.closeTriggerEl();
12103         
12104         if(close){
12105             close.hide();
12106         }
12107         
12108     },
12109
12110     /**
12111      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12112      * will be displayed in the field.  If the value does not match the data value of an existing item,
12113      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12114      * Otherwise the field will be blank (although the value will still be set).
12115      * @param {String} value The value to match
12116      */
12117     setValue : function(v){
12118         if(this.multiple){
12119             this.syncValue();
12120             return;
12121         }
12122         
12123         var text = v;
12124         if(this.valueField){
12125             var r = this.findRecord(this.valueField, v);
12126             if(r){
12127                 text = r.data[this.displayField];
12128             }else if(this.valueNotFoundText !== undefined){
12129                 text = this.valueNotFoundText;
12130             }
12131         }
12132         this.lastSelectionText = text;
12133         if(this.hiddenField){
12134             this.hiddenField.dom.value = v;
12135         }
12136         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12137         this.value = v;
12138         
12139         var close = this.closeTriggerEl();
12140         
12141         if(close){
12142             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12143         }
12144     },
12145     /**
12146      * @property {Object} the last set data for the element
12147      */
12148     
12149     lastData : false,
12150     /**
12151      * Sets the value of the field based on a object which is related to the record format for the store.
12152      * @param {Object} value the value to set as. or false on reset?
12153      */
12154     setFromData : function(o){
12155         
12156         if(this.multiple){
12157             this.addItem(o);
12158             return;
12159         }
12160             
12161         var dv = ''; // display value
12162         var vv = ''; // value value..
12163         this.lastData = o;
12164         if (this.displayField) {
12165             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12166         } else {
12167             // this is an error condition!!!
12168             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12169         }
12170         
12171         if(this.valueField){
12172             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12173         }
12174         
12175         var close = this.closeTriggerEl();
12176         
12177         if(close){
12178             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12179         }
12180         
12181         if(this.hiddenField){
12182             this.hiddenField.dom.value = vv;
12183             
12184             this.lastSelectionText = dv;
12185             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12186             this.value = vv;
12187             return;
12188         }
12189         // no hidden field.. - we store the value in 'value', but still display
12190         // display field!!!!
12191         this.lastSelectionText = dv;
12192         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12193         this.value = vv;
12194         
12195         
12196         
12197     },
12198     // private
12199     reset : function(){
12200         // overridden so that last data is reset..
12201         
12202         if(this.multiple){
12203             this.clearItem();
12204             return;
12205         }
12206         
12207         this.setValue(this.originalValue);
12208         this.clearInvalid();
12209         this.lastData = false;
12210         if (this.view) {
12211             this.view.clearSelections();
12212         }
12213     },
12214     // private
12215     findRecord : function(prop, value){
12216         var record;
12217         if(this.store.getCount() > 0){
12218             this.store.each(function(r){
12219                 if(r.data[prop] == value){
12220                     record = r;
12221                     return false;
12222                 }
12223                 return true;
12224             });
12225         }
12226         return record;
12227     },
12228     
12229     getName: function()
12230     {
12231         // returns hidden if it's set..
12232         if (!this.rendered) {return ''};
12233         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12234         
12235     },
12236     // private
12237     onViewMove : function(e, t){
12238         this.inKeyMode = false;
12239     },
12240
12241     // private
12242     onViewOver : function(e, t){
12243         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12244             return;
12245         }
12246         var item = this.view.findItemFromChild(t);
12247         
12248         if(item){
12249             var index = this.view.indexOf(item);
12250             this.select(index, false);
12251         }
12252     },
12253
12254     // private
12255     onViewClick : function(view, doFocus, el, e)
12256     {
12257         var index = this.view.getSelectedIndexes()[0];
12258         
12259         var r = this.store.getAt(index);
12260         
12261         if(this.tickable){
12262             
12263             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12264                 return;
12265             }
12266             
12267             var rm = false;
12268             var _this = this;
12269             
12270             Roo.each(this.tickItems, function(v,k){
12271                 
12272                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12273                     _this.tickItems.splice(k, 1);
12274                     
12275                     if(typeof(e) == 'undefined' && view == false){
12276                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12277                     }
12278                     
12279                     rm = true;
12280                     return;
12281                 }
12282             });
12283             
12284             if(rm){
12285                 return;
12286             }
12287             
12288             this.tickItems.push(r.data);
12289             
12290             if(typeof(e) == 'undefined' && view == false){
12291                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12292             }
12293                     
12294             return;
12295         }
12296         
12297         if(r){
12298             this.onSelect(r, index);
12299         }
12300         if(doFocus !== false && !this.blockFocus){
12301             this.inputEl().focus();
12302         }
12303     },
12304
12305     // private
12306     restrictHeight : function(){
12307         //this.innerList.dom.style.height = '';
12308         //var inner = this.innerList.dom;
12309         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12310         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12311         //this.list.beginUpdate();
12312         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12313         this.list.alignTo(this.inputEl(), this.listAlign);
12314         this.list.alignTo(this.inputEl(), this.listAlign);
12315         //this.list.endUpdate();
12316     },
12317
12318     // private
12319     onEmptyResults : function(){
12320         
12321         if(this.tickable && this.editable){
12322             this.restrictHeight();
12323             return;
12324         }
12325         
12326         this.collapse();
12327     },
12328
12329     /**
12330      * Returns true if the dropdown list is expanded, else false.
12331      */
12332     isExpanded : function(){
12333         return this.list.isVisible();
12334     },
12335
12336     /**
12337      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12338      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12339      * @param {String} value The data value of the item to select
12340      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12341      * selected item if it is not currently in view (defaults to true)
12342      * @return {Boolean} True if the value matched an item in the list, else false
12343      */
12344     selectByValue : function(v, scrollIntoView){
12345         if(v !== undefined && v !== null){
12346             var r = this.findRecord(this.valueField || this.displayField, v);
12347             if(r){
12348                 this.select(this.store.indexOf(r), scrollIntoView);
12349                 return true;
12350             }
12351         }
12352         return false;
12353     },
12354
12355     /**
12356      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12357      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12358      * @param {Number} index The zero-based index of the list item to select
12359      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12360      * selected item if it is not currently in view (defaults to true)
12361      */
12362     select : function(index, scrollIntoView){
12363         this.selectedIndex = index;
12364         this.view.select(index);
12365         if(scrollIntoView !== false){
12366             var el = this.view.getNode(index);
12367             /*
12368              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12369              */
12370             if(el){
12371                 this.list.scrollChildIntoView(el, false);
12372             }
12373         }
12374     },
12375
12376     // private
12377     selectNext : function(){
12378         var ct = this.store.getCount();
12379         if(ct > 0){
12380             if(this.selectedIndex == -1){
12381                 this.select(0);
12382             }else if(this.selectedIndex < ct-1){
12383                 this.select(this.selectedIndex+1);
12384             }
12385         }
12386     },
12387
12388     // private
12389     selectPrev : function(){
12390         var ct = this.store.getCount();
12391         if(ct > 0){
12392             if(this.selectedIndex == -1){
12393                 this.select(0);
12394             }else if(this.selectedIndex != 0){
12395                 this.select(this.selectedIndex-1);
12396             }
12397         }
12398     },
12399
12400     // private
12401     onKeyUp : function(e){
12402         if(this.editable !== false && !e.isSpecialKey()){
12403             this.lastKey = e.getKey();
12404             this.dqTask.delay(this.queryDelay);
12405         }
12406     },
12407
12408     // private
12409     validateBlur : function(){
12410         return !this.list || !this.list.isVisible();   
12411     },
12412
12413     // private
12414     initQuery : function(){
12415         
12416         var v = this.getRawValue();
12417         
12418         if(this.tickable && this.editable){
12419             v = this.tickableInputEl().getValue();
12420         }
12421         
12422         this.doQuery(v);
12423     },
12424
12425     // private
12426     doForce : function(){
12427         if(this.inputEl().dom.value.length > 0){
12428             this.inputEl().dom.value =
12429                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12430              
12431         }
12432     },
12433
12434     /**
12435      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12436      * query allowing the query action to be canceled if needed.
12437      * @param {String} query The SQL query to execute
12438      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12439      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12440      * saved in the current store (defaults to false)
12441      */
12442     doQuery : function(q, forceAll){
12443         
12444         if(q === undefined || q === null){
12445             q = '';
12446         }
12447         var qe = {
12448             query: q,
12449             forceAll: forceAll,
12450             combo: this,
12451             cancel:false
12452         };
12453         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12454             return false;
12455         }
12456         q = qe.query;
12457         
12458         forceAll = qe.forceAll;
12459         if(forceAll === true || (q.length >= this.minChars)){
12460             
12461             this.hasQuery = true;
12462             
12463             if(this.lastQuery != q || this.alwaysQuery){
12464                 this.lastQuery = q;
12465                 if(this.mode == 'local'){
12466                     this.selectedIndex = -1;
12467                     if(forceAll){
12468                         this.store.clearFilter();
12469                     }else{
12470                         
12471                         if(this.specialFilter){
12472                             this.fireEvent('specialfilter', this);
12473                             this.onLoad();
12474                             return;
12475                         }
12476                         
12477                         this.store.filter(this.displayField, q);
12478                     }
12479                     
12480                     this.store.fireEvent("datachanged", this.store);
12481                     
12482                     this.onLoad();
12483                     
12484                     
12485                 }else{
12486                     
12487                     this.store.baseParams[this.queryParam] = q;
12488                     
12489                     var options = {params : this.getParams(q)};
12490                     
12491                     if(this.loadNext){
12492                         options.add = true;
12493                         options.params.start = this.page * this.pageSize;
12494                     }
12495                     
12496                     this.store.load(options);
12497                     
12498                     /*
12499                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12500                      *  we should expand the list on onLoad
12501                      *  so command out it
12502                      */
12503 //                    this.expand();
12504                 }
12505             }else{
12506                 this.selectedIndex = -1;
12507                 this.onLoad();   
12508             }
12509         }
12510         
12511         this.loadNext = false;
12512     },
12513     
12514     // private
12515     getParams : function(q){
12516         var p = {};
12517         //p[this.queryParam] = q;
12518         
12519         if(this.pageSize){
12520             p.start = 0;
12521             p.limit = this.pageSize;
12522         }
12523         return p;
12524     },
12525
12526     /**
12527      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12528      */
12529     collapse : function(){
12530         if(!this.isExpanded()){
12531             return;
12532         }
12533         
12534         this.list.hide();
12535         
12536         if(this.tickable){
12537             this.hasFocus = false;
12538             this.okBtn.hide();
12539             this.cancelBtn.hide();
12540             this.trigger.show();
12541             
12542             if(this.editable){
12543                 this.tickableInputEl().dom.value = '';
12544                 this.tickableInputEl().blur();
12545             }
12546             
12547         }
12548         
12549         Roo.get(document).un('mousedown', this.collapseIf, this);
12550         Roo.get(document).un('mousewheel', this.collapseIf, this);
12551         if (!this.editable) {
12552             Roo.get(document).un('keydown', this.listKeyPress, this);
12553         }
12554         this.fireEvent('collapse', this);
12555     },
12556
12557     // private
12558     collapseIf : function(e){
12559         var in_combo  = e.within(this.el);
12560         var in_list =  e.within(this.list);
12561         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12562         
12563         if (in_combo || in_list || is_list) {
12564             //e.stopPropagation();
12565             return;
12566         }
12567         
12568         if(this.tickable){
12569             this.onTickableFooterButtonClick(e, false, false);
12570         }
12571
12572         this.collapse();
12573         
12574     },
12575
12576     /**
12577      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12578      */
12579     expand : function(){
12580        
12581         if(this.isExpanded() || !this.hasFocus){
12582             return;
12583         }
12584         
12585         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12586         this.list.setWidth(lw);
12587         
12588         
12589          Roo.log('expand');
12590         
12591         this.list.show();
12592         
12593         this.restrictHeight();
12594         
12595         if(this.tickable){
12596             
12597             this.tickItems = Roo.apply([], this.item);
12598             
12599             this.okBtn.show();
12600             this.cancelBtn.show();
12601             this.trigger.hide();
12602             
12603             if(this.editable){
12604                 this.tickableInputEl().focus();
12605             }
12606             
12607         }
12608         
12609         Roo.get(document).on('mousedown', this.collapseIf, this);
12610         Roo.get(document).on('mousewheel', this.collapseIf, this);
12611         if (!this.editable) {
12612             Roo.get(document).on('keydown', this.listKeyPress, this);
12613         }
12614         
12615         this.fireEvent('expand', this);
12616     },
12617
12618     // private
12619     // Implements the default empty TriggerField.onTriggerClick function
12620     onTriggerClick : function(e)
12621     {
12622         Roo.log('trigger click');
12623         
12624         if(this.disabled || !this.triggerList){
12625             return;
12626         }
12627         
12628         this.page = 0;
12629         this.loadNext = false;
12630         
12631         if(this.isExpanded()){
12632             this.collapse();
12633             if (!this.blockFocus) {
12634                 this.inputEl().focus();
12635             }
12636             
12637         }else {
12638             this.hasFocus = true;
12639             if(this.triggerAction == 'all') {
12640                 this.doQuery(this.allQuery, true);
12641             } else {
12642                 this.doQuery(this.getRawValue());
12643             }
12644             if (!this.blockFocus) {
12645                 this.inputEl().focus();
12646             }
12647         }
12648     },
12649     
12650     onTickableTriggerClick : function(e)
12651     {
12652         if(this.disabled){
12653             return;
12654         }
12655         
12656         this.page = 0;
12657         this.loadNext = false;
12658         this.hasFocus = true;
12659         
12660         if(this.triggerAction == 'all') {
12661             this.doQuery(this.allQuery, true);
12662         } else {
12663             this.doQuery(this.getRawValue());
12664         }
12665     },
12666     
12667     onSearchFieldClick : function(e)
12668     {
12669         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12670             this.onTickableFooterButtonClick(e, false, false);
12671             return;
12672         }
12673         
12674         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12675             return;
12676         }
12677         
12678         this.page = 0;
12679         this.loadNext = false;
12680         this.hasFocus = true;
12681         
12682         if(this.triggerAction == 'all') {
12683             this.doQuery(this.allQuery, true);
12684         } else {
12685             this.doQuery(this.getRawValue());
12686         }
12687     },
12688     
12689     listKeyPress : function(e)
12690     {
12691         //Roo.log('listkeypress');
12692         // scroll to first matching element based on key pres..
12693         if (e.isSpecialKey()) {
12694             return false;
12695         }
12696         var k = String.fromCharCode(e.getKey()).toUpperCase();
12697         //Roo.log(k);
12698         var match  = false;
12699         var csel = this.view.getSelectedNodes();
12700         var cselitem = false;
12701         if (csel.length) {
12702             var ix = this.view.indexOf(csel[0]);
12703             cselitem  = this.store.getAt(ix);
12704             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12705                 cselitem = false;
12706             }
12707             
12708         }
12709         
12710         this.store.each(function(v) { 
12711             if (cselitem) {
12712                 // start at existing selection.
12713                 if (cselitem.id == v.id) {
12714                     cselitem = false;
12715                 }
12716                 return true;
12717             }
12718                 
12719             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12720                 match = this.store.indexOf(v);
12721                 return false;
12722             }
12723             return true;
12724         }, this);
12725         
12726         if (match === false) {
12727             return true; // no more action?
12728         }
12729         // scroll to?
12730         this.view.select(match);
12731         var sn = Roo.get(this.view.getSelectedNodes()[0])
12732         sn.scrollIntoView(sn.dom.parentNode, false);
12733     },
12734     
12735     onViewScroll : function(e, t){
12736         
12737         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){
12738             return;
12739         }
12740         
12741         this.hasQuery = true;
12742         
12743         this.loading = this.list.select('.loading', true).first();
12744         
12745         if(this.loading === null){
12746             this.list.createChild({
12747                 tag: 'div',
12748                 cls: 'loading select2-more-results select2-active',
12749                 html: 'Loading more results...'
12750             })
12751             
12752             this.loading = this.list.select('.loading', true).first();
12753             
12754             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12755             
12756             this.loading.hide();
12757         }
12758         
12759         this.loading.show();
12760         
12761         var _combo = this;
12762         
12763         this.page++;
12764         this.loadNext = true;
12765         
12766         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12767         
12768         return;
12769     },
12770     
12771     addItem : function(o)
12772     {   
12773         var dv = ''; // display value
12774         
12775         if (this.displayField) {
12776             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12777         } else {
12778             // this is an error condition!!!
12779             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12780         }
12781         
12782         if(!dv.length){
12783             return;
12784         }
12785         
12786         var choice = this.choices.createChild({
12787             tag: 'li',
12788             cls: 'select2-search-choice',
12789             cn: [
12790                 {
12791                     tag: 'div',
12792                     html: dv
12793                 },
12794                 {
12795                     tag: 'a',
12796                     href: '#',
12797                     cls: 'select2-search-choice-close',
12798                     tabindex: '-1'
12799                 }
12800             ]
12801             
12802         }, this.searchField);
12803         
12804         var close = choice.select('a.select2-search-choice-close', true).first()
12805         
12806         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12807         
12808         this.item.push(o);
12809         
12810         this.lastData = o;
12811         
12812         this.syncValue();
12813         
12814         this.inputEl().dom.value = '';
12815         
12816         this.validate();
12817     },
12818     
12819     onRemoveItem : function(e, _self, o)
12820     {
12821         e.preventDefault();
12822         
12823         this.lastItem = Roo.apply([], this.item);
12824         
12825         var index = this.item.indexOf(o.data) * 1;
12826         
12827         if( index < 0){
12828             Roo.log('not this item?!');
12829             return;
12830         }
12831         
12832         this.item.splice(index, 1);
12833         o.item.remove();
12834         
12835         this.syncValue();
12836         
12837         this.fireEvent('remove', this, e);
12838         
12839         this.validate();
12840         
12841     },
12842     
12843     syncValue : function()
12844     {
12845         if(!this.item.length){
12846             this.clearValue();
12847             return;
12848         }
12849             
12850         var value = [];
12851         var _this = this;
12852         Roo.each(this.item, function(i){
12853             if(_this.valueField){
12854                 value.push(i[_this.valueField]);
12855                 return;
12856             }
12857
12858             value.push(i);
12859         });
12860
12861         this.value = value.join(',');
12862
12863         if(this.hiddenField){
12864             this.hiddenField.dom.value = this.value;
12865         }
12866         
12867         this.store.fireEvent("datachanged", this.store);
12868     },
12869     
12870     clearItem : function()
12871     {
12872         if(!this.multiple){
12873             return;
12874         }
12875         
12876         this.item = [];
12877         
12878         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12879            c.remove();
12880         });
12881         
12882         this.syncValue();
12883         
12884         this.validate();
12885     },
12886     
12887     inputEl: function ()
12888     {
12889         if(Roo.isTouch && this.mobileTouchView){
12890             return this.el.select('input.form-control',true).first();
12891         }
12892         
12893         if(this.tickable){
12894             return this.searchField;
12895         }
12896         
12897         return this.el.select('input.form-control',true).first();
12898     },
12899     
12900     
12901     onTickableFooterButtonClick : function(e, btn, el)
12902     {
12903         e.preventDefault();
12904         
12905         this.lastItem = Roo.apply([], this.item);
12906         
12907         if(btn && btn.name == 'cancel'){
12908             this.tickItems = Roo.apply([], this.item);
12909             this.collapse();
12910             return;
12911         }
12912         
12913         this.clearItem();
12914         
12915         var _this = this;
12916         
12917         Roo.each(this.tickItems, function(o){
12918             _this.addItem(o);
12919         });
12920         
12921         this.collapse();
12922         
12923     },
12924     
12925     validate : function()
12926     {
12927         var v = this.getRawValue();
12928         
12929         if(this.multiple){
12930             v = this.getValue();
12931         }
12932         
12933         if(this.disabled || this.allowBlank || v.length){
12934             this.markValid();
12935             return true;
12936         }
12937         
12938         this.markInvalid();
12939         return false;
12940     },
12941     
12942     tickableInputEl : function()
12943     {
12944         if(!this.tickable || !this.editable){
12945             return this.inputEl();
12946         }
12947         
12948         return this.inputEl().select('.select2-search-field-input', true).first();
12949     },
12950     
12951     
12952     getAutoCreateTouchView : function()
12953     {
12954         var id = Roo.id();
12955         
12956         var cfg = {
12957             cls: 'form-group' //input-group
12958         };
12959         
12960         var input =  {
12961             tag: 'input',
12962             id : id,
12963             type : this.inputType,
12964             cls : 'form-control x-combo-noedit',
12965             autocomplete: 'new-password',
12966             placeholder : this.placeholder || '',
12967             readonly : true
12968         };
12969         
12970         if (this.name) {
12971             input.name = this.name;
12972         }
12973         
12974         if (this.size) {
12975             input.cls += ' input-' + this.size;
12976         }
12977         
12978         if (this.disabled) {
12979             input.disabled = true;
12980         }
12981         
12982         var inputblock = {
12983             cls : '',
12984             cn : [
12985                 input
12986             ]
12987         };
12988         
12989         if(this.before){
12990             inputblock.cls += ' input-group';
12991             
12992             inputblock.cn.unshift({
12993                 tag :'span',
12994                 cls : 'input-group-addon',
12995                 html : this.before
12996             });
12997         }
12998         
12999         if(this.removable && !this.multiple){
13000             inputblock.cls += ' roo-removable';
13001             
13002             inputblock.cn.push({
13003                 tag: 'button',
13004                 html : 'x',
13005                 cls : 'roo-combo-removable-btn close'
13006             });
13007         }
13008
13009         if(this.hasFeedback && !this.allowBlank){
13010             
13011             inputblock.cls += ' has-feedback';
13012             
13013             inputblock.cn.push({
13014                 tag: 'span',
13015                 cls: 'glyphicon form-control-feedback'
13016             });
13017             
13018         }
13019         
13020         if (this.after) {
13021             
13022             inputblock.cls += (this.before) ? '' : ' input-group';
13023             
13024             inputblock.cn.push({
13025                 tag :'span',
13026                 cls : 'input-group-addon',
13027                 html : this.after
13028             });
13029         }
13030
13031         var box = {
13032             tag: 'div',
13033             cn: [
13034                 {
13035                     tag: 'input',
13036                     type : 'hidden',
13037                     cls: 'form-hidden-field'
13038                 },
13039                 inputblock
13040             ]
13041             
13042         };
13043         
13044         if(this.multiple){
13045             box = {
13046                 tag: 'div',
13047                 cn: [
13048                     {
13049                         tag: 'input',
13050                         type : 'hidden',
13051                         cls: 'form-hidden-field'
13052                     },
13053                     {
13054                         tag: 'ul',
13055                         cls: 'select2-choices',
13056                         cn:[
13057                             {
13058                                 tag: 'li',
13059                                 cls: 'select2-search-field',
13060                                 cn: [
13061
13062                                     inputblock
13063                                 ]
13064                             }
13065                         ]
13066                     }
13067                 ]
13068             }
13069         };
13070         
13071         var combobox = {
13072             cls: 'select2-container input-group',
13073             cn: [
13074                 box
13075             ]
13076         };
13077         
13078         if(this.multiple){
13079             combobox.cls += ' select2-container-multi';
13080         }
13081         
13082         var align = this.labelAlign || this.parentLabelAlign();
13083         
13084         cfg.cn = combobox;
13085         
13086         if(this.fieldLabel.length){
13087             
13088             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13089             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13090             
13091             cfg.cn = [
13092                 {
13093                     tag: 'label',
13094                     cls : 'control-label ' + lw,
13095                     html : this.fieldLabel
13096
13097                 },
13098                 {
13099                     cls : cw, 
13100                     cn: [
13101                         combobox
13102                     ]
13103                 }
13104             ];
13105         }
13106         
13107         var settings = this;
13108         
13109         ['xs','sm','md','lg'].map(function(size){
13110             if (settings[size]) {
13111                 cfg.cls += ' col-' + size + '-' + settings[size];
13112             }
13113         });
13114         
13115         return cfg;
13116     },
13117     
13118     initTouchView : function()
13119     {
13120         this.renderTouchView();
13121         
13122         this.inputEl().on("click", this.showTouchView, this);
13123         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13124         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13125         
13126         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13127         
13128         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13129         this.store.on('load', this.onTouchViewLoad, this);
13130         this.store.on('loadexception', this.onTouchViewLoadException, this);
13131         
13132         if(this.hiddenName){
13133             
13134             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13135             
13136             this.hiddenField.dom.value =
13137                 this.hiddenValue !== undefined ? this.hiddenValue :
13138                 this.value !== undefined ? this.value : '';
13139         
13140             this.el.dom.removeAttribute('name');
13141             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13142         }
13143         
13144         if(this.multiple){
13145             this.choices = this.el.select('ul.select2-choices', true).first();
13146             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13147         }
13148         
13149         if(this.removable && !this.multiple){
13150             var close = this.closeTriggerEl();
13151             if(close){
13152                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13153                 close.on('click', this.removeBtnClick, this, close);
13154             }
13155         }
13156         
13157         return;
13158         
13159         
13160     },
13161     
13162     renderTouchView : function()
13163     {
13164         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13165         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13166         
13167         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13168         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13169         
13170         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13171         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13172         this.touchViewBodyEl.setStyle('overflow', 'auto');
13173         
13174         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13175         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13176         
13177         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13178         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13179         
13180     },
13181     
13182     showTouchView : function()
13183     {
13184         this.touchViewHeaderEl.hide();
13185
13186         if(this.fieldLabel.length){
13187             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13188             this.touchViewHeaderEl.show();
13189         }
13190
13191         this.touchViewEl.show();
13192
13193         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13194         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13195
13196         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13197
13198         if(this.fieldLabel.length){
13199             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13200         }
13201
13202         this.touchViewBodyEl.setHeight(bodyHeight);
13203
13204         if(this.animate){
13205             var _this = this;
13206             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13207         }else{
13208             this.touchViewEl.addClass('in');
13209         }
13210
13211         this.doTouchViewQuery();
13212         
13213     },
13214     
13215     hideTouchView : function()
13216     {
13217         this.touchViewEl.removeClass('in');
13218
13219         if(this.animate){
13220             var _this = this;
13221             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13222         }else{
13223             this.touchViewEl.setStyle('display', 'none');
13224         }
13225         
13226     },
13227     
13228     setTouchViewValue : function()
13229     {
13230         if(this.multiple){
13231             this.clearItem();
13232         
13233             var _this = this;
13234
13235             Roo.each(this.tickItems, function(o){
13236                 this.addItem(o);
13237             }, this);
13238         }
13239         
13240         this.hideTouchView();
13241     },
13242     
13243     doTouchViewQuery : function()
13244     {
13245         var qe = {
13246             query: '',
13247             forceAll: true,
13248             combo: this,
13249             cancel:false
13250         };
13251         
13252         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13253             return false;
13254         }
13255         
13256         if(!this.alwaysQuery || this.mode == 'local'){
13257             this.onTouchViewLoad();
13258             return;
13259         }
13260         
13261         this.store.load();
13262     },
13263     
13264     onTouchViewBeforeLoad : function(combo,opts)
13265     {
13266         return;
13267     },
13268
13269     // private
13270     onTouchViewLoad : function()
13271     {
13272         if(this.store.getCount() < 1){
13273             this.onTouchViewEmptyResults();
13274             return;
13275         }
13276         
13277         this.clearTouchView();
13278         
13279         var rawValue = this.getRawValue();
13280         
13281         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13282         
13283         this.tickItems = [];
13284         
13285         this.store.data.each(function(d, rowIndex){
13286             var row = this.touchViewListGroup.createChild(template);
13287             
13288             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13289                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13290             }
13291             
13292             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13293                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13294             }
13295             
13296             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13297                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13298                 this.tickItems.push(d.data);
13299             }
13300             
13301             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13302             
13303         }, this);
13304         
13305         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13306         
13307         if(firstChecked){
13308             firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom);
13309         }
13310         
13311     },
13312     
13313     onTouchViewLoadException : function()
13314     {
13315         this.hideTouchView();
13316     },
13317     
13318     onTouchViewEmptyResults : function()
13319     {
13320         this.clearTouchView();
13321         
13322         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13323         
13324         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13325         
13326     },
13327     
13328     clearTouchView : function()
13329     {
13330         this.touchViewListGroup.dom.innerHTML = '';
13331     },
13332     
13333     onTouchViewClick : function(e, el, o)
13334     {
13335         e.preventDefault();
13336         
13337         var row = o.row;
13338         var rowIndex = o.rowIndex;
13339         
13340         var r = this.store.getAt(rowIndex);
13341         
13342         if(!this.multiple){
13343             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13344                 c.dom.removeAttribute('checked');
13345             }, this);
13346             
13347             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13348         
13349             this.setFromData(r.data);
13350             
13351             var close = this.closeTriggerEl();
13352         
13353             if(close){
13354                 close.show();
13355             }
13356
13357             this.hideTouchView();
13358             
13359             return;
13360         }
13361         
13362         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13363             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13364             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13365             return;
13366         }
13367         
13368         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13369         this.addItem(r.data);
13370         this.tickItems.push(r.data);
13371         
13372         
13373         
13374     }
13375     
13376
13377     /** 
13378     * @cfg {Boolean} grow 
13379     * @hide 
13380     */
13381     /** 
13382     * @cfg {Number} growMin 
13383     * @hide 
13384     */
13385     /** 
13386     * @cfg {Number} growMax 
13387     * @hide 
13388     */
13389     /**
13390      * @hide
13391      * @method autoSize
13392      */
13393 });
13394
13395 Roo.apply(Roo.bootstrap.ComboBox,  {
13396     
13397     header : {
13398         tag: 'div',
13399         cls: 'modal-header',
13400         cn: [
13401             {
13402                 tag: 'h4',
13403                 cls: 'modal-title'
13404             }
13405         ]
13406     },
13407     
13408     body : {
13409         tag: 'div',
13410         cls: 'modal-body',
13411         cn: [
13412             {
13413                 tag: 'ul',
13414                 cls: 'list-group'
13415             }
13416         ]
13417     },
13418     
13419     listItemRadio : {
13420         tag: 'li',
13421         cls: 'list-group-item',
13422         cn: [
13423             {
13424                 tag: 'span',
13425                 cls: 'roo-combobox-list-group-item-value'
13426             },
13427             {
13428                 tag: 'div',
13429                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13430                 cn: [
13431                     {
13432                         tag: 'input',
13433                         type: 'radio'
13434                     },
13435                     {
13436                         tag: 'label'
13437                     }
13438                 ]
13439             }
13440         ]
13441     },
13442     
13443     listItemCheckbox : {
13444         tag: 'li',
13445         cls: 'list-group-item',
13446         cn: [
13447             {
13448                 tag: 'span',
13449                 cls: 'roo-combobox-list-group-item-value'
13450             },
13451             {
13452                 tag: 'div',
13453                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13454                 cn: [
13455                     {
13456                         tag: 'input',
13457                         type: 'checkbox'
13458                     },
13459                     {
13460                         tag: 'label'
13461                     }
13462                 ]
13463             }
13464         ]
13465     },
13466     
13467     emptyResult : {
13468         tag: 'div',
13469         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13470     },
13471     
13472     footer : {
13473         tag: 'div',
13474         cls: 'modal-footer',
13475         cn: [
13476             {
13477                 tag: 'div',
13478                 cls: 'row',
13479                 cn: [
13480                     {
13481                         tag: 'div',
13482                         cls: 'col-xs-6 text-left',
13483                         cn: {
13484                             tag: 'button',
13485                             cls: 'btn btn-danger roo-touch-view-cancel',
13486                             html: 'Cancel'
13487                         }
13488                     },
13489                     {
13490                         tag: 'div',
13491                         cls: 'col-xs-6 text-right',
13492                         cn: {
13493                             tag: 'button',
13494                             cls: 'btn btn-success roo-touch-view-ok',
13495                             html: 'OK'
13496                         }
13497                     }
13498                 ]
13499             }
13500         ]
13501         
13502     }
13503 });
13504
13505 Roo.apply(Roo.bootstrap.ComboBox,  {
13506     
13507     touchViewTemplate : {
13508         tag: 'div',
13509         cls: 'modal fade roo-combobox-touch-view',
13510         cn: [
13511             {
13512                 tag: 'div',
13513                 cls: 'modal-dialog',
13514                 cn: [
13515                     {
13516                         tag: 'div',
13517                         cls: 'modal-content',
13518                         cn: [
13519                             Roo.bootstrap.ComboBox.header,
13520                             Roo.bootstrap.ComboBox.body,
13521                             Roo.bootstrap.ComboBox.footer
13522                         ]
13523                     }
13524                 ]
13525             }
13526         ]
13527     }
13528 });/*
13529  * Based on:
13530  * Ext JS Library 1.1.1
13531  * Copyright(c) 2006-2007, Ext JS, LLC.
13532  *
13533  * Originally Released Under LGPL - original licence link has changed is not relivant.
13534  *
13535  * Fork - LGPL
13536  * <script type="text/javascript">
13537  */
13538
13539 /**
13540  * @class Roo.View
13541  * @extends Roo.util.Observable
13542  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13543  * This class also supports single and multi selection modes. <br>
13544  * Create a data model bound view:
13545  <pre><code>
13546  var store = new Roo.data.Store(...);
13547
13548  var view = new Roo.View({
13549     el : "my-element",
13550     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13551  
13552     singleSelect: true,
13553     selectedClass: "ydataview-selected",
13554     store: store
13555  });
13556
13557  // listen for node click?
13558  view.on("click", function(vw, index, node, e){
13559  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13560  });
13561
13562  // load XML data
13563  dataModel.load("foobar.xml");
13564  </code></pre>
13565  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13566  * <br><br>
13567  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13568  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13569  * 
13570  * Note: old style constructor is still suported (container, template, config)
13571  * 
13572  * @constructor
13573  * Create a new View
13574  * @param {Object} config The config object
13575  * 
13576  */
13577 Roo.View = function(config, depreciated_tpl, depreciated_config){
13578     
13579     this.parent = false;
13580     
13581     if (typeof(depreciated_tpl) == 'undefined') {
13582         // new way.. - universal constructor.
13583         Roo.apply(this, config);
13584         this.el  = Roo.get(this.el);
13585     } else {
13586         // old format..
13587         this.el  = Roo.get(config);
13588         this.tpl = depreciated_tpl;
13589         Roo.apply(this, depreciated_config);
13590     }
13591     this.wrapEl  = this.el.wrap().wrap();
13592     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13593     
13594     
13595     if(typeof(this.tpl) == "string"){
13596         this.tpl = new Roo.Template(this.tpl);
13597     } else {
13598         // support xtype ctors..
13599         this.tpl = new Roo.factory(this.tpl, Roo);
13600     }
13601     
13602     
13603     this.tpl.compile();
13604     
13605     /** @private */
13606     this.addEvents({
13607         /**
13608          * @event beforeclick
13609          * Fires before a click is processed. Returns false to cancel the default action.
13610          * @param {Roo.View} this
13611          * @param {Number} index The index of the target node
13612          * @param {HTMLElement} node The target node
13613          * @param {Roo.EventObject} e The raw event object
13614          */
13615             "beforeclick" : true,
13616         /**
13617          * @event click
13618          * Fires when a template node is clicked.
13619          * @param {Roo.View} this
13620          * @param {Number} index The index of the target node
13621          * @param {HTMLElement} node The target node
13622          * @param {Roo.EventObject} e The raw event object
13623          */
13624             "click" : true,
13625         /**
13626          * @event dblclick
13627          * Fires when a template node is double clicked.
13628          * @param {Roo.View} this
13629          * @param {Number} index The index of the target node
13630          * @param {HTMLElement} node The target node
13631          * @param {Roo.EventObject} e The raw event object
13632          */
13633             "dblclick" : true,
13634         /**
13635          * @event contextmenu
13636          * Fires when a template node is right clicked.
13637          * @param {Roo.View} this
13638          * @param {Number} index The index of the target node
13639          * @param {HTMLElement} node The target node
13640          * @param {Roo.EventObject} e The raw event object
13641          */
13642             "contextmenu" : true,
13643         /**
13644          * @event selectionchange
13645          * Fires when the selected nodes change.
13646          * @param {Roo.View} this
13647          * @param {Array} selections Array of the selected nodes
13648          */
13649             "selectionchange" : true,
13650     
13651         /**
13652          * @event beforeselect
13653          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13654          * @param {Roo.View} this
13655          * @param {HTMLElement} node The node to be selected
13656          * @param {Array} selections Array of currently selected nodes
13657          */
13658             "beforeselect" : true,
13659         /**
13660          * @event preparedata
13661          * Fires on every row to render, to allow you to change the data.
13662          * @param {Roo.View} this
13663          * @param {Object} data to be rendered (change this)
13664          */
13665           "preparedata" : true
13666           
13667           
13668         });
13669
13670
13671
13672     this.el.on({
13673         "click": this.onClick,
13674         "dblclick": this.onDblClick,
13675         "contextmenu": this.onContextMenu,
13676         scope:this
13677     });
13678
13679     this.selections = [];
13680     this.nodes = [];
13681     this.cmp = new Roo.CompositeElementLite([]);
13682     if(this.store){
13683         this.store = Roo.factory(this.store, Roo.data);
13684         this.setStore(this.store, true);
13685     }
13686     
13687     if ( this.footer && this.footer.xtype) {
13688            
13689          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13690         
13691         this.footer.dataSource = this.store
13692         this.footer.container = fctr;
13693         this.footer = Roo.factory(this.footer, Roo);
13694         fctr.insertFirst(this.el);
13695         
13696         // this is a bit insane - as the paging toolbar seems to detach the el..
13697 //        dom.parentNode.parentNode.parentNode
13698          // they get detached?
13699     }
13700     
13701     
13702     Roo.View.superclass.constructor.call(this);
13703     
13704     
13705 };
13706
13707 Roo.extend(Roo.View, Roo.util.Observable, {
13708     
13709      /**
13710      * @cfg {Roo.data.Store} store Data store to load data from.
13711      */
13712     store : false,
13713     
13714     /**
13715      * @cfg {String|Roo.Element} el The container element.
13716      */
13717     el : '',
13718     
13719     /**
13720      * @cfg {String|Roo.Template} tpl The template used by this View 
13721      */
13722     tpl : false,
13723     /**
13724      * @cfg {String} dataName the named area of the template to use as the data area
13725      *                          Works with domtemplates roo-name="name"
13726      */
13727     dataName: false,
13728     /**
13729      * @cfg {String} selectedClass The css class to add to selected nodes
13730      */
13731     selectedClass : "x-view-selected",
13732      /**
13733      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13734      */
13735     emptyText : "",
13736     
13737     /**
13738      * @cfg {String} text to display on mask (default Loading)
13739      */
13740     mask : false,
13741     /**
13742      * @cfg {Boolean} multiSelect Allow multiple selection
13743      */
13744     multiSelect : false,
13745     /**
13746      * @cfg {Boolean} singleSelect Allow single selection
13747      */
13748     singleSelect:  false,
13749     
13750     /**
13751      * @cfg {Boolean} toggleSelect - selecting 
13752      */
13753     toggleSelect : false,
13754     
13755     /**
13756      * @cfg {Boolean} tickable - selecting 
13757      */
13758     tickable : false,
13759     
13760     /**
13761      * Returns the element this view is bound to.
13762      * @return {Roo.Element}
13763      */
13764     getEl : function(){
13765         return this.wrapEl;
13766     },
13767     
13768     
13769
13770     /**
13771      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13772      */
13773     refresh : function(){
13774         //Roo.log('refresh');
13775         var t = this.tpl;
13776         
13777         // if we are using something like 'domtemplate', then
13778         // the what gets used is:
13779         // t.applySubtemplate(NAME, data, wrapping data..)
13780         // the outer template then get' applied with
13781         //     the store 'extra data'
13782         // and the body get's added to the
13783         //      roo-name="data" node?
13784         //      <span class='roo-tpl-{name}'></span> ?????
13785         
13786         
13787         
13788         this.clearSelections();
13789         this.el.update("");
13790         var html = [];
13791         var records = this.store.getRange();
13792         if(records.length < 1) {
13793             
13794             // is this valid??  = should it render a template??
13795             
13796             this.el.update(this.emptyText);
13797             return;
13798         }
13799         var el = this.el;
13800         if (this.dataName) {
13801             this.el.update(t.apply(this.store.meta)); //????
13802             el = this.el.child('.roo-tpl-' + this.dataName);
13803         }
13804         
13805         for(var i = 0, len = records.length; i < len; i++){
13806             var data = this.prepareData(records[i].data, i, records[i]);
13807             this.fireEvent("preparedata", this, data, i, records[i]);
13808             
13809             var d = Roo.apply({}, data);
13810             
13811             if(this.tickable){
13812                 Roo.apply(d, {'roo-id' : Roo.id()});
13813                 
13814                 var _this = this;
13815             
13816                 Roo.each(this.parent.item, function(item){
13817                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13818                         return;
13819                     }
13820                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13821                 });
13822             }
13823             
13824             html[html.length] = Roo.util.Format.trim(
13825                 this.dataName ?
13826                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13827                     t.apply(d)
13828             );
13829         }
13830         
13831         
13832         
13833         el.update(html.join(""));
13834         this.nodes = el.dom.childNodes;
13835         this.updateIndexes(0);
13836     },
13837     
13838
13839     /**
13840      * Function to override to reformat the data that is sent to
13841      * the template for each node.
13842      * DEPRICATED - use the preparedata event handler.
13843      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13844      * a JSON object for an UpdateManager bound view).
13845      */
13846     prepareData : function(data, index, record)
13847     {
13848         this.fireEvent("preparedata", this, data, index, record);
13849         return data;
13850     },
13851
13852     onUpdate : function(ds, record){
13853         // Roo.log('on update');   
13854         this.clearSelections();
13855         var index = this.store.indexOf(record);
13856         var n = this.nodes[index];
13857         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13858         n.parentNode.removeChild(n);
13859         this.updateIndexes(index, index);
13860     },
13861
13862     
13863     
13864 // --------- FIXME     
13865     onAdd : function(ds, records, index)
13866     {
13867         //Roo.log(['on Add', ds, records, index] );        
13868         this.clearSelections();
13869         if(this.nodes.length == 0){
13870             this.refresh();
13871             return;
13872         }
13873         var n = this.nodes[index];
13874         for(var i = 0, len = records.length; i < len; i++){
13875             var d = this.prepareData(records[i].data, i, records[i]);
13876             if(n){
13877                 this.tpl.insertBefore(n, d);
13878             }else{
13879                 
13880                 this.tpl.append(this.el, d);
13881             }
13882         }
13883         this.updateIndexes(index);
13884     },
13885
13886     onRemove : function(ds, record, index){
13887        // Roo.log('onRemove');
13888         this.clearSelections();
13889         var el = this.dataName  ?
13890             this.el.child('.roo-tpl-' + this.dataName) :
13891             this.el; 
13892         
13893         el.dom.removeChild(this.nodes[index]);
13894         this.updateIndexes(index);
13895     },
13896
13897     /**
13898      * Refresh an individual node.
13899      * @param {Number} index
13900      */
13901     refreshNode : function(index){
13902         this.onUpdate(this.store, this.store.getAt(index));
13903     },
13904
13905     updateIndexes : function(startIndex, endIndex){
13906         var ns = this.nodes;
13907         startIndex = startIndex || 0;
13908         endIndex = endIndex || ns.length - 1;
13909         for(var i = startIndex; i <= endIndex; i++){
13910             ns[i].nodeIndex = i;
13911         }
13912     },
13913
13914     /**
13915      * Changes the data store this view uses and refresh the view.
13916      * @param {Store} store
13917      */
13918     setStore : function(store, initial){
13919         if(!initial && this.store){
13920             this.store.un("datachanged", this.refresh);
13921             this.store.un("add", this.onAdd);
13922             this.store.un("remove", this.onRemove);
13923             this.store.un("update", this.onUpdate);
13924             this.store.un("clear", this.refresh);
13925             this.store.un("beforeload", this.onBeforeLoad);
13926             this.store.un("load", this.onLoad);
13927             this.store.un("loadexception", this.onLoad);
13928         }
13929         if(store){
13930           
13931             store.on("datachanged", this.refresh, this);
13932             store.on("add", this.onAdd, this);
13933             store.on("remove", this.onRemove, this);
13934             store.on("update", this.onUpdate, this);
13935             store.on("clear", this.refresh, this);
13936             store.on("beforeload", this.onBeforeLoad, this);
13937             store.on("load", this.onLoad, this);
13938             store.on("loadexception", this.onLoad, this);
13939         }
13940         
13941         if(store){
13942             this.refresh();
13943         }
13944     },
13945     /**
13946      * onbeforeLoad - masks the loading area.
13947      *
13948      */
13949     onBeforeLoad : function(store,opts)
13950     {
13951          //Roo.log('onBeforeLoad');   
13952         if (!opts.add) {
13953             this.el.update("");
13954         }
13955         this.el.mask(this.mask ? this.mask : "Loading" ); 
13956     },
13957     onLoad : function ()
13958     {
13959         this.el.unmask();
13960     },
13961     
13962
13963     /**
13964      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13965      * @param {HTMLElement} node
13966      * @return {HTMLElement} The template node
13967      */
13968     findItemFromChild : function(node){
13969         var el = this.dataName  ?
13970             this.el.child('.roo-tpl-' + this.dataName,true) :
13971             this.el.dom; 
13972         
13973         if(!node || node.parentNode == el){
13974                     return node;
13975             }
13976             var p = node.parentNode;
13977             while(p && p != el){
13978             if(p.parentNode == el){
13979                 return p;
13980             }
13981             p = p.parentNode;
13982         }
13983             return null;
13984     },
13985
13986     /** @ignore */
13987     onClick : function(e){
13988         var item = this.findItemFromChild(e.getTarget());
13989         if(item){
13990             var index = this.indexOf(item);
13991             if(this.onItemClick(item, index, e) !== false){
13992                 this.fireEvent("click", this, index, item, e);
13993             }
13994         }else{
13995             this.clearSelections();
13996         }
13997     },
13998
13999     /** @ignore */
14000     onContextMenu : function(e){
14001         var item = this.findItemFromChild(e.getTarget());
14002         if(item){
14003             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14004         }
14005     },
14006
14007     /** @ignore */
14008     onDblClick : function(e){
14009         var item = this.findItemFromChild(e.getTarget());
14010         if(item){
14011             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14012         }
14013     },
14014
14015     onItemClick : function(item, index, e)
14016     {
14017         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14018             return false;
14019         }
14020         if (this.toggleSelect) {
14021             var m = this.isSelected(item) ? 'unselect' : 'select';
14022             //Roo.log(m);
14023             var _t = this;
14024             _t[m](item, true, false);
14025             return true;
14026         }
14027         if(this.multiSelect || this.singleSelect){
14028             if(this.multiSelect && e.shiftKey && this.lastSelection){
14029                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14030             }else{
14031                 this.select(item, this.multiSelect && e.ctrlKey);
14032                 this.lastSelection = item;
14033             }
14034             
14035             if(!this.tickable){
14036                 e.preventDefault();
14037             }
14038             
14039         }
14040         return true;
14041     },
14042
14043     /**
14044      * Get the number of selected nodes.
14045      * @return {Number}
14046      */
14047     getSelectionCount : function(){
14048         return this.selections.length;
14049     },
14050
14051     /**
14052      * Get the currently selected nodes.
14053      * @return {Array} An array of HTMLElements
14054      */
14055     getSelectedNodes : function(){
14056         return this.selections;
14057     },
14058
14059     /**
14060      * Get the indexes of the selected nodes.
14061      * @return {Array}
14062      */
14063     getSelectedIndexes : function(){
14064         var indexes = [], s = this.selections;
14065         for(var i = 0, len = s.length; i < len; i++){
14066             indexes.push(s[i].nodeIndex);
14067         }
14068         return indexes;
14069     },
14070
14071     /**
14072      * Clear all selections
14073      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14074      */
14075     clearSelections : function(suppressEvent){
14076         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14077             this.cmp.elements = this.selections;
14078             this.cmp.removeClass(this.selectedClass);
14079             this.selections = [];
14080             if(!suppressEvent){
14081                 this.fireEvent("selectionchange", this, this.selections);
14082             }
14083         }
14084     },
14085
14086     /**
14087      * Returns true if the passed node is selected
14088      * @param {HTMLElement/Number} node The node or node index
14089      * @return {Boolean}
14090      */
14091     isSelected : function(node){
14092         var s = this.selections;
14093         if(s.length < 1){
14094             return false;
14095         }
14096         node = this.getNode(node);
14097         return s.indexOf(node) !== -1;
14098     },
14099
14100     /**
14101      * Selects nodes.
14102      * @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
14103      * @param {Boolean} keepExisting (optional) true to keep existing selections
14104      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14105      */
14106     select : function(nodeInfo, keepExisting, suppressEvent){
14107         if(nodeInfo instanceof Array){
14108             if(!keepExisting){
14109                 this.clearSelections(true);
14110             }
14111             for(var i = 0, len = nodeInfo.length; i < len; i++){
14112                 this.select(nodeInfo[i], true, true);
14113             }
14114             return;
14115         } 
14116         var node = this.getNode(nodeInfo);
14117         if(!node || this.isSelected(node)){
14118             return; // already selected.
14119         }
14120         if(!keepExisting){
14121             this.clearSelections(true);
14122         }
14123         
14124         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14125             Roo.fly(node).addClass(this.selectedClass);
14126             this.selections.push(node);
14127             if(!suppressEvent){
14128                 this.fireEvent("selectionchange", this, this.selections);
14129             }
14130         }
14131         
14132         
14133     },
14134       /**
14135      * Unselects nodes.
14136      * @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
14137      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14138      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14139      */
14140     unselect : function(nodeInfo, keepExisting, suppressEvent)
14141     {
14142         if(nodeInfo instanceof Array){
14143             Roo.each(this.selections, function(s) {
14144                 this.unselect(s, nodeInfo);
14145             }, this);
14146             return;
14147         }
14148         var node = this.getNode(nodeInfo);
14149         if(!node || !this.isSelected(node)){
14150             //Roo.log("not selected");
14151             return; // not selected.
14152         }
14153         // fireevent???
14154         var ns = [];
14155         Roo.each(this.selections, function(s) {
14156             if (s == node ) {
14157                 Roo.fly(node).removeClass(this.selectedClass);
14158
14159                 return;
14160             }
14161             ns.push(s);
14162         },this);
14163         
14164         this.selections= ns;
14165         this.fireEvent("selectionchange", this, this.selections);
14166     },
14167
14168     /**
14169      * Gets a template node.
14170      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14171      * @return {HTMLElement} The node or null if it wasn't found
14172      */
14173     getNode : function(nodeInfo){
14174         if(typeof nodeInfo == "string"){
14175             return document.getElementById(nodeInfo);
14176         }else if(typeof nodeInfo == "number"){
14177             return this.nodes[nodeInfo];
14178         }
14179         return nodeInfo;
14180     },
14181
14182     /**
14183      * Gets a range template nodes.
14184      * @param {Number} startIndex
14185      * @param {Number} endIndex
14186      * @return {Array} An array of nodes
14187      */
14188     getNodes : function(start, end){
14189         var ns = this.nodes;
14190         start = start || 0;
14191         end = typeof end == "undefined" ? ns.length - 1 : end;
14192         var nodes = [];
14193         if(start <= end){
14194             for(var i = start; i <= end; i++){
14195                 nodes.push(ns[i]);
14196             }
14197         } else{
14198             for(var i = start; i >= end; i--){
14199                 nodes.push(ns[i]);
14200             }
14201         }
14202         return nodes;
14203     },
14204
14205     /**
14206      * Finds the index of the passed node
14207      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14208      * @return {Number} The index of the node or -1
14209      */
14210     indexOf : function(node){
14211         node = this.getNode(node);
14212         if(typeof node.nodeIndex == "number"){
14213             return node.nodeIndex;
14214         }
14215         var ns = this.nodes;
14216         for(var i = 0, len = ns.length; i < len; i++){
14217             if(ns[i] == node){
14218                 return i;
14219             }
14220         }
14221         return -1;
14222     }
14223 });
14224 /*
14225  * - LGPL
14226  *
14227  * based on jquery fullcalendar
14228  * 
14229  */
14230
14231 Roo.bootstrap = Roo.bootstrap || {};
14232 /**
14233  * @class Roo.bootstrap.Calendar
14234  * @extends Roo.bootstrap.Component
14235  * Bootstrap Calendar class
14236  * @cfg {Boolean} loadMask (true|false) default false
14237  * @cfg {Object} header generate the user specific header of the calendar, default false
14238
14239  * @constructor
14240  * Create a new Container
14241  * @param {Object} config The config object
14242  */
14243
14244
14245
14246 Roo.bootstrap.Calendar = function(config){
14247     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14248      this.addEvents({
14249         /**
14250              * @event select
14251              * Fires when a date is selected
14252              * @param {DatePicker} this
14253              * @param {Date} date The selected date
14254              */
14255         'select': true,
14256         /**
14257              * @event monthchange
14258              * Fires when the displayed month changes 
14259              * @param {DatePicker} this
14260              * @param {Date} date The selected month
14261              */
14262         'monthchange': true,
14263         /**
14264              * @event evententer
14265              * Fires when mouse over an event
14266              * @param {Calendar} this
14267              * @param {event} Event
14268              */
14269         'evententer': true,
14270         /**
14271              * @event eventleave
14272              * Fires when the mouse leaves an
14273              * @param {Calendar} this
14274              * @param {event}
14275              */
14276         'eventleave': true,
14277         /**
14278              * @event eventclick
14279              * Fires when the mouse click an
14280              * @param {Calendar} this
14281              * @param {event}
14282              */
14283         'eventclick': true
14284         
14285     });
14286
14287 };
14288
14289 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14290     
14291      /**
14292      * @cfg {Number} startDay
14293      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14294      */
14295     startDay : 0,
14296     
14297     loadMask : false,
14298     
14299     header : false,
14300       
14301     getAutoCreate : function(){
14302         
14303         
14304         var fc_button = function(name, corner, style, content ) {
14305             return Roo.apply({},{
14306                 tag : 'span',
14307                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14308                          (corner.length ?
14309                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14310                             ''
14311                         ),
14312                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14313                 unselectable: 'on'
14314             });
14315         };
14316         
14317         var header = {};
14318         
14319         if(!this.header){
14320             header = {
14321                 tag : 'table',
14322                 cls : 'fc-header',
14323                 style : 'width:100%',
14324                 cn : [
14325                     {
14326                         tag: 'tr',
14327                         cn : [
14328                             {
14329                                 tag : 'td',
14330                                 cls : 'fc-header-left',
14331                                 cn : [
14332                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14333                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14334                                     { tag: 'span', cls: 'fc-header-space' },
14335                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14336
14337
14338                                 ]
14339                             },
14340
14341                             {
14342                                 tag : 'td',
14343                                 cls : 'fc-header-center',
14344                                 cn : [
14345                                     {
14346                                         tag: 'span',
14347                                         cls: 'fc-header-title',
14348                                         cn : {
14349                                             tag: 'H2',
14350                                             html : 'month / year'
14351                                         }
14352                                     }
14353
14354                                 ]
14355                             },
14356                             {
14357                                 tag : 'td',
14358                                 cls : 'fc-header-right',
14359                                 cn : [
14360                               /*      fc_button('month', 'left', '', 'month' ),
14361                                     fc_button('week', '', '', 'week' ),
14362                                     fc_button('day', 'right', '', 'day' )
14363                                 */    
14364
14365                                 ]
14366                             }
14367
14368                         ]
14369                     }
14370                 ]
14371             };
14372         }
14373         
14374         header = this.header;
14375         
14376        
14377         var cal_heads = function() {
14378             var ret = [];
14379             // fixme - handle this.
14380             
14381             for (var i =0; i < Date.dayNames.length; i++) {
14382                 var d = Date.dayNames[i];
14383                 ret.push({
14384                     tag: 'th',
14385                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14386                     html : d.substring(0,3)
14387                 });
14388                 
14389             }
14390             ret[0].cls += ' fc-first';
14391             ret[6].cls += ' fc-last';
14392             return ret;
14393         };
14394         var cal_cell = function(n) {
14395             return  {
14396                 tag: 'td',
14397                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14398                 cn : [
14399                     {
14400                         cn : [
14401                             {
14402                                 cls: 'fc-day-number',
14403                                 html: 'D'
14404                             },
14405                             {
14406                                 cls: 'fc-day-content',
14407                              
14408                                 cn : [
14409                                      {
14410                                         style: 'position: relative;' // height: 17px;
14411                                     }
14412                                 ]
14413                             }
14414                             
14415                             
14416                         ]
14417                     }
14418                 ]
14419                 
14420             }
14421         };
14422         var cal_rows = function() {
14423             
14424             var ret = [];
14425             for (var r = 0; r < 6; r++) {
14426                 var row= {
14427                     tag : 'tr',
14428                     cls : 'fc-week',
14429                     cn : []
14430                 };
14431                 
14432                 for (var i =0; i < Date.dayNames.length; i++) {
14433                     var d = Date.dayNames[i];
14434                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14435
14436                 }
14437                 row.cn[0].cls+=' fc-first';
14438                 row.cn[0].cn[0].style = 'min-height:90px';
14439                 row.cn[6].cls+=' fc-last';
14440                 ret.push(row);
14441                 
14442             }
14443             ret[0].cls += ' fc-first';
14444             ret[4].cls += ' fc-prev-last';
14445             ret[5].cls += ' fc-last';
14446             return ret;
14447             
14448         };
14449         
14450         var cal_table = {
14451             tag: 'table',
14452             cls: 'fc-border-separate',
14453             style : 'width:100%',
14454             cellspacing  : 0,
14455             cn : [
14456                 { 
14457                     tag: 'thead',
14458                     cn : [
14459                         { 
14460                             tag: 'tr',
14461                             cls : 'fc-first fc-last',
14462                             cn : cal_heads()
14463                         }
14464                     ]
14465                 },
14466                 { 
14467                     tag: 'tbody',
14468                     cn : cal_rows()
14469                 }
14470                   
14471             ]
14472         };
14473          
14474          var cfg = {
14475             cls : 'fc fc-ltr',
14476             cn : [
14477                 header,
14478                 {
14479                     cls : 'fc-content',
14480                     style : "position: relative;",
14481                     cn : [
14482                         {
14483                             cls : 'fc-view fc-view-month fc-grid',
14484                             style : 'position: relative',
14485                             unselectable : 'on',
14486                             cn : [
14487                                 {
14488                                     cls : 'fc-event-container',
14489                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14490                                 },
14491                                 cal_table
14492                             ]
14493                         }
14494                     ]
14495     
14496                 }
14497            ] 
14498             
14499         };
14500         
14501          
14502         
14503         return cfg;
14504     },
14505     
14506     
14507     initEvents : function()
14508     {
14509         if(!this.store){
14510             throw "can not find store for calendar";
14511         }
14512         
14513         var mark = {
14514             tag: "div",
14515             cls:"x-dlg-mask",
14516             style: "text-align:center",
14517             cn: [
14518                 {
14519                     tag: "div",
14520                     style: "background-color:white;width:50%;margin:250 auto",
14521                     cn: [
14522                         {
14523                             tag: "img",
14524                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14525                         },
14526                         {
14527                             tag: "span",
14528                             html: "Loading"
14529                         }
14530                         
14531                     ]
14532                 }
14533             ]
14534         }
14535         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14536         
14537         var size = this.el.select('.fc-content', true).first().getSize();
14538         this.maskEl.setSize(size.width, size.height);
14539         this.maskEl.enableDisplayMode("block");
14540         if(!this.loadMask){
14541             this.maskEl.hide();
14542         }
14543         
14544         this.store = Roo.factory(this.store, Roo.data);
14545         this.store.on('load', this.onLoad, this);
14546         this.store.on('beforeload', this.onBeforeLoad, this);
14547         
14548         this.resize();
14549         
14550         this.cells = this.el.select('.fc-day',true);
14551         //Roo.log(this.cells);
14552         this.textNodes = this.el.query('.fc-day-number');
14553         this.cells.addClassOnOver('fc-state-hover');
14554         
14555         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14556         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14557         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14558         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14559         
14560         this.on('monthchange', this.onMonthChange, this);
14561         
14562         this.update(new Date().clearTime());
14563     },
14564     
14565     resize : function() {
14566         var sz  = this.el.getSize();
14567         
14568         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14569         this.el.select('.fc-day-content div',true).setHeight(34);
14570     },
14571     
14572     
14573     // private
14574     showPrevMonth : function(e){
14575         this.update(this.activeDate.add("mo", -1));
14576     },
14577     showToday : function(e){
14578         this.update(new Date().clearTime());
14579     },
14580     // private
14581     showNextMonth : function(e){
14582         this.update(this.activeDate.add("mo", 1));
14583     },
14584
14585     // private
14586     showPrevYear : function(){
14587         this.update(this.activeDate.add("y", -1));
14588     },
14589
14590     // private
14591     showNextYear : function(){
14592         this.update(this.activeDate.add("y", 1));
14593     },
14594
14595     
14596    // private
14597     update : function(date)
14598     {
14599         var vd = this.activeDate;
14600         this.activeDate = date;
14601 //        if(vd && this.el){
14602 //            var t = date.getTime();
14603 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14604 //                Roo.log('using add remove');
14605 //                
14606 //                this.fireEvent('monthchange', this, date);
14607 //                
14608 //                this.cells.removeClass("fc-state-highlight");
14609 //                this.cells.each(function(c){
14610 //                   if(c.dateValue == t){
14611 //                       c.addClass("fc-state-highlight");
14612 //                       setTimeout(function(){
14613 //                            try{c.dom.firstChild.focus();}catch(e){}
14614 //                       }, 50);
14615 //                       return false;
14616 //                   }
14617 //                   return true;
14618 //                });
14619 //                return;
14620 //            }
14621 //        }
14622         
14623         var days = date.getDaysInMonth();
14624         
14625         var firstOfMonth = date.getFirstDateOfMonth();
14626         var startingPos = firstOfMonth.getDay()-this.startDay;
14627         
14628         if(startingPos < this.startDay){
14629             startingPos += 7;
14630         }
14631         
14632         var pm = date.add(Date.MONTH, -1);
14633         var prevStart = pm.getDaysInMonth()-startingPos;
14634 //        
14635         this.cells = this.el.select('.fc-day',true);
14636         this.textNodes = this.el.query('.fc-day-number');
14637         this.cells.addClassOnOver('fc-state-hover');
14638         
14639         var cells = this.cells.elements;
14640         var textEls = this.textNodes;
14641         
14642         Roo.each(cells, function(cell){
14643             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14644         });
14645         
14646         days += startingPos;
14647
14648         // convert everything to numbers so it's fast
14649         var day = 86400000;
14650         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14651         //Roo.log(d);
14652         //Roo.log(pm);
14653         //Roo.log(prevStart);
14654         
14655         var today = new Date().clearTime().getTime();
14656         var sel = date.clearTime().getTime();
14657         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14658         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14659         var ddMatch = this.disabledDatesRE;
14660         var ddText = this.disabledDatesText;
14661         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14662         var ddaysText = this.disabledDaysText;
14663         var format = this.format;
14664         
14665         var setCellClass = function(cal, cell){
14666             cell.row = 0;
14667             cell.events = [];
14668             cell.more = [];
14669             //Roo.log('set Cell Class');
14670             cell.title = "";
14671             var t = d.getTime();
14672             
14673             //Roo.log(d);
14674             
14675             cell.dateValue = t;
14676             if(t == today){
14677                 cell.className += " fc-today";
14678                 cell.className += " fc-state-highlight";
14679                 cell.title = cal.todayText;
14680             }
14681             if(t == sel){
14682                 // disable highlight in other month..
14683                 //cell.className += " fc-state-highlight";
14684                 
14685             }
14686             // disabling
14687             if(t < min) {
14688                 cell.className = " fc-state-disabled";
14689                 cell.title = cal.minText;
14690                 return;
14691             }
14692             if(t > max) {
14693                 cell.className = " fc-state-disabled";
14694                 cell.title = cal.maxText;
14695                 return;
14696             }
14697             if(ddays){
14698                 if(ddays.indexOf(d.getDay()) != -1){
14699                     cell.title = ddaysText;
14700                     cell.className = " fc-state-disabled";
14701                 }
14702             }
14703             if(ddMatch && format){
14704                 var fvalue = d.dateFormat(format);
14705                 if(ddMatch.test(fvalue)){
14706                     cell.title = ddText.replace("%0", fvalue);
14707                     cell.className = " fc-state-disabled";
14708                 }
14709             }
14710             
14711             if (!cell.initialClassName) {
14712                 cell.initialClassName = cell.dom.className;
14713             }
14714             
14715             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14716         };
14717
14718         var i = 0;
14719         
14720         for(; i < startingPos; i++) {
14721             textEls[i].innerHTML = (++prevStart);
14722             d.setDate(d.getDate()+1);
14723             
14724             cells[i].className = "fc-past fc-other-month";
14725             setCellClass(this, cells[i]);
14726         }
14727         
14728         var intDay = 0;
14729         
14730         for(; i < days; i++){
14731             intDay = i - startingPos + 1;
14732             textEls[i].innerHTML = (intDay);
14733             d.setDate(d.getDate()+1);
14734             
14735             cells[i].className = ''; // "x-date-active";
14736             setCellClass(this, cells[i]);
14737         }
14738         var extraDays = 0;
14739         
14740         for(; i < 42; i++) {
14741             textEls[i].innerHTML = (++extraDays);
14742             d.setDate(d.getDate()+1);
14743             
14744             cells[i].className = "fc-future fc-other-month";
14745             setCellClass(this, cells[i]);
14746         }
14747         
14748         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14749         
14750         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14751         
14752         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14753         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14754         
14755         if(totalRows != 6){
14756             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14757             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14758         }
14759         
14760         this.fireEvent('monthchange', this, date);
14761         
14762         
14763         /*
14764         if(!this.internalRender){
14765             var main = this.el.dom.firstChild;
14766             var w = main.offsetWidth;
14767             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14768             Roo.fly(main).setWidth(w);
14769             this.internalRender = true;
14770             // opera does not respect the auto grow header center column
14771             // then, after it gets a width opera refuses to recalculate
14772             // without a second pass
14773             if(Roo.isOpera && !this.secondPass){
14774                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14775                 this.secondPass = true;
14776                 this.update.defer(10, this, [date]);
14777             }
14778         }
14779         */
14780         
14781     },
14782     
14783     findCell : function(dt) {
14784         dt = dt.clearTime().getTime();
14785         var ret = false;
14786         this.cells.each(function(c){
14787             //Roo.log("check " +c.dateValue + '?=' + dt);
14788             if(c.dateValue == dt){
14789                 ret = c;
14790                 return false;
14791             }
14792             return true;
14793         });
14794         
14795         return ret;
14796     },
14797     
14798     findCells : function(ev) {
14799         var s = ev.start.clone().clearTime().getTime();
14800        // Roo.log(s);
14801         var e= ev.end.clone().clearTime().getTime();
14802        // Roo.log(e);
14803         var ret = [];
14804         this.cells.each(function(c){
14805              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14806             
14807             if(c.dateValue > e){
14808                 return ;
14809             }
14810             if(c.dateValue < s){
14811                 return ;
14812             }
14813             ret.push(c);
14814         });
14815         
14816         return ret;    
14817     },
14818     
14819 //    findBestRow: function(cells)
14820 //    {
14821 //        var ret = 0;
14822 //        
14823 //        for (var i =0 ; i < cells.length;i++) {
14824 //            ret  = Math.max(cells[i].rows || 0,ret);
14825 //        }
14826 //        return ret;
14827 //        
14828 //    },
14829     
14830     
14831     addItem : function(ev)
14832     {
14833         // look for vertical location slot in
14834         var cells = this.findCells(ev);
14835         
14836 //        ev.row = this.findBestRow(cells);
14837         
14838         // work out the location.
14839         
14840         var crow = false;
14841         var rows = [];
14842         for(var i =0; i < cells.length; i++) {
14843             
14844             cells[i].row = cells[0].row;
14845             
14846             if(i == 0){
14847                 cells[i].row = cells[i].row + 1;
14848             }
14849             
14850             if (!crow) {
14851                 crow = {
14852                     start : cells[i],
14853                     end :  cells[i]
14854                 };
14855                 continue;
14856             }
14857             if (crow.start.getY() == cells[i].getY()) {
14858                 // on same row.
14859                 crow.end = cells[i];
14860                 continue;
14861             }
14862             // different row.
14863             rows.push(crow);
14864             crow = {
14865                 start: cells[i],
14866                 end : cells[i]
14867             };
14868             
14869         }
14870         
14871         rows.push(crow);
14872         ev.els = [];
14873         ev.rows = rows;
14874         ev.cells = cells;
14875         
14876         cells[0].events.push(ev);
14877         
14878         this.calevents.push(ev);
14879     },
14880     
14881     clearEvents: function() {
14882         
14883         if(!this.calevents){
14884             return;
14885         }
14886         
14887         Roo.each(this.cells.elements, function(c){
14888             c.row = 0;
14889             c.events = [];
14890             c.more = [];
14891         });
14892         
14893         Roo.each(this.calevents, function(e) {
14894             Roo.each(e.els, function(el) {
14895                 el.un('mouseenter' ,this.onEventEnter, this);
14896                 el.un('mouseleave' ,this.onEventLeave, this);
14897                 el.remove();
14898             },this);
14899         },this);
14900         
14901         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14902             e.remove();
14903         });
14904         
14905     },
14906     
14907     renderEvents: function()
14908     {   
14909         var _this = this;
14910         
14911         this.cells.each(function(c) {
14912             
14913             if(c.row < 5){
14914                 return;
14915             }
14916             
14917             var ev = c.events;
14918             
14919             var r = 4;
14920             if(c.row != c.events.length){
14921                 r = 4 - (4 - (c.row - c.events.length));
14922             }
14923             
14924             c.events = ev.slice(0, r);
14925             c.more = ev.slice(r);
14926             
14927             if(c.more.length && c.more.length == 1){
14928                 c.events.push(c.more.pop());
14929             }
14930             
14931             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14932             
14933         });
14934             
14935         this.cells.each(function(c) {
14936             
14937             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14938             
14939             
14940             for (var e = 0; e < c.events.length; e++){
14941                 var ev = c.events[e];
14942                 var rows = ev.rows;
14943                 
14944                 for(var i = 0; i < rows.length; i++) {
14945                 
14946                     // how many rows should it span..
14947
14948                     var  cfg = {
14949                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14950                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14951
14952                         unselectable : "on",
14953                         cn : [
14954                             {
14955                                 cls: 'fc-event-inner',
14956                                 cn : [
14957     //                                {
14958     //                                  tag:'span',
14959     //                                  cls: 'fc-event-time',
14960     //                                  html : cells.length > 1 ? '' : ev.time
14961     //                                },
14962                                     {
14963                                       tag:'span',
14964                                       cls: 'fc-event-title',
14965                                       html : String.format('{0}', ev.title)
14966                                     }
14967
14968
14969                                 ]
14970                             },
14971                             {
14972                                 cls: 'ui-resizable-handle ui-resizable-e',
14973                                 html : '&nbsp;&nbsp;&nbsp'
14974                             }
14975
14976                         ]
14977                     };
14978
14979                     if (i == 0) {
14980                         cfg.cls += ' fc-event-start';
14981                     }
14982                     if ((i+1) == rows.length) {
14983                         cfg.cls += ' fc-event-end';
14984                     }
14985
14986                     var ctr = _this.el.select('.fc-event-container',true).first();
14987                     var cg = ctr.createChild(cfg);
14988
14989                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14990                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14991
14992                     var r = (c.more.length) ? 1 : 0;
14993                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14994                     cg.setWidth(ebox.right - sbox.x -2);
14995
14996                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14997                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14998                     cg.on('click', _this.onEventClick, _this, ev);
14999
15000                     ev.els.push(cg);
15001                     
15002                 }
15003                 
15004             }
15005             
15006             
15007             if(c.more.length){
15008                 var  cfg = {
15009                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15010                     style : 'position: absolute',
15011                     unselectable : "on",
15012                     cn : [
15013                         {
15014                             cls: 'fc-event-inner',
15015                             cn : [
15016                                 {
15017                                   tag:'span',
15018                                   cls: 'fc-event-title',
15019                                   html : 'More'
15020                                 }
15021
15022
15023                             ]
15024                         },
15025                         {
15026                             cls: 'ui-resizable-handle ui-resizable-e',
15027                             html : '&nbsp;&nbsp;&nbsp'
15028                         }
15029
15030                     ]
15031                 };
15032
15033                 var ctr = _this.el.select('.fc-event-container',true).first();
15034                 var cg = ctr.createChild(cfg);
15035
15036                 var sbox = c.select('.fc-day-content',true).first().getBox();
15037                 var ebox = c.select('.fc-day-content',true).first().getBox();
15038                 //Roo.log(cg);
15039                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15040                 cg.setWidth(ebox.right - sbox.x -2);
15041
15042                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15043                 
15044             }
15045             
15046         });
15047         
15048         
15049         
15050     },
15051     
15052     onEventEnter: function (e, el,event,d) {
15053         this.fireEvent('evententer', this, el, event);
15054     },
15055     
15056     onEventLeave: function (e, el,event,d) {
15057         this.fireEvent('eventleave', this, el, event);
15058     },
15059     
15060     onEventClick: function (e, el,event,d) {
15061         this.fireEvent('eventclick', this, el, event);
15062     },
15063     
15064     onMonthChange: function () {
15065         this.store.load();
15066     },
15067     
15068     onMoreEventClick: function(e, el, more)
15069     {
15070         var _this = this;
15071         
15072         this.calpopover.placement = 'right';
15073         this.calpopover.setTitle('More');
15074         
15075         this.calpopover.setContent('');
15076         
15077         var ctr = this.calpopover.el.select('.popover-content', true).first();
15078         
15079         Roo.each(more, function(m){
15080             var cfg = {
15081                 cls : 'fc-event-hori fc-event-draggable',
15082                 html : m.title
15083             }
15084             var cg = ctr.createChild(cfg);
15085             
15086             cg.on('click', _this.onEventClick, _this, m);
15087         });
15088         
15089         this.calpopover.show(el);
15090         
15091         
15092     },
15093     
15094     onLoad: function () 
15095     {   
15096         this.calevents = [];
15097         var cal = this;
15098         
15099         if(this.store.getCount() > 0){
15100             this.store.data.each(function(d){
15101                cal.addItem({
15102                     id : d.data.id,
15103                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15104                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15105                     time : d.data.start_time,
15106                     title : d.data.title,
15107                     description : d.data.description,
15108                     venue : d.data.venue
15109                 });
15110             });
15111         }
15112         
15113         this.renderEvents();
15114         
15115         if(this.calevents.length && this.loadMask){
15116             this.maskEl.hide();
15117         }
15118     },
15119     
15120     onBeforeLoad: function()
15121     {
15122         this.clearEvents();
15123         if(this.loadMask){
15124             this.maskEl.show();
15125         }
15126     }
15127 });
15128
15129  
15130  /*
15131  * - LGPL
15132  *
15133  * element
15134  * 
15135  */
15136
15137 /**
15138  * @class Roo.bootstrap.Popover
15139  * @extends Roo.bootstrap.Component
15140  * Bootstrap Popover class
15141  * @cfg {String} html contents of the popover   (or false to use children..)
15142  * @cfg {String} title of popover (or false to hide)
15143  * @cfg {String} placement how it is placed
15144  * @cfg {String} trigger click || hover (or false to trigger manually)
15145  * @cfg {String} over what (parent or false to trigger manually.)
15146  * @cfg {Number} delay - delay before showing
15147  
15148  * @constructor
15149  * Create a new Popover
15150  * @param {Object} config The config object
15151  */
15152
15153 Roo.bootstrap.Popover = function(config){
15154     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15155 };
15156
15157 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15158     
15159     title: 'Fill in a title',
15160     html: false,
15161     
15162     placement : 'right',
15163     trigger : 'hover', // hover
15164     
15165     delay : 0,
15166     
15167     over: 'parent',
15168     
15169     can_build_overlaid : false,
15170     
15171     getChildContainer : function()
15172     {
15173         return this.el.select('.popover-content',true).first();
15174     },
15175     
15176     getAutoCreate : function(){
15177          Roo.log('make popover?');
15178         var cfg = {
15179            cls : 'popover roo-dynamic',
15180            style: 'display:block',
15181            cn : [
15182                 {
15183                     cls : 'arrow'
15184                 },
15185                 {
15186                     cls : 'popover-inner',
15187                     cn : [
15188                         {
15189                             tag: 'h3',
15190                             cls: 'popover-title',
15191                             html : this.title
15192                         },
15193                         {
15194                             cls : 'popover-content',
15195                             html : this.html
15196                         }
15197                     ]
15198                     
15199                 }
15200            ]
15201         };
15202         
15203         return cfg;
15204     },
15205     setTitle: function(str)
15206     {
15207         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15208     },
15209     setContent: function(str)
15210     {
15211         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15212     },
15213     // as it get's added to the bottom of the page.
15214     onRender : function(ct, position)
15215     {
15216         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15217         if(!this.el){
15218             var cfg = Roo.apply({},  this.getAutoCreate());
15219             cfg.id = Roo.id();
15220             
15221             if (this.cls) {
15222                 cfg.cls += ' ' + this.cls;
15223             }
15224             if (this.style) {
15225                 cfg.style = this.style;
15226             }
15227             Roo.log("adding to ")
15228             this.el = Roo.get(document.body).createChild(cfg, position);
15229             Roo.log(this.el);
15230         }
15231         this.initEvents();
15232     },
15233     
15234     initEvents : function()
15235     {
15236         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15237         this.el.enableDisplayMode('block');
15238         this.el.hide();
15239         if (this.over === false) {
15240             return; 
15241         }
15242         if (this.triggers === false) {
15243             return;
15244         }
15245         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15246         var triggers = this.trigger ? this.trigger.split(' ') : [];
15247         Roo.each(triggers, function(trigger) {
15248         
15249             if (trigger == 'click') {
15250                 on_el.on('click', this.toggle, this);
15251             } else if (trigger != 'manual') {
15252                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15253                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15254       
15255                 on_el.on(eventIn  ,this.enter, this);
15256                 on_el.on(eventOut, this.leave, this);
15257             }
15258         }, this);
15259         
15260     },
15261     
15262     
15263     // private
15264     timeout : null,
15265     hoverState : null,
15266     
15267     toggle : function () {
15268         this.hoverState == 'in' ? this.leave() : this.enter();
15269     },
15270     
15271     enter : function () {
15272        
15273     
15274         clearTimeout(this.timeout);
15275     
15276         this.hoverState = 'in';
15277     
15278         if (!this.delay || !this.delay.show) {
15279             this.show();
15280             return;
15281         }
15282         var _t = this;
15283         this.timeout = setTimeout(function () {
15284             if (_t.hoverState == 'in') {
15285                 _t.show();
15286             }
15287         }, this.delay.show)
15288     },
15289     leave : function() {
15290         clearTimeout(this.timeout);
15291     
15292         this.hoverState = 'out';
15293     
15294         if (!this.delay || !this.delay.hide) {
15295             this.hide();
15296             return;
15297         }
15298         var _t = this;
15299         this.timeout = setTimeout(function () {
15300             if (_t.hoverState == 'out') {
15301                 _t.hide();
15302             }
15303         }, this.delay.hide)
15304     },
15305     
15306     show : function (on_el)
15307     {
15308         if (!on_el) {
15309             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15310         }
15311         // set content.
15312         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15313         if (this.html !== false) {
15314             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
15315         }
15316         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15317         if (!this.title.length) {
15318             this.el.select('.popover-title',true).hide();
15319         }
15320         
15321         var placement = typeof this.placement == 'function' ?
15322             this.placement.call(this, this.el, on_el) :
15323             this.placement;
15324             
15325         var autoToken = /\s?auto?\s?/i;
15326         var autoPlace = autoToken.test(placement);
15327         if (autoPlace) {
15328             placement = placement.replace(autoToken, '') || 'top';
15329         }
15330         
15331         //this.el.detach()
15332         //this.el.setXY([0,0]);
15333         this.el.show();
15334         this.el.dom.style.display='block';
15335         this.el.addClass(placement);
15336         
15337         //this.el.appendTo(on_el);
15338         
15339         var p = this.getPosition();
15340         var box = this.el.getBox();
15341         
15342         if (autoPlace) {
15343             // fixme..
15344         }
15345         var align = Roo.bootstrap.Popover.alignment[placement];
15346         this.el.alignTo(on_el, align[0],align[1]);
15347         //var arrow = this.el.select('.arrow',true).first();
15348         //arrow.set(align[2], 
15349         
15350         this.el.addClass('in');
15351         this.hoverState = null;
15352         
15353         if (this.el.hasClass('fade')) {
15354             // fade it?
15355         }
15356         
15357     },
15358     hide : function()
15359     {
15360         this.el.setXY([0,0]);
15361         this.el.removeClass('in');
15362         this.el.hide();
15363         
15364     }
15365     
15366 });
15367
15368 Roo.bootstrap.Popover.alignment = {
15369     'left' : ['r-l', [-10,0], 'right'],
15370     'right' : ['l-r', [10,0], 'left'],
15371     'bottom' : ['t-b', [0,10], 'top'],
15372     'top' : [ 'b-t', [0,-10], 'bottom']
15373 };
15374
15375  /*
15376  * - LGPL
15377  *
15378  * Progress
15379  * 
15380  */
15381
15382 /**
15383  * @class Roo.bootstrap.Progress
15384  * @extends Roo.bootstrap.Component
15385  * Bootstrap Progress class
15386  * @cfg {Boolean} striped striped of the progress bar
15387  * @cfg {Boolean} active animated of the progress bar
15388  * 
15389  * 
15390  * @constructor
15391  * Create a new Progress
15392  * @param {Object} config The config object
15393  */
15394
15395 Roo.bootstrap.Progress = function(config){
15396     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15397 };
15398
15399 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15400     
15401     striped : false,
15402     active: false,
15403     
15404     getAutoCreate : function(){
15405         var cfg = {
15406             tag: 'div',
15407             cls: 'progress'
15408         };
15409         
15410         
15411         if(this.striped){
15412             cfg.cls += ' progress-striped';
15413         }
15414       
15415         if(this.active){
15416             cfg.cls += ' active';
15417         }
15418         
15419         
15420         return cfg;
15421     }
15422    
15423 });
15424
15425  
15426
15427  /*
15428  * - LGPL
15429  *
15430  * ProgressBar
15431  * 
15432  */
15433
15434 /**
15435  * @class Roo.bootstrap.ProgressBar
15436  * @extends Roo.bootstrap.Component
15437  * Bootstrap ProgressBar class
15438  * @cfg {Number} aria_valuenow aria-value now
15439  * @cfg {Number} aria_valuemin aria-value min
15440  * @cfg {Number} aria_valuemax aria-value max
15441  * @cfg {String} label label for the progress bar
15442  * @cfg {String} panel (success | info | warning | danger )
15443  * @cfg {String} role role of the progress bar
15444  * @cfg {String} sr_only text
15445  * 
15446  * 
15447  * @constructor
15448  * Create a new ProgressBar
15449  * @param {Object} config The config object
15450  */
15451
15452 Roo.bootstrap.ProgressBar = function(config){
15453     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15454 };
15455
15456 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15457     
15458     aria_valuenow : 0,
15459     aria_valuemin : 0,
15460     aria_valuemax : 100,
15461     label : false,
15462     panel : false,
15463     role : false,
15464     sr_only: false,
15465     
15466     getAutoCreate : function()
15467     {
15468         
15469         var cfg = {
15470             tag: 'div',
15471             cls: 'progress-bar',
15472             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15473         };
15474         
15475         if(this.sr_only){
15476             cfg.cn = {
15477                 tag: 'span',
15478                 cls: 'sr-only',
15479                 html: this.sr_only
15480             }
15481         }
15482         
15483         if(this.role){
15484             cfg.role = this.role;
15485         }
15486         
15487         if(this.aria_valuenow){
15488             cfg['aria-valuenow'] = this.aria_valuenow;
15489         }
15490         
15491         if(this.aria_valuemin){
15492             cfg['aria-valuemin'] = this.aria_valuemin;
15493         }
15494         
15495         if(this.aria_valuemax){
15496             cfg['aria-valuemax'] = this.aria_valuemax;
15497         }
15498         
15499         if(this.label && !this.sr_only){
15500             cfg.html = this.label;
15501         }
15502         
15503         if(this.panel){
15504             cfg.cls += ' progress-bar-' + this.panel;
15505         }
15506         
15507         return cfg;
15508     },
15509     
15510     update : function(aria_valuenow)
15511     {
15512         this.aria_valuenow = aria_valuenow;
15513         
15514         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15515     }
15516    
15517 });
15518
15519  
15520
15521  /*
15522  * - LGPL
15523  *
15524  * column
15525  * 
15526  */
15527
15528 /**
15529  * @class Roo.bootstrap.TabGroup
15530  * @extends Roo.bootstrap.Column
15531  * Bootstrap Column class
15532  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15533  * @cfg {Boolean} carousel true to make the group behave like a carousel
15534  * @cfg {Number} bullets show the panel pointer.. default 0
15535  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15536  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15537  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15538  * 
15539  * @constructor
15540  * Create a new TabGroup
15541  * @param {Object} config The config object
15542  */
15543
15544 Roo.bootstrap.TabGroup = function(config){
15545     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15546     if (!this.navId) {
15547         this.navId = Roo.id();
15548     }
15549     this.tabs = [];
15550     Roo.bootstrap.TabGroup.register(this);
15551     
15552 };
15553
15554 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15555     
15556     carousel : false,
15557     transition : false,
15558     bullets : 0,
15559     timer : 0,
15560     autoslide : false,
15561     slideFn : false,
15562     slideOnTouch : false,
15563     
15564     getAutoCreate : function()
15565     {
15566         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15567         
15568         cfg.cls += ' tab-content';
15569         
15570         Roo.log('get auto create...............');
15571         
15572         if (this.carousel) {
15573             cfg.cls += ' carousel slide';
15574             
15575             cfg.cn = [{
15576                cls : 'carousel-inner'
15577             }];
15578         
15579             if(this.bullets > 0 && !Roo.isTouch){
15580                 
15581                 var bullets = {
15582                     cls : 'carousel-bullets',
15583                     cn : []
15584                 };
15585                 
15586                 if(this.bullets_cls){
15587                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15588                 }
15589                 
15590                 for (var i = 0; i < this.bullets; i++){
15591                     bullets.cn.push({
15592                         cls : 'bullet bullet-' + i
15593                     });
15594                 }
15595                 
15596                 bullets.cn.push({
15597                     cls : 'clear'
15598                 });
15599                 
15600                 cfg.cn[0].cn = bullets;
15601             }
15602         }
15603         
15604         return cfg;
15605     },
15606     
15607     initEvents:  function()
15608     {
15609         Roo.log('-------- init events on tab group ---------');
15610         
15611         if(this.bullets > 0 && !Roo.isTouch){
15612             this.initBullet();
15613         }
15614         
15615         Roo.log(this);
15616         
15617         if(Roo.isTouch && this.slideOnTouch){
15618             this.el.on("touchstart", this.onTouchStart, this);
15619         }
15620         
15621         if(this.autoslide){
15622             var _this = this;
15623             
15624             this.slideFn = window.setInterval(function() {
15625                 _this.showPanelNext();
15626             }, this.timer);
15627         }
15628         
15629     },
15630     
15631     onTouchStart : function(e, el, o)
15632     {
15633         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15634             return;
15635         }
15636         
15637         this.showPanelNext();
15638     },
15639     
15640     getChildContainer : function()
15641     {
15642         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15643     },
15644     
15645     /**
15646     * register a Navigation item
15647     * @param {Roo.bootstrap.NavItem} the navitem to add
15648     */
15649     register : function(item)
15650     {
15651         this.tabs.push( item);
15652         item.navId = this.navId; // not really needed..
15653     
15654     },
15655     
15656     getActivePanel : function()
15657     {
15658         var r = false;
15659         Roo.each(this.tabs, function(t) {
15660             if (t.active) {
15661                 r = t;
15662                 return false;
15663             }
15664             return null;
15665         });
15666         return r;
15667         
15668     },
15669     getPanelByName : function(n)
15670     {
15671         var r = false;
15672         Roo.each(this.tabs, function(t) {
15673             if (t.tabId == n) {
15674                 r = t;
15675                 return false;
15676             }
15677             return null;
15678         });
15679         return r;
15680     },
15681     indexOfPanel : function(p)
15682     {
15683         var r = false;
15684         Roo.each(this.tabs, function(t,i) {
15685             if (t.tabId == p.tabId) {
15686                 r = i;
15687                 return false;
15688             }
15689             return null;
15690         });
15691         return r;
15692     },
15693     /**
15694      * show a specific panel
15695      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15696      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15697      */
15698     showPanel : function (pan)
15699     {
15700         if(this.transition){
15701             Roo.log("waiting for the transitionend");
15702             return;
15703         }
15704         
15705         if (typeof(pan) == 'number') {
15706             pan = this.tabs[pan];
15707         }
15708         if (typeof(pan) == 'string') {
15709             pan = this.getPanelByName(pan);
15710         }
15711         if (pan.tabId == this.getActivePanel().tabId) {
15712             return true;
15713         }
15714         var cur = this.getActivePanel();
15715         
15716         if (false === cur.fireEvent('beforedeactivate')) {
15717             return false;
15718         }
15719         
15720         if(this.bullets > 0 && !Roo.isTouch){
15721             this.setActiveBullet(this.indexOfPanel(pan));
15722         }
15723         
15724         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15725             
15726             this.transition = true;
15727             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15728             var lr = dir == 'next' ? 'left' : 'right';
15729             pan.el.addClass(dir); // or prev
15730             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15731             cur.el.addClass(lr); // or right
15732             pan.el.addClass(lr);
15733             
15734             var _this = this;
15735             cur.el.on('transitionend', function() {
15736                 Roo.log("trans end?");
15737                 
15738                 pan.el.removeClass([lr,dir]);
15739                 pan.setActive(true);
15740                 
15741                 cur.el.removeClass([lr]);
15742                 cur.setActive(false);
15743                 
15744                 _this.transition = false;
15745                 
15746             }, this, { single:  true } );
15747             
15748             return true;
15749         }
15750         
15751         cur.setActive(false);
15752         pan.setActive(true);
15753         
15754         return true;
15755         
15756     },
15757     showPanelNext : function()
15758     {
15759         var i = this.indexOfPanel(this.getActivePanel());
15760         
15761         if (i >= this.tabs.length - 1 && !this.autoslide) {
15762             return;
15763         }
15764         
15765         if (i >= this.tabs.length - 1 && this.autoslide) {
15766             i = -1;
15767         }
15768         
15769         this.showPanel(this.tabs[i+1]);
15770     },
15771     
15772     showPanelPrev : function()
15773     {
15774         var i = this.indexOfPanel(this.getActivePanel());
15775         
15776         if (i  < 1 && !this.autoslide) {
15777             return;
15778         }
15779         
15780         if (i < 1 && this.autoslide) {
15781             i = this.tabs.length;
15782         }
15783         
15784         this.showPanel(this.tabs[i-1]);
15785     },
15786     
15787     initBullet : function()
15788     {
15789         if(Roo.isTouch){
15790             return;
15791         }
15792         
15793         var _this = this;
15794         
15795         for (var i = 0; i < this.bullets; i++){
15796             var bullet = this.el.select('.bullet-' + i, true).first();
15797
15798             if(!bullet){
15799                 continue;
15800             }
15801
15802             bullet.on('click', (function(e, el, o, ii, t){
15803
15804                 e.preventDefault();
15805
15806                 _this.showPanel(ii);
15807
15808                 if(_this.autoslide && _this.slideFn){
15809                     clearInterval(_this.slideFn);
15810                     _this.slideFn = window.setInterval(function() {
15811                         _this.showPanelNext();
15812                     }, _this.timer);
15813                 }
15814
15815             }).createDelegate(this, [i, bullet], true));
15816         }
15817     },
15818     
15819     setActiveBullet : function(i)
15820     {
15821         if(Roo.isTouch){
15822             return;
15823         }
15824         
15825         Roo.each(this.el.select('.bullet', true).elements, function(el){
15826             el.removeClass('selected');
15827         });
15828
15829         var bullet = this.el.select('.bullet-' + i, true).first();
15830         
15831         if(!bullet){
15832             return;
15833         }
15834         
15835         bullet.addClass('selected');
15836     }
15837     
15838     
15839   
15840 });
15841
15842  
15843
15844  
15845  
15846 Roo.apply(Roo.bootstrap.TabGroup, {
15847     
15848     groups: {},
15849      /**
15850     * register a Navigation Group
15851     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15852     */
15853     register : function(navgrp)
15854     {
15855         this.groups[navgrp.navId] = navgrp;
15856         
15857     },
15858     /**
15859     * fetch a Navigation Group based on the navigation ID
15860     * if one does not exist , it will get created.
15861     * @param {string} the navgroup to add
15862     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15863     */
15864     get: function(navId) {
15865         if (typeof(this.groups[navId]) == 'undefined') {
15866             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15867         }
15868         return this.groups[navId] ;
15869     }
15870     
15871     
15872     
15873 });
15874
15875  /*
15876  * - LGPL
15877  *
15878  * TabPanel
15879  * 
15880  */
15881
15882 /**
15883  * @class Roo.bootstrap.TabPanel
15884  * @extends Roo.bootstrap.Component
15885  * Bootstrap TabPanel class
15886  * @cfg {Boolean} active panel active
15887  * @cfg {String} html panel content
15888  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15889  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15890  * 
15891  * 
15892  * @constructor
15893  * Create a new TabPanel
15894  * @param {Object} config The config object
15895  */
15896
15897 Roo.bootstrap.TabPanel = function(config){
15898     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15899     this.addEvents({
15900         /**
15901              * @event changed
15902              * Fires when the active status changes
15903              * @param {Roo.bootstrap.TabPanel} this
15904              * @param {Boolean} state the new state
15905             
15906          */
15907         'changed': true,
15908         /**
15909              * @event beforedeactivate
15910              * Fires before a tab is de-activated - can be used to do validation on a form.
15911              * @param {Roo.bootstrap.TabPanel} this
15912              * @return {Boolean} false if there is an error
15913             
15914          */
15915         'beforedeactivate': true
15916      });
15917     
15918     this.tabId = this.tabId || Roo.id();
15919   
15920 };
15921
15922 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15923     
15924     active: false,
15925     html: false,
15926     tabId: false,
15927     navId : false,
15928     
15929     getAutoCreate : function(){
15930         var cfg = {
15931             tag: 'div',
15932             // item is needed for carousel - not sure if it has any effect otherwise
15933             cls: 'tab-pane item',
15934             html: this.html || ''
15935         };
15936         
15937         if(this.active){
15938             cfg.cls += ' active';
15939         }
15940         
15941         if(this.tabId){
15942             cfg.tabId = this.tabId;
15943         }
15944         
15945         
15946         return cfg;
15947     },
15948     
15949     initEvents:  function()
15950     {
15951         Roo.log('-------- init events on tab panel ---------');
15952         
15953         var p = this.parent();
15954         this.navId = this.navId || p.navId;
15955         
15956         if (typeof(this.navId) != 'undefined') {
15957             // not really needed.. but just in case.. parent should be a NavGroup.
15958             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15959             Roo.log(['register', tg, this]);
15960             tg.register(this);
15961             
15962             var i = tg.tabs.length - 1;
15963             
15964             if(this.active && tg.bullets > 0 && i < tg.bullets){
15965                 tg.setActiveBullet(i);
15966             }
15967         }
15968         
15969     },
15970     
15971     
15972     onRender : function(ct, position)
15973     {
15974        // Roo.log("Call onRender: " + this.xtype);
15975         
15976         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15977         
15978         
15979         
15980         
15981         
15982     },
15983     
15984     setActive: function(state)
15985     {
15986         Roo.log("panel - set active " + this.tabId + "=" + state);
15987         
15988         this.active = state;
15989         if (!state) {
15990             this.el.removeClass('active');
15991             
15992         } else  if (!this.el.hasClass('active')) {
15993             this.el.addClass('active');
15994         }
15995         
15996         this.fireEvent('changed', this, state);
15997     }
15998     
15999     
16000 });
16001  
16002
16003  
16004
16005  /*
16006  * - LGPL
16007  *
16008  * DateField
16009  * 
16010  */
16011
16012 /**
16013  * @class Roo.bootstrap.DateField
16014  * @extends Roo.bootstrap.Input
16015  * Bootstrap DateField class
16016  * @cfg {Number} weekStart default 0
16017  * @cfg {String} viewMode default empty, (months|years)
16018  * @cfg {String} minViewMode default empty, (months|years)
16019  * @cfg {Number} startDate default -Infinity
16020  * @cfg {Number} endDate default Infinity
16021  * @cfg {Boolean} todayHighlight default false
16022  * @cfg {Boolean} todayBtn default false
16023  * @cfg {Boolean} calendarWeeks default false
16024  * @cfg {Object} daysOfWeekDisabled default empty
16025  * @cfg {Boolean} singleMode default false (true | false)
16026  * 
16027  * @cfg {Boolean} keyboardNavigation default true
16028  * @cfg {String} language default en
16029  * 
16030  * @constructor
16031  * Create a new DateField
16032  * @param {Object} config The config object
16033  */
16034
16035 Roo.bootstrap.DateField = function(config){
16036     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16037      this.addEvents({
16038             /**
16039              * @event show
16040              * Fires when this field show.
16041              * @param {Roo.bootstrap.DateField} this
16042              * @param {Mixed} date The date value
16043              */
16044             show : true,
16045             /**
16046              * @event show
16047              * Fires when this field hide.
16048              * @param {Roo.bootstrap.DateField} this
16049              * @param {Mixed} date The date value
16050              */
16051             hide : true,
16052             /**
16053              * @event select
16054              * Fires when select a date.
16055              * @param {Roo.bootstrap.DateField} this
16056              * @param {Mixed} date The date value
16057              */
16058             select : true
16059         });
16060 };
16061
16062 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16063     
16064     /**
16065      * @cfg {String} format
16066      * The default date format string which can be overriden for localization support.  The format must be
16067      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16068      */
16069     format : "m/d/y",
16070     /**
16071      * @cfg {String} altFormats
16072      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16073      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16074      */
16075     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16076     
16077     weekStart : 0,
16078     
16079     viewMode : '',
16080     
16081     minViewMode : '',
16082     
16083     todayHighlight : false,
16084     
16085     todayBtn: false,
16086     
16087     language: 'en',
16088     
16089     keyboardNavigation: true,
16090     
16091     calendarWeeks: false,
16092     
16093     startDate: -Infinity,
16094     
16095     endDate: Infinity,
16096     
16097     daysOfWeekDisabled: [],
16098     
16099     _events: [],
16100     
16101     singleMode : false,
16102     
16103     UTCDate: function()
16104     {
16105         return new Date(Date.UTC.apply(Date, arguments));
16106     },
16107     
16108     UTCToday: function()
16109     {
16110         var today = new Date();
16111         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16112     },
16113     
16114     getDate: function() {
16115             var d = this.getUTCDate();
16116             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16117     },
16118     
16119     getUTCDate: function() {
16120             return this.date;
16121     },
16122     
16123     setDate: function(d) {
16124             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16125     },
16126     
16127     setUTCDate: function(d) {
16128             this.date = d;
16129             this.setValue(this.formatDate(this.date));
16130     },
16131         
16132     onRender: function(ct, position)
16133     {
16134         
16135         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16136         
16137         this.language = this.language || 'en';
16138         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16139         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16140         
16141         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16142         this.format = this.format || 'm/d/y';
16143         this.isInline = false;
16144         this.isInput = true;
16145         this.component = this.el.select('.add-on', true).first() || false;
16146         this.component = (this.component && this.component.length === 0) ? false : this.component;
16147         this.hasInput = this.component && this.inputEL().length;
16148         
16149         if (typeof(this.minViewMode === 'string')) {
16150             switch (this.minViewMode) {
16151                 case 'months':
16152                     this.minViewMode = 1;
16153                     break;
16154                 case 'years':
16155                     this.minViewMode = 2;
16156                     break;
16157                 default:
16158                     this.minViewMode = 0;
16159                     break;
16160             }
16161         }
16162         
16163         if (typeof(this.viewMode === 'string')) {
16164             switch (this.viewMode) {
16165                 case 'months':
16166                     this.viewMode = 1;
16167                     break;
16168                 case 'years':
16169                     this.viewMode = 2;
16170                     break;
16171                 default:
16172                     this.viewMode = 0;
16173                     break;
16174             }
16175         }
16176                 
16177         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16178         
16179 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16180         
16181         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16182         
16183         this.picker().on('mousedown', this.onMousedown, this);
16184         this.picker().on('click', this.onClick, this);
16185         
16186         this.picker().addClass('datepicker-dropdown');
16187         
16188         this.startViewMode = this.viewMode;
16189         
16190         if(this.singleMode){
16191             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16192                 v.setVisibilityMode(Roo.Element.DISPLAY)
16193                 v.hide();
16194             });
16195             
16196             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16197                 v.setStyle('width', '189px');
16198             });
16199         }
16200         
16201         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16202             if(!this.calendarWeeks){
16203                 v.remove();
16204                 return;
16205             }
16206             
16207             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16208             v.attr('colspan', function(i, val){
16209                 return parseInt(val) + 1;
16210             });
16211         })
16212                         
16213         
16214         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16215         
16216         this.setStartDate(this.startDate);
16217         this.setEndDate(this.endDate);
16218         
16219         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16220         
16221         this.fillDow();
16222         this.fillMonths();
16223         this.update();
16224         this.showMode();
16225         
16226         if(this.isInline) {
16227             this.show();
16228         }
16229     },
16230     
16231     picker : function()
16232     {
16233         return this.pickerEl;
16234 //        return this.el.select('.datepicker', true).first();
16235     },
16236     
16237     fillDow: function()
16238     {
16239         var dowCnt = this.weekStart;
16240         
16241         var dow = {
16242             tag: 'tr',
16243             cn: [
16244                 
16245             ]
16246         };
16247         
16248         if(this.calendarWeeks){
16249             dow.cn.push({
16250                 tag: 'th',
16251                 cls: 'cw',
16252                 html: '&nbsp;'
16253             })
16254         }
16255         
16256         while (dowCnt < this.weekStart + 7) {
16257             dow.cn.push({
16258                 tag: 'th',
16259                 cls: 'dow',
16260                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16261             });
16262         }
16263         
16264         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16265     },
16266     
16267     fillMonths: function()
16268     {    
16269         var i = 0;
16270         var months = this.picker().select('>.datepicker-months td', true).first();
16271         
16272         months.dom.innerHTML = '';
16273         
16274         while (i < 12) {
16275             var month = {
16276                 tag: 'span',
16277                 cls: 'month',
16278                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16279             }
16280             
16281             months.createChild(month);
16282         }
16283         
16284     },
16285     
16286     update: function()
16287     {
16288         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;
16289         
16290         if (this.date < this.startDate) {
16291             this.viewDate = new Date(this.startDate);
16292         } else if (this.date > this.endDate) {
16293             this.viewDate = new Date(this.endDate);
16294         } else {
16295             this.viewDate = new Date(this.date);
16296         }
16297         
16298         this.fill();
16299     },
16300     
16301     fill: function() 
16302     {
16303         var d = new Date(this.viewDate),
16304                 year = d.getUTCFullYear(),
16305                 month = d.getUTCMonth(),
16306                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16307                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16308                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16309                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16310                 currentDate = this.date && this.date.valueOf(),
16311                 today = this.UTCToday();
16312         
16313         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16314         
16315 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16316         
16317 //        this.picker.select('>tfoot th.today').
16318 //                                              .text(dates[this.language].today)
16319 //                                              .toggle(this.todayBtn !== false);
16320     
16321         this.updateNavArrows();
16322         this.fillMonths();
16323                                                 
16324         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16325         
16326         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16327          
16328         prevMonth.setUTCDate(day);
16329         
16330         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16331         
16332         var nextMonth = new Date(prevMonth);
16333         
16334         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16335         
16336         nextMonth = nextMonth.valueOf();
16337         
16338         var fillMonths = false;
16339         
16340         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16341         
16342         while(prevMonth.valueOf() < nextMonth) {
16343             var clsName = '';
16344             
16345             if (prevMonth.getUTCDay() === this.weekStart) {
16346                 if(fillMonths){
16347                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16348                 }
16349                     
16350                 fillMonths = {
16351                     tag: 'tr',
16352                     cn: []
16353                 };
16354                 
16355                 if(this.calendarWeeks){
16356                     // ISO 8601: First week contains first thursday.
16357                     // ISO also states week starts on Monday, but we can be more abstract here.
16358                     var
16359                     // Start of current week: based on weekstart/current date
16360                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16361                     // Thursday of this week
16362                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16363                     // First Thursday of year, year from thursday
16364                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16365                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16366                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16367                     
16368                     fillMonths.cn.push({
16369                         tag: 'td',
16370                         cls: 'cw',
16371                         html: calWeek
16372                     });
16373                 }
16374             }
16375             
16376             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16377                 clsName += ' old';
16378             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16379                 clsName += ' new';
16380             }
16381             if (this.todayHighlight &&
16382                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16383                 prevMonth.getUTCMonth() == today.getMonth() &&
16384                 prevMonth.getUTCDate() == today.getDate()) {
16385                 clsName += ' today';
16386             }
16387             
16388             if (currentDate && prevMonth.valueOf() === currentDate) {
16389                 clsName += ' active';
16390             }
16391             
16392             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16393                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16394                     clsName += ' disabled';
16395             }
16396             
16397             fillMonths.cn.push({
16398                 tag: 'td',
16399                 cls: 'day ' + clsName,
16400                 html: prevMonth.getDate()
16401             })
16402             
16403             prevMonth.setDate(prevMonth.getDate()+1);
16404         }
16405           
16406         var currentYear = this.date && this.date.getUTCFullYear();
16407         var currentMonth = this.date && this.date.getUTCMonth();
16408         
16409         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16410         
16411         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16412             v.removeClass('active');
16413             
16414             if(currentYear === year && k === currentMonth){
16415                 v.addClass('active');
16416             }
16417             
16418             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16419                 v.addClass('disabled');
16420             }
16421             
16422         });
16423         
16424         
16425         year = parseInt(year/10, 10) * 10;
16426         
16427         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16428         
16429         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16430         
16431         year -= 1;
16432         for (var i = -1; i < 11; i++) {
16433             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16434                 tag: 'span',
16435                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16436                 html: year
16437             })
16438             
16439             year += 1;
16440         }
16441     },
16442     
16443     showMode: function(dir) 
16444     {
16445         if (dir) {
16446             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16447         }
16448         
16449         Roo.each(this.picker().select('>div',true).elements, function(v){
16450             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16451             v.hide();
16452         });
16453         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16454     },
16455     
16456     place: function()
16457     {
16458         if(this.isInline) return;
16459         
16460         this.picker().removeClass(['bottom', 'top']);
16461         
16462         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16463             /*
16464              * place to the top of element!
16465              *
16466              */
16467             
16468             this.picker().addClass('top');
16469             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16470             
16471             return;
16472         }
16473         
16474         this.picker().addClass('bottom');
16475         
16476         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16477     },
16478     
16479     parseDate : function(value)
16480     {
16481         if(!value || value instanceof Date){
16482             return value;
16483         }
16484         var v = Date.parseDate(value, this.format);
16485         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16486             v = Date.parseDate(value, 'Y-m-d');
16487         }
16488         if(!v && this.altFormats){
16489             if(!this.altFormatsArray){
16490                 this.altFormatsArray = this.altFormats.split("|");
16491             }
16492             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16493                 v = Date.parseDate(value, this.altFormatsArray[i]);
16494             }
16495         }
16496         return v;
16497     },
16498     
16499     formatDate : function(date, fmt)
16500     {   
16501         return (!date || !(date instanceof Date)) ?
16502         date : date.dateFormat(fmt || this.format);
16503     },
16504     
16505     onFocus : function()
16506     {
16507         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16508         this.show();
16509     },
16510     
16511     onBlur : function()
16512     {
16513         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16514         
16515         var d = this.inputEl().getValue();
16516         
16517         this.setValue(d);
16518                 
16519         this.hide();
16520     },
16521     
16522     show : function()
16523     {
16524         this.picker().show();
16525         this.update();
16526         this.place();
16527         
16528         this.fireEvent('show', this, this.date);
16529     },
16530     
16531     hide : function()
16532     {
16533         if(this.isInline) return;
16534         this.picker().hide();
16535         this.viewMode = this.startViewMode;
16536         this.showMode();
16537         
16538         this.fireEvent('hide', this, this.date);
16539         
16540     },
16541     
16542     onMousedown: function(e)
16543     {
16544         e.stopPropagation();
16545         e.preventDefault();
16546     },
16547     
16548     keyup: function(e)
16549     {
16550         Roo.bootstrap.DateField.superclass.keyup.call(this);
16551         this.update();
16552     },
16553
16554     setValue: function(v)
16555     {
16556         
16557         // v can be a string or a date..
16558         
16559         
16560         var d = new Date(this.parseDate(v) ).clearTime();
16561         
16562         if(isNaN(d.getTime())){
16563             this.date = this.viewDate = '';
16564             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16565             return;
16566         }
16567         
16568         v = this.formatDate(d);
16569         
16570         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16571         
16572         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16573      
16574         this.update();
16575
16576         this.fireEvent('select', this, this.date);
16577         
16578     },
16579     
16580     getValue: function()
16581     {
16582         return this.formatDate(this.date);
16583     },
16584     
16585     fireKey: function(e)
16586     {
16587         if (!this.picker().isVisible()){
16588             if (e.keyCode == 27) // allow escape to hide and re-show picker
16589                 this.show();
16590             return;
16591         }
16592         
16593         var dateChanged = false,
16594         dir, day, month,
16595         newDate, newViewDate;
16596         
16597         switch(e.keyCode){
16598             case 27: // escape
16599                 this.hide();
16600                 e.preventDefault();
16601                 break;
16602             case 37: // left
16603             case 39: // right
16604                 if (!this.keyboardNavigation) break;
16605                 dir = e.keyCode == 37 ? -1 : 1;
16606                 
16607                 if (e.ctrlKey){
16608                     newDate = this.moveYear(this.date, dir);
16609                     newViewDate = this.moveYear(this.viewDate, dir);
16610                 } else if (e.shiftKey){
16611                     newDate = this.moveMonth(this.date, dir);
16612                     newViewDate = this.moveMonth(this.viewDate, dir);
16613                 } else {
16614                     newDate = new Date(this.date);
16615                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16616                     newViewDate = new Date(this.viewDate);
16617                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16618                 }
16619                 if (this.dateWithinRange(newDate)){
16620                     this.date = newDate;
16621                     this.viewDate = newViewDate;
16622                     this.setValue(this.formatDate(this.date));
16623 //                    this.update();
16624                     e.preventDefault();
16625                     dateChanged = true;
16626                 }
16627                 break;
16628             case 38: // up
16629             case 40: // down
16630                 if (!this.keyboardNavigation) break;
16631                 dir = e.keyCode == 38 ? -1 : 1;
16632                 if (e.ctrlKey){
16633                     newDate = this.moveYear(this.date, dir);
16634                     newViewDate = this.moveYear(this.viewDate, dir);
16635                 } else if (e.shiftKey){
16636                     newDate = this.moveMonth(this.date, dir);
16637                     newViewDate = this.moveMonth(this.viewDate, dir);
16638                 } else {
16639                     newDate = new Date(this.date);
16640                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16641                     newViewDate = new Date(this.viewDate);
16642                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16643                 }
16644                 if (this.dateWithinRange(newDate)){
16645                     this.date = newDate;
16646                     this.viewDate = newViewDate;
16647                     this.setValue(this.formatDate(this.date));
16648 //                    this.update();
16649                     e.preventDefault();
16650                     dateChanged = true;
16651                 }
16652                 break;
16653             case 13: // enter
16654                 this.setValue(this.formatDate(this.date));
16655                 this.hide();
16656                 e.preventDefault();
16657                 break;
16658             case 9: // tab
16659                 this.setValue(this.formatDate(this.date));
16660                 this.hide();
16661                 break;
16662             case 16: // shift
16663             case 17: // ctrl
16664             case 18: // alt
16665                 break;
16666             default :
16667                 this.hide();
16668                 
16669         }
16670     },
16671     
16672     
16673     onClick: function(e) 
16674     {
16675         e.stopPropagation();
16676         e.preventDefault();
16677         
16678         var target = e.getTarget();
16679         
16680         if(target.nodeName.toLowerCase() === 'i'){
16681             target = Roo.get(target).dom.parentNode;
16682         }
16683         
16684         var nodeName = target.nodeName;
16685         var className = target.className;
16686         var html = target.innerHTML;
16687         //Roo.log(nodeName);
16688         
16689         switch(nodeName.toLowerCase()) {
16690             case 'th':
16691                 switch(className) {
16692                     case 'switch':
16693                         this.showMode(1);
16694                         break;
16695                     case 'prev':
16696                     case 'next':
16697                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16698                         switch(this.viewMode){
16699                                 case 0:
16700                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16701                                         break;
16702                                 case 1:
16703                                 case 2:
16704                                         this.viewDate = this.moveYear(this.viewDate, dir);
16705                                         break;
16706                         }
16707                         this.fill();
16708                         break;
16709                     case 'today':
16710                         var date = new Date();
16711                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16712 //                        this.fill()
16713                         this.setValue(this.formatDate(this.date));
16714                         
16715                         this.hide();
16716                         break;
16717                 }
16718                 break;
16719             case 'span':
16720                 if (className.indexOf('disabled') < 0) {
16721                     this.viewDate.setUTCDate(1);
16722                     if (className.indexOf('month') > -1) {
16723                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16724                     } else {
16725                         var year = parseInt(html, 10) || 0;
16726                         this.viewDate.setUTCFullYear(year);
16727                         
16728                     }
16729                     
16730                     if(this.singleMode){
16731                         this.setValue(this.formatDate(this.viewDate));
16732                         this.hide();
16733                         return;
16734                     }
16735                     
16736                     this.showMode(-1);
16737                     this.fill();
16738                 }
16739                 break;
16740                 
16741             case 'td':
16742                 //Roo.log(className);
16743                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16744                     var day = parseInt(html, 10) || 1;
16745                     var year = this.viewDate.getUTCFullYear(),
16746                         month = this.viewDate.getUTCMonth();
16747
16748                     if (className.indexOf('old') > -1) {
16749                         if(month === 0 ){
16750                             month = 11;
16751                             year -= 1;
16752                         }else{
16753                             month -= 1;
16754                         }
16755                     } else if (className.indexOf('new') > -1) {
16756                         if (month == 11) {
16757                             month = 0;
16758                             year += 1;
16759                         } else {
16760                             month += 1;
16761                         }
16762                     }
16763                     //Roo.log([year,month,day]);
16764                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16765                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16766 //                    this.fill();
16767                     //Roo.log(this.formatDate(this.date));
16768                     this.setValue(this.formatDate(this.date));
16769                     this.hide();
16770                 }
16771                 break;
16772         }
16773     },
16774     
16775     setStartDate: function(startDate)
16776     {
16777         this.startDate = startDate || -Infinity;
16778         if (this.startDate !== -Infinity) {
16779             this.startDate = this.parseDate(this.startDate);
16780         }
16781         this.update();
16782         this.updateNavArrows();
16783     },
16784
16785     setEndDate: function(endDate)
16786     {
16787         this.endDate = endDate || Infinity;
16788         if (this.endDate !== Infinity) {
16789             this.endDate = this.parseDate(this.endDate);
16790         }
16791         this.update();
16792         this.updateNavArrows();
16793     },
16794     
16795     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16796     {
16797         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16798         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16799             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16800         }
16801         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16802             return parseInt(d, 10);
16803         });
16804         this.update();
16805         this.updateNavArrows();
16806     },
16807     
16808     updateNavArrows: function() 
16809     {
16810         if(this.singleMode){
16811             return;
16812         }
16813         
16814         var d = new Date(this.viewDate),
16815         year = d.getUTCFullYear(),
16816         month = d.getUTCMonth();
16817         
16818         Roo.each(this.picker().select('.prev', true).elements, function(v){
16819             v.show();
16820             switch (this.viewMode) {
16821                 case 0:
16822
16823                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16824                         v.hide();
16825                     }
16826                     break;
16827                 case 1:
16828                 case 2:
16829                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16830                         v.hide();
16831                     }
16832                     break;
16833             }
16834         });
16835         
16836         Roo.each(this.picker().select('.next', true).elements, function(v){
16837             v.show();
16838             switch (this.viewMode) {
16839                 case 0:
16840
16841                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16842                         v.hide();
16843                     }
16844                     break;
16845                 case 1:
16846                 case 2:
16847                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16848                         v.hide();
16849                     }
16850                     break;
16851             }
16852         })
16853     },
16854     
16855     moveMonth: function(date, dir)
16856     {
16857         if (!dir) return date;
16858         var new_date = new Date(date.valueOf()),
16859         day = new_date.getUTCDate(),
16860         month = new_date.getUTCMonth(),
16861         mag = Math.abs(dir),
16862         new_month, test;
16863         dir = dir > 0 ? 1 : -1;
16864         if (mag == 1){
16865             test = dir == -1
16866             // If going back one month, make sure month is not current month
16867             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16868             ? function(){
16869                 return new_date.getUTCMonth() == month;
16870             }
16871             // If going forward one month, make sure month is as expected
16872             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16873             : function(){
16874                 return new_date.getUTCMonth() != new_month;
16875             };
16876             new_month = month + dir;
16877             new_date.setUTCMonth(new_month);
16878             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16879             if (new_month < 0 || new_month > 11)
16880                 new_month = (new_month + 12) % 12;
16881         } else {
16882             // For magnitudes >1, move one month at a time...
16883             for (var i=0; i<mag; i++)
16884                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16885                 new_date = this.moveMonth(new_date, dir);
16886             // ...then reset the day, keeping it in the new month
16887             new_month = new_date.getUTCMonth();
16888             new_date.setUTCDate(day);
16889             test = function(){
16890                 return new_month != new_date.getUTCMonth();
16891             };
16892         }
16893         // Common date-resetting loop -- if date is beyond end of month, make it
16894         // end of month
16895         while (test()){
16896             new_date.setUTCDate(--day);
16897             new_date.setUTCMonth(new_month);
16898         }
16899         return new_date;
16900     },
16901
16902     moveYear: function(date, dir)
16903     {
16904         return this.moveMonth(date, dir*12);
16905     },
16906
16907     dateWithinRange: function(date)
16908     {
16909         return date >= this.startDate && date <= this.endDate;
16910     },
16911
16912     
16913     remove: function() 
16914     {
16915         this.picker().remove();
16916     }
16917    
16918 });
16919
16920 Roo.apply(Roo.bootstrap.DateField,  {
16921     
16922     head : {
16923         tag: 'thead',
16924         cn: [
16925         {
16926             tag: 'tr',
16927             cn: [
16928             {
16929                 tag: 'th',
16930                 cls: 'prev',
16931                 html: '<i class="fa fa-arrow-left"/>'
16932             },
16933             {
16934                 tag: 'th',
16935                 cls: 'switch',
16936                 colspan: '5'
16937             },
16938             {
16939                 tag: 'th',
16940                 cls: 'next',
16941                 html: '<i class="fa fa-arrow-right"/>'
16942             }
16943
16944             ]
16945         }
16946         ]
16947     },
16948     
16949     content : {
16950         tag: 'tbody',
16951         cn: [
16952         {
16953             tag: 'tr',
16954             cn: [
16955             {
16956                 tag: 'td',
16957                 colspan: '7'
16958             }
16959             ]
16960         }
16961         ]
16962     },
16963     
16964     footer : {
16965         tag: 'tfoot',
16966         cn: [
16967         {
16968             tag: 'tr',
16969             cn: [
16970             {
16971                 tag: 'th',
16972                 colspan: '7',
16973                 cls: 'today'
16974             }
16975                     
16976             ]
16977         }
16978         ]
16979     },
16980     
16981     dates:{
16982         en: {
16983             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16984             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16985             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16986             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16987             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16988             today: "Today"
16989         }
16990     },
16991     
16992     modes: [
16993     {
16994         clsName: 'days',
16995         navFnc: 'Month',
16996         navStep: 1
16997     },
16998     {
16999         clsName: 'months',
17000         navFnc: 'FullYear',
17001         navStep: 1
17002     },
17003     {
17004         clsName: 'years',
17005         navFnc: 'FullYear',
17006         navStep: 10
17007     }]
17008 });
17009
17010 Roo.apply(Roo.bootstrap.DateField,  {
17011   
17012     template : {
17013         tag: 'div',
17014         cls: 'datepicker dropdown-menu roo-dynamic',
17015         cn: [
17016         {
17017             tag: 'div',
17018             cls: 'datepicker-days',
17019             cn: [
17020             {
17021                 tag: 'table',
17022                 cls: 'table-condensed',
17023                 cn:[
17024                 Roo.bootstrap.DateField.head,
17025                 {
17026                     tag: 'tbody'
17027                 },
17028                 Roo.bootstrap.DateField.footer
17029                 ]
17030             }
17031             ]
17032         },
17033         {
17034             tag: 'div',
17035             cls: 'datepicker-months',
17036             cn: [
17037             {
17038                 tag: 'table',
17039                 cls: 'table-condensed',
17040                 cn:[
17041                 Roo.bootstrap.DateField.head,
17042                 Roo.bootstrap.DateField.content,
17043                 Roo.bootstrap.DateField.footer
17044                 ]
17045             }
17046             ]
17047         },
17048         {
17049             tag: 'div',
17050             cls: 'datepicker-years',
17051             cn: [
17052             {
17053                 tag: 'table',
17054                 cls: 'table-condensed',
17055                 cn:[
17056                 Roo.bootstrap.DateField.head,
17057                 Roo.bootstrap.DateField.content,
17058                 Roo.bootstrap.DateField.footer
17059                 ]
17060             }
17061             ]
17062         }
17063         ]
17064     }
17065 });
17066
17067  
17068
17069  /*
17070  * - LGPL
17071  *
17072  * TimeField
17073  * 
17074  */
17075
17076 /**
17077  * @class Roo.bootstrap.TimeField
17078  * @extends Roo.bootstrap.Input
17079  * Bootstrap DateField class
17080  * 
17081  * 
17082  * @constructor
17083  * Create a new TimeField
17084  * @param {Object} config The config object
17085  */
17086
17087 Roo.bootstrap.TimeField = function(config){
17088     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17089     this.addEvents({
17090             /**
17091              * @event show
17092              * Fires when this field show.
17093              * @param {Roo.bootstrap.DateField} thisthis
17094              * @param {Mixed} date The date value
17095              */
17096             show : true,
17097             /**
17098              * @event show
17099              * Fires when this field hide.
17100              * @param {Roo.bootstrap.DateField} this
17101              * @param {Mixed} date The date value
17102              */
17103             hide : true,
17104             /**
17105              * @event select
17106              * Fires when select a date.
17107              * @param {Roo.bootstrap.DateField} this
17108              * @param {Mixed} date The date value
17109              */
17110             select : true
17111         });
17112 };
17113
17114 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17115     
17116     /**
17117      * @cfg {String} format
17118      * The default time format string which can be overriden for localization support.  The format must be
17119      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17120      */
17121     format : "H:i",
17122        
17123     onRender: function(ct, position)
17124     {
17125         
17126         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17127                 
17128         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17129         
17130         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17131         
17132         this.pop = this.picker().select('>.datepicker-time',true).first();
17133         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17134         
17135         this.picker().on('mousedown', this.onMousedown, this);
17136         this.picker().on('click', this.onClick, this);
17137         
17138         this.picker().addClass('datepicker-dropdown');
17139     
17140         this.fillTime();
17141         this.update();
17142             
17143         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17144         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17145         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17146         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17147         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17148         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17149
17150     },
17151     
17152     fireKey: function(e){
17153         if (!this.picker().isVisible()){
17154             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17155                 this.show();
17156             }
17157             return;
17158         }
17159
17160         e.preventDefault();
17161         
17162         switch(e.keyCode){
17163             case 27: // escape
17164                 this.hide();
17165                 break;
17166             case 37: // left
17167             case 39: // right
17168                 this.onTogglePeriod();
17169                 break;
17170             case 38: // up
17171                 this.onIncrementMinutes();
17172                 break;
17173             case 40: // down
17174                 this.onDecrementMinutes();
17175                 break;
17176             case 13: // enter
17177             case 9: // tab
17178                 this.setTime();
17179                 break;
17180         }
17181     },
17182     
17183     onClick: function(e) {
17184         e.stopPropagation();
17185         e.preventDefault();
17186     },
17187     
17188     picker : function()
17189     {
17190         return this.el.select('.datepicker', true).first();
17191     },
17192     
17193     fillTime: function()
17194     {    
17195         var time = this.pop.select('tbody', true).first();
17196         
17197         time.dom.innerHTML = '';
17198         
17199         time.createChild({
17200             tag: 'tr',
17201             cn: [
17202                 {
17203                     tag: 'td',
17204                     cn: [
17205                         {
17206                             tag: 'a',
17207                             href: '#',
17208                             cls: 'btn',
17209                             cn: [
17210                                 {
17211                                     tag: 'span',
17212                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17213                                 }
17214                             ]
17215                         } 
17216                     ]
17217                 },
17218                 {
17219                     tag: 'td',
17220                     cls: 'separator'
17221                 },
17222                 {
17223                     tag: 'td',
17224                     cn: [
17225                         {
17226                             tag: 'a',
17227                             href: '#',
17228                             cls: 'btn',
17229                             cn: [
17230                                 {
17231                                     tag: 'span',
17232                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17233                                 }
17234                             ]
17235                         }
17236                     ]
17237                 },
17238                 {
17239                     tag: 'td',
17240                     cls: 'separator'
17241                 }
17242             ]
17243         });
17244         
17245         time.createChild({
17246             tag: 'tr',
17247             cn: [
17248                 {
17249                     tag: 'td',
17250                     cn: [
17251                         {
17252                             tag: 'span',
17253                             cls: 'timepicker-hour',
17254                             html: '00'
17255                         }  
17256                     ]
17257                 },
17258                 {
17259                     tag: 'td',
17260                     cls: 'separator',
17261                     html: ':'
17262                 },
17263                 {
17264                     tag: 'td',
17265                     cn: [
17266                         {
17267                             tag: 'span',
17268                             cls: 'timepicker-minute',
17269                             html: '00'
17270                         }  
17271                     ]
17272                 },
17273                 {
17274                     tag: 'td',
17275                     cls: 'separator'
17276                 },
17277                 {
17278                     tag: 'td',
17279                     cn: [
17280                         {
17281                             tag: 'button',
17282                             type: 'button',
17283                             cls: 'btn btn-primary period',
17284                             html: 'AM'
17285                             
17286                         }
17287                     ]
17288                 }
17289             ]
17290         });
17291         
17292         time.createChild({
17293             tag: 'tr',
17294             cn: [
17295                 {
17296                     tag: 'td',
17297                     cn: [
17298                         {
17299                             tag: 'a',
17300                             href: '#',
17301                             cls: 'btn',
17302                             cn: [
17303                                 {
17304                                     tag: 'span',
17305                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17306                                 }
17307                             ]
17308                         }
17309                     ]
17310                 },
17311                 {
17312                     tag: 'td',
17313                     cls: 'separator'
17314                 },
17315                 {
17316                     tag: 'td',
17317                     cn: [
17318                         {
17319                             tag: 'a',
17320                             href: '#',
17321                             cls: 'btn',
17322                             cn: [
17323                                 {
17324                                     tag: 'span',
17325                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17326                                 }
17327                             ]
17328                         }
17329                     ]
17330                 },
17331                 {
17332                     tag: 'td',
17333                     cls: 'separator'
17334                 }
17335             ]
17336         });
17337         
17338     },
17339     
17340     update: function()
17341     {
17342         
17343         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17344         
17345         this.fill();
17346     },
17347     
17348     fill: function() 
17349     {
17350         var hours = this.time.getHours();
17351         var minutes = this.time.getMinutes();
17352         var period = 'AM';
17353         
17354         if(hours > 11){
17355             period = 'PM';
17356         }
17357         
17358         if(hours == 0){
17359             hours = 12;
17360         }
17361         
17362         
17363         if(hours > 12){
17364             hours = hours - 12;
17365         }
17366         
17367         if(hours < 10){
17368             hours = '0' + hours;
17369         }
17370         
17371         if(minutes < 10){
17372             minutes = '0' + minutes;
17373         }
17374         
17375         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17376         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17377         this.pop.select('button', true).first().dom.innerHTML = period;
17378         
17379     },
17380     
17381     place: function()
17382     {   
17383         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17384         
17385         var cls = ['bottom'];
17386         
17387         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17388             cls.pop();
17389             cls.push('top');
17390         }
17391         
17392         cls.push('right');
17393         
17394         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17395             cls.pop();
17396             cls.push('left');
17397         }
17398         
17399         this.picker().addClass(cls.join('-'));
17400         
17401         var _this = this;
17402         
17403         Roo.each(cls, function(c){
17404             if(c == 'bottom'){
17405                 _this.picker().setTop(_this.inputEl().getHeight());
17406                 return;
17407             }
17408             if(c == 'top'){
17409                 _this.picker().setTop(0 - _this.picker().getHeight());
17410                 return;
17411             }
17412             
17413             if(c == 'left'){
17414                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17415                 return;
17416             }
17417             if(c == 'right'){
17418                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17419                 return;
17420             }
17421         });
17422         
17423     },
17424   
17425     onFocus : function()
17426     {
17427         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17428         this.show();
17429     },
17430     
17431     onBlur : function()
17432     {
17433         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17434         this.hide();
17435     },
17436     
17437     show : function()
17438     {
17439         this.picker().show();
17440         this.pop.show();
17441         this.update();
17442         this.place();
17443         
17444         this.fireEvent('show', this, this.date);
17445     },
17446     
17447     hide : function()
17448     {
17449         this.picker().hide();
17450         this.pop.hide();
17451         
17452         this.fireEvent('hide', this, this.date);
17453     },
17454     
17455     setTime : function()
17456     {
17457         this.hide();
17458         this.setValue(this.time.format(this.format));
17459         
17460         this.fireEvent('select', this, this.date);
17461         
17462         
17463     },
17464     
17465     onMousedown: function(e){
17466         e.stopPropagation();
17467         e.preventDefault();
17468     },
17469     
17470     onIncrementHours: function()
17471     {
17472         Roo.log('onIncrementHours');
17473         this.time = this.time.add(Date.HOUR, 1);
17474         this.update();
17475         
17476     },
17477     
17478     onDecrementHours: function()
17479     {
17480         Roo.log('onDecrementHours');
17481         this.time = this.time.add(Date.HOUR, -1);
17482         this.update();
17483     },
17484     
17485     onIncrementMinutes: function()
17486     {
17487         Roo.log('onIncrementMinutes');
17488         this.time = this.time.add(Date.MINUTE, 1);
17489         this.update();
17490     },
17491     
17492     onDecrementMinutes: function()
17493     {
17494         Roo.log('onDecrementMinutes');
17495         this.time = this.time.add(Date.MINUTE, -1);
17496         this.update();
17497     },
17498     
17499     onTogglePeriod: function()
17500     {
17501         Roo.log('onTogglePeriod');
17502         this.time = this.time.add(Date.HOUR, 12);
17503         this.update();
17504     }
17505     
17506    
17507 });
17508
17509 Roo.apply(Roo.bootstrap.TimeField,  {
17510     
17511     content : {
17512         tag: 'tbody',
17513         cn: [
17514             {
17515                 tag: 'tr',
17516                 cn: [
17517                 {
17518                     tag: 'td',
17519                     colspan: '7'
17520                 }
17521                 ]
17522             }
17523         ]
17524     },
17525     
17526     footer : {
17527         tag: 'tfoot',
17528         cn: [
17529             {
17530                 tag: 'tr',
17531                 cn: [
17532                 {
17533                     tag: 'th',
17534                     colspan: '7',
17535                     cls: '',
17536                     cn: [
17537                         {
17538                             tag: 'button',
17539                             cls: 'btn btn-info ok',
17540                             html: 'OK'
17541                         }
17542                     ]
17543                 }
17544
17545                 ]
17546             }
17547         ]
17548     }
17549 });
17550
17551 Roo.apply(Roo.bootstrap.TimeField,  {
17552   
17553     template : {
17554         tag: 'div',
17555         cls: 'datepicker dropdown-menu',
17556         cn: [
17557             {
17558                 tag: 'div',
17559                 cls: 'datepicker-time',
17560                 cn: [
17561                 {
17562                     tag: 'table',
17563                     cls: 'table-condensed',
17564                     cn:[
17565                     Roo.bootstrap.TimeField.content,
17566                     Roo.bootstrap.TimeField.footer
17567                     ]
17568                 }
17569                 ]
17570             }
17571         ]
17572     }
17573 });
17574
17575  
17576
17577  /*
17578  * - LGPL
17579  *
17580  * MonthField
17581  * 
17582  */
17583
17584 /**
17585  * @class Roo.bootstrap.MonthField
17586  * @extends Roo.bootstrap.Input
17587  * Bootstrap MonthField class
17588  * 
17589  * @cfg {String} language default en
17590  * 
17591  * @constructor
17592  * Create a new MonthField
17593  * @param {Object} config The config object
17594  */
17595
17596 Roo.bootstrap.MonthField = function(config){
17597     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17598     
17599     this.addEvents({
17600         /**
17601          * @event show
17602          * Fires when this field show.
17603          * @param {Roo.bootstrap.MonthField} this
17604          * @param {Mixed} date The date value
17605          */
17606         show : true,
17607         /**
17608          * @event show
17609          * Fires when this field hide.
17610          * @param {Roo.bootstrap.MonthField} this
17611          * @param {Mixed} date The date value
17612          */
17613         hide : true,
17614         /**
17615          * @event select
17616          * Fires when select a date.
17617          * @param {Roo.bootstrap.MonthField} this
17618          * @param {String} oldvalue The old value
17619          * @param {String} newvalue The new value
17620          */
17621         select : true
17622     });
17623 };
17624
17625 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17626     
17627     onRender: function(ct, position)
17628     {
17629         
17630         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17631         
17632         this.language = this.language || 'en';
17633         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17634         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17635         
17636         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17637         this.isInline = false;
17638         this.isInput = true;
17639         this.component = this.el.select('.add-on', true).first() || false;
17640         this.component = (this.component && this.component.length === 0) ? false : this.component;
17641         this.hasInput = this.component && this.inputEL().length;
17642         
17643         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17644         
17645         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17646         
17647         this.picker().on('mousedown', this.onMousedown, this);
17648         this.picker().on('click', this.onClick, this);
17649         
17650         this.picker().addClass('datepicker-dropdown');
17651         
17652         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17653             v.setStyle('width', '189px');
17654         });
17655         
17656         this.fillMonths();
17657         
17658         this.update();
17659         
17660         if(this.isInline) {
17661             this.show();
17662         }
17663         
17664     },
17665     
17666     setValue: function(v, suppressEvent)
17667     {   
17668         var o = this.getValue();
17669         
17670         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17671         
17672         this.update();
17673
17674         if(suppressEvent !== true){
17675             this.fireEvent('select', this, o, v);
17676         }
17677         
17678     },
17679     
17680     getValue: function()
17681     {
17682         return this.value;
17683     },
17684     
17685     onClick: function(e) 
17686     {
17687         e.stopPropagation();
17688         e.preventDefault();
17689         
17690         var target = e.getTarget();
17691         
17692         if(target.nodeName.toLowerCase() === 'i'){
17693             target = Roo.get(target).dom.parentNode;
17694         }
17695         
17696         var nodeName = target.nodeName;
17697         var className = target.className;
17698         var html = target.innerHTML;
17699         
17700         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17701             return;
17702         }
17703         
17704         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17705         
17706         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17707         
17708         this.hide();
17709                         
17710     },
17711     
17712     picker : function()
17713     {
17714         return this.pickerEl;
17715     },
17716     
17717     fillMonths: function()
17718     {    
17719         var i = 0;
17720         var months = this.picker().select('>.datepicker-months td', true).first();
17721         
17722         months.dom.innerHTML = '';
17723         
17724         while (i < 12) {
17725             var month = {
17726                 tag: 'span',
17727                 cls: 'month',
17728                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17729             }
17730             
17731             months.createChild(month);
17732         }
17733         
17734     },
17735     
17736     update: function()
17737     {
17738         var _this = this;
17739         
17740         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17741             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17742         }
17743         
17744         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17745             e.removeClass('active');
17746             
17747             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17748                 e.addClass('active');
17749             }
17750         })
17751     },
17752     
17753     place: function()
17754     {
17755         if(this.isInline) return;
17756         
17757         this.picker().removeClass(['bottom', 'top']);
17758         
17759         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17760             /*
17761              * place to the top of element!
17762              *
17763              */
17764             
17765             this.picker().addClass('top');
17766             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17767             
17768             return;
17769         }
17770         
17771         this.picker().addClass('bottom');
17772         
17773         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17774     },
17775     
17776     onFocus : function()
17777     {
17778         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17779         this.show();
17780     },
17781     
17782     onBlur : function()
17783     {
17784         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17785         
17786         var d = this.inputEl().getValue();
17787         
17788         this.setValue(d);
17789                 
17790         this.hide();
17791     },
17792     
17793     show : function()
17794     {
17795         this.picker().show();
17796         this.picker().select('>.datepicker-months', true).first().show();
17797         this.update();
17798         this.place();
17799         
17800         this.fireEvent('show', this, this.date);
17801     },
17802     
17803     hide : function()
17804     {
17805         if(this.isInline) return;
17806         this.picker().hide();
17807         this.fireEvent('hide', this, this.date);
17808         
17809     },
17810     
17811     onMousedown: function(e)
17812     {
17813         e.stopPropagation();
17814         e.preventDefault();
17815     },
17816     
17817     keyup: function(e)
17818     {
17819         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17820         this.update();
17821     },
17822
17823     fireKey: function(e)
17824     {
17825         if (!this.picker().isVisible()){
17826             if (e.keyCode == 27) // allow escape to hide and re-show picker
17827                 this.show();
17828             return;
17829         }
17830         
17831         var dir;
17832         
17833         switch(e.keyCode){
17834             case 27: // escape
17835                 this.hide();
17836                 e.preventDefault();
17837                 break;
17838             case 37: // left
17839             case 39: // right
17840                 dir = e.keyCode == 37 ? -1 : 1;
17841                 
17842                 this.vIndex = this.vIndex + dir;
17843                 
17844                 if(this.vIndex < 0){
17845                     this.vIndex = 0;
17846                 }
17847                 
17848                 if(this.vIndex > 11){
17849                     this.vIndex = 11;
17850                 }
17851                 
17852                 if(isNaN(this.vIndex)){
17853                     this.vIndex = 0;
17854                 }
17855                 
17856                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17857                 
17858                 break;
17859             case 38: // up
17860             case 40: // down
17861                 
17862                 dir = e.keyCode == 38 ? -1 : 1;
17863                 
17864                 this.vIndex = this.vIndex + dir * 4;
17865                 
17866                 if(this.vIndex < 0){
17867                     this.vIndex = 0;
17868                 }
17869                 
17870                 if(this.vIndex > 11){
17871                     this.vIndex = 11;
17872                 }
17873                 
17874                 if(isNaN(this.vIndex)){
17875                     this.vIndex = 0;
17876                 }
17877                 
17878                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17879                 break;
17880                 
17881             case 13: // enter
17882                 
17883                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17884                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17885                 }
17886                 
17887                 this.hide();
17888                 e.preventDefault();
17889                 break;
17890             case 9: // tab
17891                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17892                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17893                 }
17894                 this.hide();
17895                 break;
17896             case 16: // shift
17897             case 17: // ctrl
17898             case 18: // alt
17899                 break;
17900             default :
17901                 this.hide();
17902                 
17903         }
17904     },
17905     
17906     remove: function() 
17907     {
17908         this.picker().remove();
17909     }
17910    
17911 });
17912
17913 Roo.apply(Roo.bootstrap.MonthField,  {
17914     
17915     content : {
17916         tag: 'tbody',
17917         cn: [
17918         {
17919             tag: 'tr',
17920             cn: [
17921             {
17922                 tag: 'td',
17923                 colspan: '7'
17924             }
17925             ]
17926         }
17927         ]
17928     },
17929     
17930     dates:{
17931         en: {
17932             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17933             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17934         }
17935     }
17936 });
17937
17938 Roo.apply(Roo.bootstrap.MonthField,  {
17939   
17940     template : {
17941         tag: 'div',
17942         cls: 'datepicker dropdown-menu roo-dynamic',
17943         cn: [
17944             {
17945                 tag: 'div',
17946                 cls: 'datepicker-months',
17947                 cn: [
17948                 {
17949                     tag: 'table',
17950                     cls: 'table-condensed',
17951                     cn:[
17952                         Roo.bootstrap.DateField.content
17953                     ]
17954                 }
17955                 ]
17956             }
17957         ]
17958     }
17959 });
17960
17961  
17962
17963  
17964  /*
17965  * - LGPL
17966  *
17967  * CheckBox
17968  * 
17969  */
17970
17971 /**
17972  * @class Roo.bootstrap.CheckBox
17973  * @extends Roo.bootstrap.Input
17974  * Bootstrap CheckBox class
17975  * 
17976  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17977  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17978  * @cfg {String} boxLabel The text that appears beside the checkbox
17979  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17980  * @cfg {Boolean} checked initnal the element
17981  * @cfg {Boolean} inline inline the element (default false)
17982  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17983  * 
17984  * @constructor
17985  * Create a new CheckBox
17986  * @param {Object} config The config object
17987  */
17988
17989 Roo.bootstrap.CheckBox = function(config){
17990     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17991    
17992     this.addEvents({
17993         /**
17994         * @event check
17995         * Fires when the element is checked or unchecked.
17996         * @param {Roo.bootstrap.CheckBox} this This input
17997         * @param {Boolean} checked The new checked value
17998         */
17999        check : true
18000     });
18001     
18002 };
18003
18004 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18005   
18006     inputType: 'checkbox',
18007     inputValue: 1,
18008     valueOff: 0,
18009     boxLabel: false,
18010     checked: false,
18011     weight : false,
18012     inline: false,
18013     
18014     getAutoCreate : function()
18015     {
18016         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18017         
18018         var id = Roo.id();
18019         
18020         var cfg = {};
18021         
18022         cfg.cls = 'form-group ' + this.inputType; //input-group
18023         
18024         if(this.inline){
18025             cfg.cls += ' ' + this.inputType + '-inline';
18026         }
18027         
18028         var input =  {
18029             tag: 'input',
18030             id : id,
18031             type : this.inputType,
18032             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18033             cls : 'roo-' + this.inputType, //'form-box',
18034             placeholder : this.placeholder || ''
18035             
18036         };
18037         
18038         if (this.weight) { // Validity check?
18039             cfg.cls += " " + this.inputType + "-" + this.weight;
18040         }
18041         
18042         if (this.disabled) {
18043             input.disabled=true;
18044         }
18045         
18046         if(this.checked){
18047             input.checked = this.checked;
18048         }
18049         
18050         if (this.name) {
18051             input.name = this.name;
18052         }
18053         
18054         if (this.size) {
18055             input.cls += ' input-' + this.size;
18056         }
18057         
18058         var settings=this;
18059         
18060         ['xs','sm','md','lg'].map(function(size){
18061             if (settings[size]) {
18062                 cfg.cls += ' col-' + size + '-' + settings[size];
18063             }
18064         });
18065         
18066         var inputblock = input;
18067          
18068         if (this.before || this.after) {
18069             
18070             inputblock = {
18071                 cls : 'input-group',
18072                 cn :  [] 
18073             };
18074             
18075             if (this.before) {
18076                 inputblock.cn.push({
18077                     tag :'span',
18078                     cls : 'input-group-addon',
18079                     html : this.before
18080                 });
18081             }
18082             
18083             inputblock.cn.push(input);
18084             
18085             if (this.after) {
18086                 inputblock.cn.push({
18087                     tag :'span',
18088                     cls : 'input-group-addon',
18089                     html : this.after
18090                 });
18091             }
18092             
18093         }
18094         
18095         if (align ==='left' && this.fieldLabel.length) {
18096                 Roo.log("left and has label");
18097                 cfg.cn = [
18098                     
18099                     {
18100                         tag: 'label',
18101                         'for' :  id,
18102                         cls : 'control-label col-md-' + this.labelWidth,
18103                         html : this.fieldLabel
18104                         
18105                     },
18106                     {
18107                         cls : "col-md-" + (12 - this.labelWidth), 
18108                         cn: [
18109                             inputblock
18110                         ]
18111                     }
18112                     
18113                 ];
18114         } else if ( this.fieldLabel.length) {
18115                 Roo.log(" label");
18116                 cfg.cn = [
18117                    
18118                     {
18119                         tag: this.boxLabel ? 'span' : 'label',
18120                         'for': id,
18121                         cls: 'control-label box-input-label',
18122                         //cls : 'input-group-addon',
18123                         html : this.fieldLabel
18124                         
18125                     },
18126                     
18127                     inputblock
18128                     
18129                 ];
18130
18131         } else {
18132             
18133                 Roo.log(" no label && no align");
18134                 cfg.cn = [  inputblock ] ;
18135                 
18136                 
18137         }
18138         if(this.boxLabel){
18139              var boxLabelCfg = {
18140                 tag: 'label',
18141                 //'for': id, // box label is handled by onclick - so no for...
18142                 cls: 'box-label',
18143                 html: this.boxLabel
18144             }
18145             
18146             if(this.tooltip){
18147                 boxLabelCfg.tooltip = this.tooltip;
18148             }
18149              
18150             cfg.cn.push(boxLabelCfg);
18151         }
18152         
18153         
18154        
18155         return cfg;
18156         
18157     },
18158     
18159     /**
18160      * return the real input element.
18161      */
18162     inputEl: function ()
18163     {
18164         return this.el.select('input.roo-' + this.inputType,true).first();
18165     },
18166     
18167     labelEl: function()
18168     {
18169         return this.el.select('label.control-label',true).first();
18170     },
18171     /* depricated... */
18172     
18173     label: function()
18174     {
18175         return this.labelEl();
18176     },
18177     
18178     initEvents : function()
18179     {
18180 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18181         
18182         this.inputEl().on('click', this.onClick,  this);
18183         
18184         if (this.boxLabel) { 
18185             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18186         }
18187         
18188         this.startValue = this.getValue();
18189         
18190         if(this.groupId){
18191             Roo.bootstrap.CheckBox.register(this);
18192         }
18193     },
18194     
18195     onClick : function()
18196     {   
18197         this.setChecked(!this.checked);
18198     },
18199     
18200     setChecked : function(state,suppressEvent)
18201     {
18202         this.startValue = this.getValue();
18203         
18204         if(this.inputType == 'radio'){
18205             
18206             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18207                 e.dom.checked = false;
18208             });
18209             
18210             this.inputEl().dom.checked = true;
18211             
18212             this.inputEl().dom.value = this.inputValue;
18213             
18214             if(suppressEvent !== true){
18215                 this.fireEvent('check', this, true);
18216             }
18217             
18218             this.validate();
18219             
18220             return;
18221         }
18222         
18223         this.checked = state;
18224         
18225         this.inputEl().dom.checked = state;
18226         
18227         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18228         
18229         if(suppressEvent !== true){
18230             this.fireEvent('check', this, state);
18231         }
18232         
18233         this.validate();
18234     },
18235     
18236     getValue : function()
18237     {
18238         if(this.inputType == 'radio'){
18239             return this.getGroupValue();
18240         }
18241         
18242         return this.inputEl().getValue();
18243         
18244     },
18245     
18246     getGroupValue : function()
18247     {
18248         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18249             return '';
18250         }
18251         
18252         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18253     },
18254     
18255     setValue : function(v,suppressEvent)
18256     {
18257         if(this.inputType == 'radio'){
18258             this.setGroupValue(v, suppressEvent);
18259             return;
18260         }
18261         
18262         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18263         
18264         this.validate();
18265     },
18266     
18267     setGroupValue : function(v, suppressEvent)
18268     {
18269         this.startValue = this.getValue();
18270         
18271         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18272             e.dom.checked = false;
18273             
18274             if(e.dom.value == v){
18275                 e.dom.checked = true;
18276             }
18277         });
18278         
18279         if(suppressEvent !== true){
18280             this.fireEvent('check', this, true);
18281         }
18282
18283         this.validate();
18284         
18285         return;
18286     },
18287     
18288     validate : function()
18289     {
18290         if(
18291                 this.disabled || 
18292                 (this.inputType == 'radio' && this.validateRadio()) ||
18293                 (this.inputType == 'checkbox' && this.validateCheckbox())
18294         ){
18295             this.markValid();
18296             return true;
18297         }
18298         
18299         this.markInvalid();
18300         return false;
18301     },
18302     
18303     validateRadio : function()
18304     {
18305         var valid = false;
18306         
18307         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18308             if(!e.dom.checked){
18309                 return;
18310             }
18311             
18312             valid = true;
18313             
18314             return false;
18315         });
18316         
18317         return valid;
18318     },
18319     
18320     validateCheckbox : function()
18321     {
18322         if(!this.groupId){
18323             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18324         }
18325         
18326         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18327         
18328         if(!group){
18329             return false;
18330         }
18331         
18332         var r = false;
18333         
18334         for(var i in group){
18335             if(r){
18336                 break;
18337             }
18338             
18339             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18340         }
18341         
18342         return r;
18343     },
18344     
18345     /**
18346      * Mark this field as valid
18347      */
18348     markValid : function()
18349     {
18350         if(this.allowBlank){
18351             return;
18352         }
18353         
18354         var _this = this;
18355         
18356         this.fireEvent('valid', this);
18357         
18358         if(this.inputType == 'radio'){
18359             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18360                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18361                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18362             });
18363             
18364             return;
18365         }
18366         
18367         if(!this.groupId){
18368             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18369             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18370             return;
18371         }
18372         
18373         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18374             
18375         if(!group){
18376             return;
18377         }
18378         
18379         for(var i in group){
18380             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18381             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18382         }
18383     },
18384     
18385      /**
18386      * Mark this field as invalid
18387      * @param {String} msg The validation message
18388      */
18389     markInvalid : function(msg)
18390     {
18391         if(this.allowBlank){
18392             return;
18393         }
18394         
18395         var _this = this;
18396         
18397         this.fireEvent('invalid', this, msg);
18398         
18399         if(this.inputType == 'radio'){
18400             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18401                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18402                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18403             });
18404             
18405             return;
18406         }
18407         
18408         if(!this.groupId){
18409             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18410             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18411             return;
18412         }
18413         
18414         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18415             
18416         if(!group){
18417             return;
18418         }
18419         
18420         for(var i in group){
18421             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18422             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18423         }
18424         
18425     }
18426     
18427 });
18428
18429 Roo.apply(Roo.bootstrap.CheckBox, {
18430     
18431     groups: {},
18432     
18433      /**
18434     * register a CheckBox Group
18435     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18436     */
18437     register : function(checkbox)
18438     {
18439         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18440             this.groups[checkbox.groupId] = {};
18441         }
18442         
18443         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18444             return;
18445         }
18446         
18447         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18448         
18449     },
18450     /**
18451     * fetch a CheckBox Group based on the group ID
18452     * @param {string} the group ID
18453     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18454     */
18455     get: function(groupId) {
18456         if (typeof(this.groups[groupId]) == 'undefined') {
18457             return false;
18458         }
18459         
18460         return this.groups[groupId] ;
18461     }
18462     
18463     
18464 });
18465 /*
18466  * - LGPL
18467  *
18468  * Radio
18469  *
18470  *
18471  * not inline
18472  *<div class="radio">
18473   <label>
18474     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18475     Option one is this and that&mdash;be sure to include why it's great
18476   </label>
18477 </div>
18478  *
18479  *
18480  *inline
18481  *<span>
18482  *<label class="radio-inline">fieldLabel</label>
18483  *<label class="radio-inline">
18484   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18485 </label>
18486 <span>
18487  * 
18488  * 
18489  */
18490
18491 /**
18492  * @class Roo.bootstrap.Radio
18493  * @extends Roo.bootstrap.CheckBox
18494  * Bootstrap Radio class
18495
18496  * @constructor
18497  * Create a new Radio
18498  * @param {Object} config The config object
18499  */
18500
18501 Roo.bootstrap.Radio = function(config){
18502     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18503    
18504 };
18505
18506 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18507     
18508     inputType: 'radio',
18509     inputValue: '',
18510     valueOff: '',
18511     
18512     getAutoCreate : function()
18513     {
18514         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18515         align = align || 'left'; // default...
18516         
18517         
18518         
18519         var id = Roo.id();
18520         
18521         var cfg = {
18522                 tag : this.inline ? 'span' : 'div',
18523                 cls : '',
18524                 cn : []
18525         };
18526         
18527         var inline = this.inline ? ' radio-inline' : '';
18528         
18529         var lbl = {
18530                 tag: 'label' ,
18531                 // does not need for, as we wrap the input with it..
18532                 'for' : id,
18533                 cls : 'control-label box-label' + inline,
18534                 cn : []
18535         };
18536         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18537         
18538         var fieldLabel = {
18539             tag: 'label' ,
18540             //cls : 'control-label' + inline,
18541             html : this.fieldLabel,
18542             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18543         };
18544         
18545  
18546         
18547         
18548         var input =  {
18549             tag: 'input',
18550             id : id,
18551             type : this.inputType,
18552             //value : (!this.checked) ? this.valueOff : this.inputValue,
18553             value : this.inputValue,
18554             cls : 'roo-radio',
18555             placeholder : this.placeholder || '' // ?? needed????
18556             
18557         };
18558         if (this.weight) { // Validity check?
18559             input.cls += " radio-" + this.weight;
18560         }
18561         if (this.disabled) {
18562             input.disabled=true;
18563         }
18564         
18565         if(this.checked){
18566             input.checked = this.checked;
18567         }
18568         
18569         if (this.name) {
18570             input.name = this.name;
18571         }
18572         
18573         if (this.size) {
18574             input.cls += ' input-' + this.size;
18575         }
18576         
18577         //?? can span's inline have a width??
18578         
18579         var settings=this;
18580         ['xs','sm','md','lg'].map(function(size){
18581             if (settings[size]) {
18582                 cfg.cls += ' col-' + size + '-' + settings[size];
18583             }
18584         });
18585         
18586         var inputblock = input;
18587         
18588         if (this.before || this.after) {
18589             
18590             inputblock = {
18591                 cls : 'input-group',
18592                 tag : 'span',
18593                 cn :  [] 
18594             };
18595             if (this.before) {
18596                 inputblock.cn.push({
18597                     tag :'span',
18598                     cls : 'input-group-addon',
18599                     html : this.before
18600                 });
18601             }
18602             inputblock.cn.push(input);
18603             if (this.after) {
18604                 inputblock.cn.push({
18605                     tag :'span',
18606                     cls : 'input-group-addon',
18607                     html : this.after
18608                 });
18609             }
18610             
18611         };
18612         
18613         
18614         if (this.fieldLabel && this.fieldLabel.length) {
18615             cfg.cn.push(fieldLabel);
18616         }
18617        
18618         // normal bootstrap puts the input inside the label.
18619         // however with our styled version - it has to go after the input.
18620        
18621         //lbl.cn.push(inputblock);
18622         
18623         var lblwrap =  {
18624             tag: 'span',
18625             cls: 'radio' + inline,
18626             cn: [
18627                 inputblock,
18628                 lbl
18629             ]
18630         };
18631         
18632         cfg.cn.push( lblwrap);
18633         
18634         if(this.boxLabel){
18635             lbl.cn.push({
18636                 tag: 'span',
18637                 html: this.boxLabel
18638             })
18639         }
18640          
18641         
18642         return cfg;
18643         
18644     },
18645     
18646     initEvents : function()
18647     {
18648 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18649         
18650         this.inputEl().on('click', this.onClick,  this);
18651         if (this.boxLabel) {
18652             Roo.log('find label')
18653             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18654         }
18655         
18656     },
18657     
18658     inputEl: function ()
18659     {
18660         return this.el.select('input.roo-radio',true).first();
18661     },
18662     onClick : function()
18663     {   
18664         Roo.log("click");
18665         this.setChecked(true);
18666     },
18667     
18668     setChecked : function(state,suppressEvent)
18669     {
18670         if(state){
18671             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18672                 v.dom.checked = false;
18673             });
18674         }
18675         Roo.log(this.inputEl().dom);
18676         this.checked = state;
18677         this.inputEl().dom.checked = state;
18678         
18679         if(suppressEvent !== true){
18680             this.fireEvent('check', this, state);
18681         }
18682         
18683         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18684         
18685     },
18686     
18687     getGroupValue : function()
18688     {
18689         var value = '';
18690         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18691             if(v.dom.checked == true){
18692                 value = v.dom.value;
18693             }
18694         });
18695         
18696         return value;
18697     },
18698     
18699     /**
18700      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18701      * @return {Mixed} value The field value
18702      */
18703     getValue : function(){
18704         return this.getGroupValue();
18705     }
18706     
18707 });
18708
18709  
18710 //<script type="text/javascript">
18711
18712 /*
18713  * Based  Ext JS Library 1.1.1
18714  * Copyright(c) 2006-2007, Ext JS, LLC.
18715  * LGPL
18716  *
18717  */
18718  
18719 /**
18720  * @class Roo.HtmlEditorCore
18721  * @extends Roo.Component
18722  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18723  *
18724  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18725  */
18726
18727 Roo.HtmlEditorCore = function(config){
18728     
18729     
18730     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18731     
18732     
18733     this.addEvents({
18734         /**
18735          * @event initialize
18736          * Fires when the editor is fully initialized (including the iframe)
18737          * @param {Roo.HtmlEditorCore} this
18738          */
18739         initialize: true,
18740         /**
18741          * @event activate
18742          * Fires when the editor is first receives the focus. Any insertion must wait
18743          * until after this event.
18744          * @param {Roo.HtmlEditorCore} this
18745          */
18746         activate: true,
18747          /**
18748          * @event beforesync
18749          * Fires before the textarea is updated with content from the editor iframe. Return false
18750          * to cancel the sync.
18751          * @param {Roo.HtmlEditorCore} this
18752          * @param {String} html
18753          */
18754         beforesync: true,
18755          /**
18756          * @event beforepush
18757          * Fires before the iframe editor is updated with content from the textarea. Return false
18758          * to cancel the push.
18759          * @param {Roo.HtmlEditorCore} this
18760          * @param {String} html
18761          */
18762         beforepush: true,
18763          /**
18764          * @event sync
18765          * Fires when the textarea is updated with content from the editor iframe.
18766          * @param {Roo.HtmlEditorCore} this
18767          * @param {String} html
18768          */
18769         sync: true,
18770          /**
18771          * @event push
18772          * Fires when the iframe editor is updated with content from the textarea.
18773          * @param {Roo.HtmlEditorCore} this
18774          * @param {String} html
18775          */
18776         push: true,
18777         
18778         /**
18779          * @event editorevent
18780          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18781          * @param {Roo.HtmlEditorCore} this
18782          */
18783         editorevent: true
18784         
18785     });
18786     
18787     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18788     
18789     // defaults : white / black...
18790     this.applyBlacklists();
18791     
18792     
18793     
18794 };
18795
18796
18797 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18798
18799
18800      /**
18801      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18802      */
18803     
18804     owner : false,
18805     
18806      /**
18807      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18808      *                        Roo.resizable.
18809      */
18810     resizable : false,
18811      /**
18812      * @cfg {Number} height (in pixels)
18813      */   
18814     height: 300,
18815    /**
18816      * @cfg {Number} width (in pixels)
18817      */   
18818     width: 500,
18819     
18820     /**
18821      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18822      * 
18823      */
18824     stylesheets: false,
18825     
18826     // id of frame..
18827     frameId: false,
18828     
18829     // private properties
18830     validationEvent : false,
18831     deferHeight: true,
18832     initialized : false,
18833     activated : false,
18834     sourceEditMode : false,
18835     onFocus : Roo.emptyFn,
18836     iframePad:3,
18837     hideMode:'offsets',
18838     
18839     clearUp: true,
18840     
18841     // blacklist + whitelisted elements..
18842     black: false,
18843     white: false,
18844      
18845     
18846
18847     /**
18848      * Protected method that will not generally be called directly. It
18849      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18850      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18851      */
18852     getDocMarkup : function(){
18853         // body styles..
18854         var st = '';
18855         
18856         // inherit styels from page...?? 
18857         if (this.stylesheets === false) {
18858             
18859             Roo.get(document.head).select('style').each(function(node) {
18860                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18861             });
18862             
18863             Roo.get(document.head).select('link').each(function(node) { 
18864                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18865             });
18866             
18867         } else if (!this.stylesheets.length) {
18868                 // simple..
18869                 st = '<style type="text/css">' +
18870                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18871                    '</style>';
18872         } else { 
18873             
18874         }
18875         
18876         st +=  '<style type="text/css">' +
18877             'IMG { cursor: pointer } ' +
18878         '</style>';
18879
18880         
18881         return '<html><head>' + st  +
18882             //<style type="text/css">' +
18883             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18884             //'</style>' +
18885             ' </head><body class="roo-htmleditor-body"></body></html>';
18886     },
18887
18888     // private
18889     onRender : function(ct, position)
18890     {
18891         var _t = this;
18892         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18893         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18894         
18895         
18896         this.el.dom.style.border = '0 none';
18897         this.el.dom.setAttribute('tabIndex', -1);
18898         this.el.addClass('x-hidden hide');
18899         
18900         
18901         
18902         if(Roo.isIE){ // fix IE 1px bogus margin
18903             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18904         }
18905        
18906         
18907         this.frameId = Roo.id();
18908         
18909          
18910         
18911         var iframe = this.owner.wrap.createChild({
18912             tag: 'iframe',
18913             cls: 'form-control', // bootstrap..
18914             id: this.frameId,
18915             name: this.frameId,
18916             frameBorder : 'no',
18917             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18918         }, this.el
18919         );
18920         
18921         
18922         this.iframe = iframe.dom;
18923
18924          this.assignDocWin();
18925         
18926         this.doc.designMode = 'on';
18927        
18928         this.doc.open();
18929         this.doc.write(this.getDocMarkup());
18930         this.doc.close();
18931
18932         
18933         var task = { // must defer to wait for browser to be ready
18934             run : function(){
18935                 //console.log("run task?" + this.doc.readyState);
18936                 this.assignDocWin();
18937                 if(this.doc.body || this.doc.readyState == 'complete'){
18938                     try {
18939                         this.doc.designMode="on";
18940                     } catch (e) {
18941                         return;
18942                     }
18943                     Roo.TaskMgr.stop(task);
18944                     this.initEditor.defer(10, this);
18945                 }
18946             },
18947             interval : 10,
18948             duration: 10000,
18949             scope: this
18950         };
18951         Roo.TaskMgr.start(task);
18952
18953     },
18954
18955     // private
18956     onResize : function(w, h)
18957     {
18958          Roo.log('resize: ' +w + ',' + h );
18959         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18960         if(!this.iframe){
18961             return;
18962         }
18963         if(typeof w == 'number'){
18964             
18965             this.iframe.style.width = w + 'px';
18966         }
18967         if(typeof h == 'number'){
18968             
18969             this.iframe.style.height = h + 'px';
18970             if(this.doc){
18971                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18972             }
18973         }
18974         
18975     },
18976
18977     /**
18978      * Toggles the editor between standard and source edit mode.
18979      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18980      */
18981     toggleSourceEdit : function(sourceEditMode){
18982         
18983         this.sourceEditMode = sourceEditMode === true;
18984         
18985         if(this.sourceEditMode){
18986  
18987             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18988             
18989         }else{
18990             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18991             //this.iframe.className = '';
18992             this.deferFocus();
18993         }
18994         //this.setSize(this.owner.wrap.getSize());
18995         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18996     },
18997
18998     
18999   
19000
19001     /**
19002      * Protected method that will not generally be called directly. If you need/want
19003      * custom HTML cleanup, this is the method you should override.
19004      * @param {String} html The HTML to be cleaned
19005      * return {String} The cleaned HTML
19006      */
19007     cleanHtml : function(html){
19008         html = String(html);
19009         if(html.length > 5){
19010             if(Roo.isSafari){ // strip safari nonsense
19011                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19012             }
19013         }
19014         if(html == '&nbsp;'){
19015             html = '';
19016         }
19017         return html;
19018     },
19019
19020     /**
19021      * HTML Editor -> Textarea
19022      * Protected method that will not generally be called directly. Syncs the contents
19023      * of the editor iframe with the textarea.
19024      */
19025     syncValue : function(){
19026         if(this.initialized){
19027             var bd = (this.doc.body || this.doc.documentElement);
19028             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19029             var html = bd.innerHTML;
19030             if(Roo.isSafari){
19031                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19032                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19033                 if(m && m[1]){
19034                     html = '<div style="'+m[0]+'">' + html + '</div>';
19035                 }
19036             }
19037             html = this.cleanHtml(html);
19038             // fix up the special chars.. normaly like back quotes in word...
19039             // however we do not want to do this with chinese..
19040             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19041                 var cc = b.charCodeAt();
19042                 if (
19043                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19044                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19045                     (cc >= 0xf900 && cc < 0xfb00 )
19046                 ) {
19047                         return b;
19048                 }
19049                 return "&#"+cc+";" 
19050             });
19051             if(this.owner.fireEvent('beforesync', this, html) !== false){
19052                 this.el.dom.value = html;
19053                 this.owner.fireEvent('sync', this, html);
19054             }
19055         }
19056     },
19057
19058     /**
19059      * Protected method that will not generally be called directly. Pushes the value of the textarea
19060      * into the iframe editor.
19061      */
19062     pushValue : function(){
19063         if(this.initialized){
19064             var v = this.el.dom.value.trim();
19065             
19066 //            if(v.length < 1){
19067 //                v = '&#160;';
19068 //            }
19069             
19070             if(this.owner.fireEvent('beforepush', this, v) !== false){
19071                 var d = (this.doc.body || this.doc.documentElement);
19072                 d.innerHTML = v;
19073                 this.cleanUpPaste();
19074                 this.el.dom.value = d.innerHTML;
19075                 this.owner.fireEvent('push', this, v);
19076             }
19077         }
19078     },
19079
19080     // private
19081     deferFocus : function(){
19082         this.focus.defer(10, this);
19083     },
19084
19085     // doc'ed in Field
19086     focus : function(){
19087         if(this.win && !this.sourceEditMode){
19088             this.win.focus();
19089         }else{
19090             this.el.focus();
19091         }
19092     },
19093     
19094     assignDocWin: function()
19095     {
19096         var iframe = this.iframe;
19097         
19098          if(Roo.isIE){
19099             this.doc = iframe.contentWindow.document;
19100             this.win = iframe.contentWindow;
19101         } else {
19102 //            if (!Roo.get(this.frameId)) {
19103 //                return;
19104 //            }
19105 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19106 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19107             
19108             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19109                 return;
19110             }
19111             
19112             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19113             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19114         }
19115     },
19116     
19117     // private
19118     initEditor : function(){
19119         //console.log("INIT EDITOR");
19120         this.assignDocWin();
19121         
19122         
19123         
19124         this.doc.designMode="on";
19125         this.doc.open();
19126         this.doc.write(this.getDocMarkup());
19127         this.doc.close();
19128         
19129         var dbody = (this.doc.body || this.doc.documentElement);
19130         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19131         // this copies styles from the containing element into thsi one..
19132         // not sure why we need all of this..
19133         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19134         
19135         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19136         //ss['background-attachment'] = 'fixed'; // w3c
19137         dbody.bgProperties = 'fixed'; // ie
19138         //Roo.DomHelper.applyStyles(dbody, ss);
19139         Roo.EventManager.on(this.doc, {
19140             //'mousedown': this.onEditorEvent,
19141             'mouseup': this.onEditorEvent,
19142             'dblclick': this.onEditorEvent,
19143             'click': this.onEditorEvent,
19144             'keyup': this.onEditorEvent,
19145             buffer:100,
19146             scope: this
19147         });
19148         if(Roo.isGecko){
19149             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19150         }
19151         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19152             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19153         }
19154         this.initialized = true;
19155
19156         this.owner.fireEvent('initialize', this);
19157         this.pushValue();
19158     },
19159
19160     // private
19161     onDestroy : function(){
19162         
19163         
19164         
19165         if(this.rendered){
19166             
19167             //for (var i =0; i < this.toolbars.length;i++) {
19168             //    // fixme - ask toolbars for heights?
19169             //    this.toolbars[i].onDestroy();
19170            // }
19171             
19172             //this.wrap.dom.innerHTML = '';
19173             //this.wrap.remove();
19174         }
19175     },
19176
19177     // private
19178     onFirstFocus : function(){
19179         
19180         this.assignDocWin();
19181         
19182         
19183         this.activated = true;
19184          
19185     
19186         if(Roo.isGecko){ // prevent silly gecko errors
19187             this.win.focus();
19188             var s = this.win.getSelection();
19189             if(!s.focusNode || s.focusNode.nodeType != 3){
19190                 var r = s.getRangeAt(0);
19191                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19192                 r.collapse(true);
19193                 this.deferFocus();
19194             }
19195             try{
19196                 this.execCmd('useCSS', true);
19197                 this.execCmd('styleWithCSS', false);
19198             }catch(e){}
19199         }
19200         this.owner.fireEvent('activate', this);
19201     },
19202
19203     // private
19204     adjustFont: function(btn){
19205         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19206         //if(Roo.isSafari){ // safari
19207         //    adjust *= 2;
19208        // }
19209         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19210         if(Roo.isSafari){ // safari
19211             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19212             v =  (v < 10) ? 10 : v;
19213             v =  (v > 48) ? 48 : v;
19214             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19215             
19216         }
19217         
19218         
19219         v = Math.max(1, v+adjust);
19220         
19221         this.execCmd('FontSize', v  );
19222     },
19223
19224     onEditorEvent : function(e)
19225     {
19226         this.owner.fireEvent('editorevent', this, e);
19227       //  this.updateToolbar();
19228         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19229     },
19230
19231     insertTag : function(tg)
19232     {
19233         // could be a bit smarter... -> wrap the current selected tRoo..
19234         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19235             
19236             range = this.createRange(this.getSelection());
19237             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19238             wrappingNode.appendChild(range.extractContents());
19239             range.insertNode(wrappingNode);
19240
19241             return;
19242             
19243             
19244             
19245         }
19246         this.execCmd("formatblock",   tg);
19247         
19248     },
19249     
19250     insertText : function(txt)
19251     {
19252         
19253         
19254         var range = this.createRange();
19255         range.deleteContents();
19256                //alert(Sender.getAttribute('label'));
19257                
19258         range.insertNode(this.doc.createTextNode(txt));
19259     } ,
19260     
19261      
19262
19263     /**
19264      * Executes a Midas editor command on the editor document and performs necessary focus and
19265      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19266      * @param {String} cmd The Midas command
19267      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19268      */
19269     relayCmd : function(cmd, value){
19270         this.win.focus();
19271         this.execCmd(cmd, value);
19272         this.owner.fireEvent('editorevent', this);
19273         //this.updateToolbar();
19274         this.owner.deferFocus();
19275     },
19276
19277     /**
19278      * Executes a Midas editor command directly on the editor document.
19279      * For visual commands, you should use {@link #relayCmd} instead.
19280      * <b>This should only be called after the editor is initialized.</b>
19281      * @param {String} cmd The Midas command
19282      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19283      */
19284     execCmd : function(cmd, value){
19285         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19286         this.syncValue();
19287     },
19288  
19289  
19290    
19291     /**
19292      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19293      * to insert tRoo.
19294      * @param {String} text | dom node.. 
19295      */
19296     insertAtCursor : function(text)
19297     {
19298         
19299         
19300         
19301         if(!this.activated){
19302             return;
19303         }
19304         /*
19305         if(Roo.isIE){
19306             this.win.focus();
19307             var r = this.doc.selection.createRange();
19308             if(r){
19309                 r.collapse(true);
19310                 r.pasteHTML(text);
19311                 this.syncValue();
19312                 this.deferFocus();
19313             
19314             }
19315             return;
19316         }
19317         */
19318         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19319             this.win.focus();
19320             
19321             
19322             // from jquery ui (MIT licenced)
19323             var range, node;
19324             var win = this.win;
19325             
19326             if (win.getSelection && win.getSelection().getRangeAt) {
19327                 range = win.getSelection().getRangeAt(0);
19328                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19329                 range.insertNode(node);
19330             } else if (win.document.selection && win.document.selection.createRange) {
19331                 // no firefox support
19332                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19333                 win.document.selection.createRange().pasteHTML(txt);
19334             } else {
19335                 // no firefox support
19336                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19337                 this.execCmd('InsertHTML', txt);
19338             } 
19339             
19340             this.syncValue();
19341             
19342             this.deferFocus();
19343         }
19344     },
19345  // private
19346     mozKeyPress : function(e){
19347         if(e.ctrlKey){
19348             var c = e.getCharCode(), cmd;
19349           
19350             if(c > 0){
19351                 c = String.fromCharCode(c).toLowerCase();
19352                 switch(c){
19353                     case 'b':
19354                         cmd = 'bold';
19355                         break;
19356                     case 'i':
19357                         cmd = 'italic';
19358                         break;
19359                     
19360                     case 'u':
19361                         cmd = 'underline';
19362                         break;
19363                     
19364                     case 'v':
19365                         this.cleanUpPaste.defer(100, this);
19366                         return;
19367                         
19368                 }
19369                 if(cmd){
19370                     this.win.focus();
19371                     this.execCmd(cmd);
19372                     this.deferFocus();
19373                     e.preventDefault();
19374                 }
19375                 
19376             }
19377         }
19378     },
19379
19380     // private
19381     fixKeys : function(){ // load time branching for fastest keydown performance
19382         if(Roo.isIE){
19383             return function(e){
19384                 var k = e.getKey(), r;
19385                 if(k == e.TAB){
19386                     e.stopEvent();
19387                     r = this.doc.selection.createRange();
19388                     if(r){
19389                         r.collapse(true);
19390                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19391                         this.deferFocus();
19392                     }
19393                     return;
19394                 }
19395                 
19396                 if(k == e.ENTER){
19397                     r = this.doc.selection.createRange();
19398                     if(r){
19399                         var target = r.parentElement();
19400                         if(!target || target.tagName.toLowerCase() != 'li'){
19401                             e.stopEvent();
19402                             r.pasteHTML('<br />');
19403                             r.collapse(false);
19404                             r.select();
19405                         }
19406                     }
19407                 }
19408                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19409                     this.cleanUpPaste.defer(100, this);
19410                     return;
19411                 }
19412                 
19413                 
19414             };
19415         }else if(Roo.isOpera){
19416             return function(e){
19417                 var k = e.getKey();
19418                 if(k == e.TAB){
19419                     e.stopEvent();
19420                     this.win.focus();
19421                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19422                     this.deferFocus();
19423                 }
19424                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19425                     this.cleanUpPaste.defer(100, this);
19426                     return;
19427                 }
19428                 
19429             };
19430         }else if(Roo.isSafari){
19431             return function(e){
19432                 var k = e.getKey();
19433                 
19434                 if(k == e.TAB){
19435                     e.stopEvent();
19436                     this.execCmd('InsertText','\t');
19437                     this.deferFocus();
19438                     return;
19439                 }
19440                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19441                     this.cleanUpPaste.defer(100, this);
19442                     return;
19443                 }
19444                 
19445              };
19446         }
19447     }(),
19448     
19449     getAllAncestors: function()
19450     {
19451         var p = this.getSelectedNode();
19452         var a = [];
19453         if (!p) {
19454             a.push(p); // push blank onto stack..
19455             p = this.getParentElement();
19456         }
19457         
19458         
19459         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19460             a.push(p);
19461             p = p.parentNode;
19462         }
19463         a.push(this.doc.body);
19464         return a;
19465     },
19466     lastSel : false,
19467     lastSelNode : false,
19468     
19469     
19470     getSelection : function() 
19471     {
19472         this.assignDocWin();
19473         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19474     },
19475     
19476     getSelectedNode: function() 
19477     {
19478         // this may only work on Gecko!!!
19479         
19480         // should we cache this!!!!
19481         
19482         
19483         
19484          
19485         var range = this.createRange(this.getSelection()).cloneRange();
19486         
19487         if (Roo.isIE) {
19488             var parent = range.parentElement();
19489             while (true) {
19490                 var testRange = range.duplicate();
19491                 testRange.moveToElementText(parent);
19492                 if (testRange.inRange(range)) {
19493                     break;
19494                 }
19495                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19496                     break;
19497                 }
19498                 parent = parent.parentElement;
19499             }
19500             return parent;
19501         }
19502         
19503         // is ancestor a text element.
19504         var ac =  range.commonAncestorContainer;
19505         if (ac.nodeType == 3) {
19506             ac = ac.parentNode;
19507         }
19508         
19509         var ar = ac.childNodes;
19510          
19511         var nodes = [];
19512         var other_nodes = [];
19513         var has_other_nodes = false;
19514         for (var i=0;i<ar.length;i++) {
19515             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19516                 continue;
19517             }
19518             // fullly contained node.
19519             
19520             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19521                 nodes.push(ar[i]);
19522                 continue;
19523             }
19524             
19525             // probably selected..
19526             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19527                 other_nodes.push(ar[i]);
19528                 continue;
19529             }
19530             // outer..
19531             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19532                 continue;
19533             }
19534             
19535             
19536             has_other_nodes = true;
19537         }
19538         if (!nodes.length && other_nodes.length) {
19539             nodes= other_nodes;
19540         }
19541         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19542             return false;
19543         }
19544         
19545         return nodes[0];
19546     },
19547     createRange: function(sel)
19548     {
19549         // this has strange effects when using with 
19550         // top toolbar - not sure if it's a great idea.
19551         //this.editor.contentWindow.focus();
19552         if (typeof sel != "undefined") {
19553             try {
19554                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19555             } catch(e) {
19556                 return this.doc.createRange();
19557             }
19558         } else {
19559             return this.doc.createRange();
19560         }
19561     },
19562     getParentElement: function()
19563     {
19564         
19565         this.assignDocWin();
19566         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19567         
19568         var range = this.createRange(sel);
19569          
19570         try {
19571             var p = range.commonAncestorContainer;
19572             while (p.nodeType == 3) { // text node
19573                 p = p.parentNode;
19574             }
19575             return p;
19576         } catch (e) {
19577             return null;
19578         }
19579     
19580     },
19581     /***
19582      *
19583      * Range intersection.. the hard stuff...
19584      *  '-1' = before
19585      *  '0' = hits..
19586      *  '1' = after.
19587      *         [ -- selected range --- ]
19588      *   [fail]                        [fail]
19589      *
19590      *    basically..
19591      *      if end is before start or  hits it. fail.
19592      *      if start is after end or hits it fail.
19593      *
19594      *   if either hits (but other is outside. - then it's not 
19595      *   
19596      *    
19597      **/
19598     
19599     
19600     // @see http://www.thismuchiknow.co.uk/?p=64.
19601     rangeIntersectsNode : function(range, node)
19602     {
19603         var nodeRange = node.ownerDocument.createRange();
19604         try {
19605             nodeRange.selectNode(node);
19606         } catch (e) {
19607             nodeRange.selectNodeContents(node);
19608         }
19609     
19610         var rangeStartRange = range.cloneRange();
19611         rangeStartRange.collapse(true);
19612     
19613         var rangeEndRange = range.cloneRange();
19614         rangeEndRange.collapse(false);
19615     
19616         var nodeStartRange = nodeRange.cloneRange();
19617         nodeStartRange.collapse(true);
19618     
19619         var nodeEndRange = nodeRange.cloneRange();
19620         nodeEndRange.collapse(false);
19621     
19622         return rangeStartRange.compareBoundaryPoints(
19623                  Range.START_TO_START, nodeEndRange) == -1 &&
19624                rangeEndRange.compareBoundaryPoints(
19625                  Range.START_TO_START, nodeStartRange) == 1;
19626         
19627          
19628     },
19629     rangeCompareNode : function(range, node)
19630     {
19631         var nodeRange = node.ownerDocument.createRange();
19632         try {
19633             nodeRange.selectNode(node);
19634         } catch (e) {
19635             nodeRange.selectNodeContents(node);
19636         }
19637         
19638         
19639         range.collapse(true);
19640     
19641         nodeRange.collapse(true);
19642      
19643         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19644         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19645          
19646         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19647         
19648         var nodeIsBefore   =  ss == 1;
19649         var nodeIsAfter    = ee == -1;
19650         
19651         if (nodeIsBefore && nodeIsAfter)
19652             return 0; // outer
19653         if (!nodeIsBefore && nodeIsAfter)
19654             return 1; //right trailed.
19655         
19656         if (nodeIsBefore && !nodeIsAfter)
19657             return 2;  // left trailed.
19658         // fully contined.
19659         return 3;
19660     },
19661
19662     // private? - in a new class?
19663     cleanUpPaste :  function()
19664     {
19665         // cleans up the whole document..
19666         Roo.log('cleanuppaste');
19667         
19668         this.cleanUpChildren(this.doc.body);
19669         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19670         if (clean != this.doc.body.innerHTML) {
19671             this.doc.body.innerHTML = clean;
19672         }
19673         
19674     },
19675     
19676     cleanWordChars : function(input) {// change the chars to hex code
19677         var he = Roo.HtmlEditorCore;
19678         
19679         var output = input;
19680         Roo.each(he.swapCodes, function(sw) { 
19681             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19682             
19683             output = output.replace(swapper, sw[1]);
19684         });
19685         
19686         return output;
19687     },
19688     
19689     
19690     cleanUpChildren : function (n)
19691     {
19692         if (!n.childNodes.length) {
19693             return;
19694         }
19695         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19696            this.cleanUpChild(n.childNodes[i]);
19697         }
19698     },
19699     
19700     
19701         
19702     
19703     cleanUpChild : function (node)
19704     {
19705         var ed = this;
19706         //console.log(node);
19707         if (node.nodeName == "#text") {
19708             // clean up silly Windows -- stuff?
19709             return; 
19710         }
19711         if (node.nodeName == "#comment") {
19712             node.parentNode.removeChild(node);
19713             // clean up silly Windows -- stuff?
19714             return; 
19715         }
19716         var lcname = node.tagName.toLowerCase();
19717         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19718         // whitelist of tags..
19719         
19720         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19721             // remove node.
19722             node.parentNode.removeChild(node);
19723             return;
19724             
19725         }
19726         
19727         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19728         
19729         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19730         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19731         
19732         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19733         //    remove_keep_children = true;
19734         //}
19735         
19736         if (remove_keep_children) {
19737             this.cleanUpChildren(node);
19738             // inserts everything just before this node...
19739             while (node.childNodes.length) {
19740                 var cn = node.childNodes[0];
19741                 node.removeChild(cn);
19742                 node.parentNode.insertBefore(cn, node);
19743             }
19744             node.parentNode.removeChild(node);
19745             return;
19746         }
19747         
19748         if (!node.attributes || !node.attributes.length) {
19749             this.cleanUpChildren(node);
19750             return;
19751         }
19752         
19753         function cleanAttr(n,v)
19754         {
19755             
19756             if (v.match(/^\./) || v.match(/^\//)) {
19757                 return;
19758             }
19759             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19760                 return;
19761             }
19762             if (v.match(/^#/)) {
19763                 return;
19764             }
19765 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19766             node.removeAttribute(n);
19767             
19768         }
19769         
19770         var cwhite = this.cwhite;
19771         var cblack = this.cblack;
19772             
19773         function cleanStyle(n,v)
19774         {
19775             if (v.match(/expression/)) { //XSS?? should we even bother..
19776                 node.removeAttribute(n);
19777                 return;
19778             }
19779             
19780             var parts = v.split(/;/);
19781             var clean = [];
19782             
19783             Roo.each(parts, function(p) {
19784                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19785                 if (!p.length) {
19786                     return true;
19787                 }
19788                 var l = p.split(':').shift().replace(/\s+/g,'');
19789                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19790                 
19791                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19792 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19793                     //node.removeAttribute(n);
19794                     return true;
19795                 }
19796                 //Roo.log()
19797                 // only allow 'c whitelisted system attributes'
19798                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19799 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19800                     //node.removeAttribute(n);
19801                     return true;
19802                 }
19803                 
19804                 
19805                  
19806                 
19807                 clean.push(p);
19808                 return true;
19809             });
19810             if (clean.length) { 
19811                 node.setAttribute(n, clean.join(';'));
19812             } else {
19813                 node.removeAttribute(n);
19814             }
19815             
19816         }
19817         
19818         
19819         for (var i = node.attributes.length-1; i > -1 ; i--) {
19820             var a = node.attributes[i];
19821             //console.log(a);
19822             
19823             if (a.name.toLowerCase().substr(0,2)=='on')  {
19824                 node.removeAttribute(a.name);
19825                 continue;
19826             }
19827             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19828                 node.removeAttribute(a.name);
19829                 continue;
19830             }
19831             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19832                 cleanAttr(a.name,a.value); // fixme..
19833                 continue;
19834             }
19835             if (a.name == 'style') {
19836                 cleanStyle(a.name,a.value);
19837                 continue;
19838             }
19839             /// clean up MS crap..
19840             // tecnically this should be a list of valid class'es..
19841             
19842             
19843             if (a.name == 'class') {
19844                 if (a.value.match(/^Mso/)) {
19845                     node.className = '';
19846                 }
19847                 
19848                 if (a.value.match(/body/)) {
19849                     node.className = '';
19850                 }
19851                 continue;
19852             }
19853             
19854             // style cleanup!?
19855             // class cleanup?
19856             
19857         }
19858         
19859         
19860         this.cleanUpChildren(node);
19861         
19862         
19863     },
19864     
19865     /**
19866      * Clean up MS wordisms...
19867      */
19868     cleanWord : function(node)
19869     {
19870         
19871         
19872         if (!node) {
19873             this.cleanWord(this.doc.body);
19874             return;
19875         }
19876         if (node.nodeName == "#text") {
19877             // clean up silly Windows -- stuff?
19878             return; 
19879         }
19880         if (node.nodeName == "#comment") {
19881             node.parentNode.removeChild(node);
19882             // clean up silly Windows -- stuff?
19883             return; 
19884         }
19885         
19886         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19887             node.parentNode.removeChild(node);
19888             return;
19889         }
19890         
19891         // remove - but keep children..
19892         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19893             while (node.childNodes.length) {
19894                 var cn = node.childNodes[0];
19895                 node.removeChild(cn);
19896                 node.parentNode.insertBefore(cn, node);
19897             }
19898             node.parentNode.removeChild(node);
19899             this.iterateChildren(node, this.cleanWord);
19900             return;
19901         }
19902         // clean styles
19903         if (node.className.length) {
19904             
19905             var cn = node.className.split(/\W+/);
19906             var cna = [];
19907             Roo.each(cn, function(cls) {
19908                 if (cls.match(/Mso[a-zA-Z]+/)) {
19909                     return;
19910                 }
19911                 cna.push(cls);
19912             });
19913             node.className = cna.length ? cna.join(' ') : '';
19914             if (!cna.length) {
19915                 node.removeAttribute("class");
19916             }
19917         }
19918         
19919         if (node.hasAttribute("lang")) {
19920             node.removeAttribute("lang");
19921         }
19922         
19923         if (node.hasAttribute("style")) {
19924             
19925             var styles = node.getAttribute("style").split(";");
19926             var nstyle = [];
19927             Roo.each(styles, function(s) {
19928                 if (!s.match(/:/)) {
19929                     return;
19930                 }
19931                 var kv = s.split(":");
19932                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19933                     return;
19934                 }
19935                 // what ever is left... we allow.
19936                 nstyle.push(s);
19937             });
19938             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19939             if (!nstyle.length) {
19940                 node.removeAttribute('style');
19941             }
19942         }
19943         this.iterateChildren(node, this.cleanWord);
19944         
19945         
19946         
19947     },
19948     /**
19949      * iterateChildren of a Node, calling fn each time, using this as the scole..
19950      * @param {DomNode} node node to iterate children of.
19951      * @param {Function} fn method of this class to call on each item.
19952      */
19953     iterateChildren : function(node, fn)
19954     {
19955         if (!node.childNodes.length) {
19956                 return;
19957         }
19958         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19959            fn.call(this, node.childNodes[i])
19960         }
19961     },
19962     
19963     
19964     /**
19965      * cleanTableWidths.
19966      *
19967      * Quite often pasting from word etc.. results in tables with column and widths.
19968      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19969      *
19970      */
19971     cleanTableWidths : function(node)
19972     {
19973          
19974          
19975         if (!node) {
19976             this.cleanTableWidths(this.doc.body);
19977             return;
19978         }
19979         
19980         // ignore list...
19981         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19982             return; 
19983         }
19984         Roo.log(node.tagName);
19985         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19986             this.iterateChildren(node, this.cleanTableWidths);
19987             return;
19988         }
19989         if (node.hasAttribute('width')) {
19990             node.removeAttribute('width');
19991         }
19992         
19993          
19994         if (node.hasAttribute("style")) {
19995             // pretty basic...
19996             
19997             var styles = node.getAttribute("style").split(";");
19998             var nstyle = [];
19999             Roo.each(styles, function(s) {
20000                 if (!s.match(/:/)) {
20001                     return;
20002                 }
20003                 var kv = s.split(":");
20004                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20005                     return;
20006                 }
20007                 // what ever is left... we allow.
20008                 nstyle.push(s);
20009             });
20010             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20011             if (!nstyle.length) {
20012                 node.removeAttribute('style');
20013             }
20014         }
20015         
20016         this.iterateChildren(node, this.cleanTableWidths);
20017         
20018         
20019     },
20020     
20021     
20022     
20023     
20024     domToHTML : function(currentElement, depth, nopadtext) {
20025         
20026         depth = depth || 0;
20027         nopadtext = nopadtext || false;
20028     
20029         if (!currentElement) {
20030             return this.domToHTML(this.doc.body);
20031         }
20032         
20033         //Roo.log(currentElement);
20034         var j;
20035         var allText = false;
20036         var nodeName = currentElement.nodeName;
20037         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20038         
20039         if  (nodeName == '#text') {
20040             
20041             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20042         }
20043         
20044         
20045         var ret = '';
20046         if (nodeName != 'BODY') {
20047              
20048             var i = 0;
20049             // Prints the node tagName, such as <A>, <IMG>, etc
20050             if (tagName) {
20051                 var attr = [];
20052                 for(i = 0; i < currentElement.attributes.length;i++) {
20053                     // quoting?
20054                     var aname = currentElement.attributes.item(i).name;
20055                     if (!currentElement.attributes.item(i).value.length) {
20056                         continue;
20057                     }
20058                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20059                 }
20060                 
20061                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20062             } 
20063             else {
20064                 
20065                 // eack
20066             }
20067         } else {
20068             tagName = false;
20069         }
20070         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20071             return ret;
20072         }
20073         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20074             nopadtext = true;
20075         }
20076         
20077         
20078         // Traverse the tree
20079         i = 0;
20080         var currentElementChild = currentElement.childNodes.item(i);
20081         var allText = true;
20082         var innerHTML  = '';
20083         lastnode = '';
20084         while (currentElementChild) {
20085             // Formatting code (indent the tree so it looks nice on the screen)
20086             var nopad = nopadtext;
20087             if (lastnode == 'SPAN') {
20088                 nopad  = true;
20089             }
20090             // text
20091             if  (currentElementChild.nodeName == '#text') {
20092                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20093                 toadd = nopadtext ? toadd : toadd.trim();
20094                 if (!nopad && toadd.length > 80) {
20095                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20096                 }
20097                 innerHTML  += toadd;
20098                 
20099                 i++;
20100                 currentElementChild = currentElement.childNodes.item(i);
20101                 lastNode = '';
20102                 continue;
20103             }
20104             allText = false;
20105             
20106             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20107                 
20108             // Recursively traverse the tree structure of the child node
20109             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20110             lastnode = currentElementChild.nodeName;
20111             i++;
20112             currentElementChild=currentElement.childNodes.item(i);
20113         }
20114         
20115         ret += innerHTML;
20116         
20117         if (!allText) {
20118                 // The remaining code is mostly for formatting the tree
20119             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20120         }
20121         
20122         
20123         if (tagName) {
20124             ret+= "</"+tagName+">";
20125         }
20126         return ret;
20127         
20128     },
20129         
20130     applyBlacklists : function()
20131     {
20132         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20133         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20134         
20135         this.white = [];
20136         this.black = [];
20137         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20138             if (b.indexOf(tag) > -1) {
20139                 return;
20140             }
20141             this.white.push(tag);
20142             
20143         }, this);
20144         
20145         Roo.each(w, function(tag) {
20146             if (b.indexOf(tag) > -1) {
20147                 return;
20148             }
20149             if (this.white.indexOf(tag) > -1) {
20150                 return;
20151             }
20152             this.white.push(tag);
20153             
20154         }, this);
20155         
20156         
20157         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20158             if (w.indexOf(tag) > -1) {
20159                 return;
20160             }
20161             this.black.push(tag);
20162             
20163         }, this);
20164         
20165         Roo.each(b, function(tag) {
20166             if (w.indexOf(tag) > -1) {
20167                 return;
20168             }
20169             if (this.black.indexOf(tag) > -1) {
20170                 return;
20171             }
20172             this.black.push(tag);
20173             
20174         }, this);
20175         
20176         
20177         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20178         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20179         
20180         this.cwhite = [];
20181         this.cblack = [];
20182         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20183             if (b.indexOf(tag) > -1) {
20184                 return;
20185             }
20186             this.cwhite.push(tag);
20187             
20188         }, this);
20189         
20190         Roo.each(w, function(tag) {
20191             if (b.indexOf(tag) > -1) {
20192                 return;
20193             }
20194             if (this.cwhite.indexOf(tag) > -1) {
20195                 return;
20196             }
20197             this.cwhite.push(tag);
20198             
20199         }, this);
20200         
20201         
20202         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20203             if (w.indexOf(tag) > -1) {
20204                 return;
20205             }
20206             this.cblack.push(tag);
20207             
20208         }, this);
20209         
20210         Roo.each(b, function(tag) {
20211             if (w.indexOf(tag) > -1) {
20212                 return;
20213             }
20214             if (this.cblack.indexOf(tag) > -1) {
20215                 return;
20216             }
20217             this.cblack.push(tag);
20218             
20219         }, this);
20220     },
20221     
20222     setStylesheets : function(stylesheets)
20223     {
20224         if(typeof(stylesheets) == 'string'){
20225             Roo.get(this.iframe.contentDocument.head).createChild({
20226                 tag : 'link',
20227                 rel : 'stylesheet',
20228                 type : 'text/css',
20229                 href : stylesheets
20230             });
20231             
20232             return;
20233         }
20234         var _this = this;
20235      
20236         Roo.each(stylesheets, function(s) {
20237             if(!s.length){
20238                 return;
20239             }
20240             
20241             Roo.get(_this.iframe.contentDocument.head).createChild({
20242                 tag : 'link',
20243                 rel : 'stylesheet',
20244                 type : 'text/css',
20245                 href : s
20246             });
20247         });
20248
20249         
20250     },
20251     
20252     removeStylesheets : function()
20253     {
20254         var _this = this;
20255         
20256         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20257             s.remove();
20258         });
20259     }
20260     
20261     // hide stuff that is not compatible
20262     /**
20263      * @event blur
20264      * @hide
20265      */
20266     /**
20267      * @event change
20268      * @hide
20269      */
20270     /**
20271      * @event focus
20272      * @hide
20273      */
20274     /**
20275      * @event specialkey
20276      * @hide
20277      */
20278     /**
20279      * @cfg {String} fieldClass @hide
20280      */
20281     /**
20282      * @cfg {String} focusClass @hide
20283      */
20284     /**
20285      * @cfg {String} autoCreate @hide
20286      */
20287     /**
20288      * @cfg {String} inputType @hide
20289      */
20290     /**
20291      * @cfg {String} invalidClass @hide
20292      */
20293     /**
20294      * @cfg {String} invalidText @hide
20295      */
20296     /**
20297      * @cfg {String} msgFx @hide
20298      */
20299     /**
20300      * @cfg {String} validateOnBlur @hide
20301      */
20302 });
20303
20304 Roo.HtmlEditorCore.white = [
20305         'area', 'br', 'img', 'input', 'hr', 'wbr',
20306         
20307        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20308        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20309        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20310        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20311        'table',   'ul',         'xmp', 
20312        
20313        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20314       'thead',   'tr', 
20315      
20316       'dir', 'menu', 'ol', 'ul', 'dl',
20317        
20318       'embed',  'object'
20319 ];
20320
20321
20322 Roo.HtmlEditorCore.black = [
20323     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20324         'applet', // 
20325         'base',   'basefont', 'bgsound', 'blink',  'body', 
20326         'frame',  'frameset', 'head',    'html',   'ilayer', 
20327         'iframe', 'layer',  'link',     'meta',    'object',   
20328         'script', 'style' ,'title',  'xml' // clean later..
20329 ];
20330 Roo.HtmlEditorCore.clean = [
20331     'script', 'style', 'title', 'xml'
20332 ];
20333 Roo.HtmlEditorCore.remove = [
20334     'font'
20335 ];
20336 // attributes..
20337
20338 Roo.HtmlEditorCore.ablack = [
20339     'on'
20340 ];
20341     
20342 Roo.HtmlEditorCore.aclean = [ 
20343     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20344 ];
20345
20346 // protocols..
20347 Roo.HtmlEditorCore.pwhite= [
20348         'http',  'https',  'mailto'
20349 ];
20350
20351 // white listed style attributes.
20352 Roo.HtmlEditorCore.cwhite= [
20353       //  'text-align', /// default is to allow most things..
20354       
20355          
20356 //        'font-size'//??
20357 ];
20358
20359 // black listed style attributes.
20360 Roo.HtmlEditorCore.cblack= [
20361       //  'font-size' -- this can be set by the project 
20362 ];
20363
20364
20365 Roo.HtmlEditorCore.swapCodes   =[ 
20366     [    8211, "--" ], 
20367     [    8212, "--" ], 
20368     [    8216,  "'" ],  
20369     [    8217, "'" ],  
20370     [    8220, '"' ],  
20371     [    8221, '"' ],  
20372     [    8226, "*" ],  
20373     [    8230, "..." ]
20374 ]; 
20375
20376     /*
20377  * - LGPL
20378  *
20379  * HtmlEditor
20380  * 
20381  */
20382
20383 /**
20384  * @class Roo.bootstrap.HtmlEditor
20385  * @extends Roo.bootstrap.TextArea
20386  * Bootstrap HtmlEditor class
20387
20388  * @constructor
20389  * Create a new HtmlEditor
20390  * @param {Object} config The config object
20391  */
20392
20393 Roo.bootstrap.HtmlEditor = function(config){
20394     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20395     if (!this.toolbars) {
20396         this.toolbars = [];
20397     }
20398     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20399     this.addEvents({
20400             /**
20401              * @event initialize
20402              * Fires when the editor is fully initialized (including the iframe)
20403              * @param {HtmlEditor} this
20404              */
20405             initialize: true,
20406             /**
20407              * @event activate
20408              * Fires when the editor is first receives the focus. Any insertion must wait
20409              * until after this event.
20410              * @param {HtmlEditor} this
20411              */
20412             activate: true,
20413              /**
20414              * @event beforesync
20415              * Fires before the textarea is updated with content from the editor iframe. Return false
20416              * to cancel the sync.
20417              * @param {HtmlEditor} this
20418              * @param {String} html
20419              */
20420             beforesync: true,
20421              /**
20422              * @event beforepush
20423              * Fires before the iframe editor is updated with content from the textarea. Return false
20424              * to cancel the push.
20425              * @param {HtmlEditor} this
20426              * @param {String} html
20427              */
20428             beforepush: true,
20429              /**
20430              * @event sync
20431              * Fires when the textarea is updated with content from the editor iframe.
20432              * @param {HtmlEditor} this
20433              * @param {String} html
20434              */
20435             sync: true,
20436              /**
20437              * @event push
20438              * Fires when the iframe editor is updated with content from the textarea.
20439              * @param {HtmlEditor} this
20440              * @param {String} html
20441              */
20442             push: true,
20443              /**
20444              * @event editmodechange
20445              * Fires when the editor switches edit modes
20446              * @param {HtmlEditor} this
20447              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20448              */
20449             editmodechange: true,
20450             /**
20451              * @event editorevent
20452              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20453              * @param {HtmlEditor} this
20454              */
20455             editorevent: true,
20456             /**
20457              * @event firstfocus
20458              * Fires when on first focus - needed by toolbars..
20459              * @param {HtmlEditor} this
20460              */
20461             firstfocus: true,
20462             /**
20463              * @event autosave
20464              * Auto save the htmlEditor value as a file into Events
20465              * @param {HtmlEditor} this
20466              */
20467             autosave: true,
20468             /**
20469              * @event savedpreview
20470              * preview the saved version of htmlEditor
20471              * @param {HtmlEditor} this
20472              */
20473             savedpreview: true
20474         });
20475 };
20476
20477
20478 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20479     
20480     
20481       /**
20482      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20483      */
20484     toolbars : false,
20485    
20486      /**
20487      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20488      *                        Roo.resizable.
20489      */
20490     resizable : false,
20491      /**
20492      * @cfg {Number} height (in pixels)
20493      */   
20494     height: 300,
20495    /**
20496      * @cfg {Number} width (in pixels)
20497      */   
20498     width: false,
20499     
20500     /**
20501      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20502      * 
20503      */
20504     stylesheets: false,
20505     
20506     // id of frame..
20507     frameId: false,
20508     
20509     // private properties
20510     validationEvent : false,
20511     deferHeight: true,
20512     initialized : false,
20513     activated : false,
20514     
20515     onFocus : Roo.emptyFn,
20516     iframePad:3,
20517     hideMode:'offsets',
20518     
20519     
20520     tbContainer : false,
20521     
20522     toolbarContainer :function() {
20523         return this.wrap.select('.x-html-editor-tb',true).first();
20524     },
20525
20526     /**
20527      * Protected method that will not generally be called directly. It
20528      * is called when the editor creates its toolbar. Override this method if you need to
20529      * add custom toolbar buttons.
20530      * @param {HtmlEditor} editor
20531      */
20532     createToolbar : function(){
20533         
20534         Roo.log("create toolbars");
20535         
20536         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20537         this.toolbars[0].render(this.toolbarContainer());
20538         
20539         return;
20540         
20541 //        if (!editor.toolbars || !editor.toolbars.length) {
20542 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20543 //        }
20544 //        
20545 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20546 //            editor.toolbars[i] = Roo.factory(
20547 //                    typeof(editor.toolbars[i]) == 'string' ?
20548 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20549 //                Roo.bootstrap.HtmlEditor);
20550 //            editor.toolbars[i].init(editor);
20551 //        }
20552     },
20553
20554      
20555     // private
20556     onRender : function(ct, position)
20557     {
20558        // Roo.log("Call onRender: " + this.xtype);
20559         var _t = this;
20560         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20561       
20562         this.wrap = this.inputEl().wrap({
20563             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20564         });
20565         
20566         this.editorcore.onRender(ct, position);
20567          
20568         if (this.resizable) {
20569             this.resizeEl = new Roo.Resizable(this.wrap, {
20570                 pinned : true,
20571                 wrap: true,
20572                 dynamic : true,
20573                 minHeight : this.height,
20574                 height: this.height,
20575                 handles : this.resizable,
20576                 width: this.width,
20577                 listeners : {
20578                     resize : function(r, w, h) {
20579                         _t.onResize(w,h); // -something
20580                     }
20581                 }
20582             });
20583             
20584         }
20585         this.createToolbar(this);
20586        
20587         
20588         if(!this.width && this.resizable){
20589             this.setSize(this.wrap.getSize());
20590         }
20591         if (this.resizeEl) {
20592             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20593             // should trigger onReize..
20594         }
20595         
20596     },
20597
20598     // private
20599     onResize : function(w, h)
20600     {
20601         Roo.log('resize: ' +w + ',' + h );
20602         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20603         var ew = false;
20604         var eh = false;
20605         
20606         if(this.inputEl() ){
20607             if(typeof w == 'number'){
20608                 var aw = w - this.wrap.getFrameWidth('lr');
20609                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20610                 ew = aw;
20611             }
20612             if(typeof h == 'number'){
20613                  var tbh = -11;  // fixme it needs to tool bar size!
20614                 for (var i =0; i < this.toolbars.length;i++) {
20615                     // fixme - ask toolbars for heights?
20616                     tbh += this.toolbars[i].el.getHeight();
20617                     //if (this.toolbars[i].footer) {
20618                     //    tbh += this.toolbars[i].footer.el.getHeight();
20619                     //}
20620                 }
20621               
20622                 
20623                 
20624                 
20625                 
20626                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20627                 ah -= 5; // knock a few pixes off for look..
20628                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20629                 var eh = ah;
20630             }
20631         }
20632         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20633         this.editorcore.onResize(ew,eh);
20634         
20635     },
20636
20637     /**
20638      * Toggles the editor between standard and source edit mode.
20639      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20640      */
20641     toggleSourceEdit : function(sourceEditMode)
20642     {
20643         this.editorcore.toggleSourceEdit(sourceEditMode);
20644         
20645         if(this.editorcore.sourceEditMode){
20646             Roo.log('editor - showing textarea');
20647             
20648 //            Roo.log('in');
20649 //            Roo.log(this.syncValue());
20650             this.syncValue();
20651             this.inputEl().removeClass(['hide', 'x-hidden']);
20652             this.inputEl().dom.removeAttribute('tabIndex');
20653             this.inputEl().focus();
20654         }else{
20655             Roo.log('editor - hiding textarea');
20656 //            Roo.log('out')
20657 //            Roo.log(this.pushValue()); 
20658             this.pushValue();
20659             
20660             this.inputEl().addClass(['hide', 'x-hidden']);
20661             this.inputEl().dom.setAttribute('tabIndex', -1);
20662             //this.deferFocus();
20663         }
20664          
20665         if(this.resizable){
20666             this.setSize(this.wrap.getSize());
20667         }
20668         
20669         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20670     },
20671  
20672     // private (for BoxComponent)
20673     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20674
20675     // private (for BoxComponent)
20676     getResizeEl : function(){
20677         return this.wrap;
20678     },
20679
20680     // private (for BoxComponent)
20681     getPositionEl : function(){
20682         return this.wrap;
20683     },
20684
20685     // private
20686     initEvents : function(){
20687         this.originalValue = this.getValue();
20688     },
20689
20690 //    /**
20691 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20692 //     * @method
20693 //     */
20694 //    markInvalid : Roo.emptyFn,
20695 //    /**
20696 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20697 //     * @method
20698 //     */
20699 //    clearInvalid : Roo.emptyFn,
20700
20701     setValue : function(v){
20702         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20703         this.editorcore.pushValue();
20704     },
20705
20706      
20707     // private
20708     deferFocus : function(){
20709         this.focus.defer(10, this);
20710     },
20711
20712     // doc'ed in Field
20713     focus : function(){
20714         this.editorcore.focus();
20715         
20716     },
20717       
20718
20719     // private
20720     onDestroy : function(){
20721         
20722         
20723         
20724         if(this.rendered){
20725             
20726             for (var i =0; i < this.toolbars.length;i++) {
20727                 // fixme - ask toolbars for heights?
20728                 this.toolbars[i].onDestroy();
20729             }
20730             
20731             this.wrap.dom.innerHTML = '';
20732             this.wrap.remove();
20733         }
20734     },
20735
20736     // private
20737     onFirstFocus : function(){
20738         //Roo.log("onFirstFocus");
20739         this.editorcore.onFirstFocus();
20740          for (var i =0; i < this.toolbars.length;i++) {
20741             this.toolbars[i].onFirstFocus();
20742         }
20743         
20744     },
20745     
20746     // private
20747     syncValue : function()
20748     {   
20749         this.editorcore.syncValue();
20750     },
20751     
20752     pushValue : function()
20753     {   
20754         this.editorcore.pushValue();
20755     }
20756      
20757     
20758     // hide stuff that is not compatible
20759     /**
20760      * @event blur
20761      * @hide
20762      */
20763     /**
20764      * @event change
20765      * @hide
20766      */
20767     /**
20768      * @event focus
20769      * @hide
20770      */
20771     /**
20772      * @event specialkey
20773      * @hide
20774      */
20775     /**
20776      * @cfg {String} fieldClass @hide
20777      */
20778     /**
20779      * @cfg {String} focusClass @hide
20780      */
20781     /**
20782      * @cfg {String} autoCreate @hide
20783      */
20784     /**
20785      * @cfg {String} inputType @hide
20786      */
20787     /**
20788      * @cfg {String} invalidClass @hide
20789      */
20790     /**
20791      * @cfg {String} invalidText @hide
20792      */
20793     /**
20794      * @cfg {String} msgFx @hide
20795      */
20796     /**
20797      * @cfg {String} validateOnBlur @hide
20798      */
20799 });
20800  
20801     
20802    
20803    
20804    
20805       
20806 Roo.namespace('Roo.bootstrap.htmleditor');
20807 /**
20808  * @class Roo.bootstrap.HtmlEditorToolbar1
20809  * Basic Toolbar
20810  * 
20811  * Usage:
20812  *
20813  new Roo.bootstrap.HtmlEditor({
20814     ....
20815     toolbars : [
20816         new Roo.bootstrap.HtmlEditorToolbar1({
20817             disable : { fonts: 1 , format: 1, ..., ... , ...],
20818             btns : [ .... ]
20819         })
20820     }
20821      
20822  * 
20823  * @cfg {Object} disable List of elements to disable..
20824  * @cfg {Array} btns List of additional buttons.
20825  * 
20826  * 
20827  * NEEDS Extra CSS? 
20828  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20829  */
20830  
20831 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20832 {
20833     
20834     Roo.apply(this, config);
20835     
20836     // default disabled, based on 'good practice'..
20837     this.disable = this.disable || {};
20838     Roo.applyIf(this.disable, {
20839         fontSize : true,
20840         colors : true,
20841         specialElements : true
20842     });
20843     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20844     
20845     this.editor = config.editor;
20846     this.editorcore = config.editor.editorcore;
20847     
20848     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20849     
20850     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20851     // dont call parent... till later.
20852 }
20853 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20854      
20855     bar : true,
20856     
20857     editor : false,
20858     editorcore : false,
20859     
20860     
20861     formats : [
20862         "p" ,  
20863         "h1","h2","h3","h4","h5","h6", 
20864         "pre", "code", 
20865         "abbr", "acronym", "address", "cite", "samp", "var",
20866         'div','span'
20867     ],
20868     
20869     onRender : function(ct, position)
20870     {
20871        // Roo.log("Call onRender: " + this.xtype);
20872         
20873        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20874        Roo.log(this.el);
20875        this.el.dom.style.marginBottom = '0';
20876        var _this = this;
20877        var editorcore = this.editorcore;
20878        var editor= this.editor;
20879        
20880        var children = [];
20881        var btn = function(id,cmd , toggle, handler){
20882        
20883             var  event = toggle ? 'toggle' : 'click';
20884        
20885             var a = {
20886                 size : 'sm',
20887                 xtype: 'Button',
20888                 xns: Roo.bootstrap,
20889                 glyphicon : id,
20890                 cmd : id || cmd,
20891                 enableToggle:toggle !== false,
20892                 //html : 'submit'
20893                 pressed : toggle ? false : null,
20894                 listeners : {}
20895             }
20896             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20897                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20898             }
20899             children.push(a);
20900             return a;
20901        }
20902         
20903         var style = {
20904                 xtype: 'Button',
20905                 size : 'sm',
20906                 xns: Roo.bootstrap,
20907                 glyphicon : 'font',
20908                 //html : 'submit'
20909                 menu : {
20910                     xtype: 'Menu',
20911                     xns: Roo.bootstrap,
20912                     items:  []
20913                 }
20914         };
20915         Roo.each(this.formats, function(f) {
20916             style.menu.items.push({
20917                 xtype :'MenuItem',
20918                 xns: Roo.bootstrap,
20919                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20920                 tagname : f,
20921                 listeners : {
20922                     click : function()
20923                     {
20924                         editorcore.insertTag(this.tagname);
20925                         editor.focus();
20926                     }
20927                 }
20928                 
20929             });
20930         });
20931          children.push(style);   
20932             
20933             
20934         btn('bold',false,true);
20935         btn('italic',false,true);
20936         btn('align-left', 'justifyleft',true);
20937         btn('align-center', 'justifycenter',true);
20938         btn('align-right' , 'justifyright',true);
20939         btn('link', false, false, function(btn) {
20940             //Roo.log("create link?");
20941             var url = prompt(this.createLinkText, this.defaultLinkValue);
20942             if(url && url != 'http:/'+'/'){
20943                 this.editorcore.relayCmd('createlink', url);
20944             }
20945         }),
20946         btn('list','insertunorderedlist',true);
20947         btn('pencil', false,true, function(btn){
20948                 Roo.log(this);
20949                 
20950                 this.toggleSourceEdit(btn.pressed);
20951         });
20952         /*
20953         var cog = {
20954                 xtype: 'Button',
20955                 size : 'sm',
20956                 xns: Roo.bootstrap,
20957                 glyphicon : 'cog',
20958                 //html : 'submit'
20959                 menu : {
20960                     xtype: 'Menu',
20961                     xns: Roo.bootstrap,
20962                     items:  []
20963                 }
20964         };
20965         
20966         cog.menu.items.push({
20967             xtype :'MenuItem',
20968             xns: Roo.bootstrap,
20969             html : Clean styles,
20970             tagname : f,
20971             listeners : {
20972                 click : function()
20973                 {
20974                     editorcore.insertTag(this.tagname);
20975                     editor.focus();
20976                 }
20977             }
20978             
20979         });
20980        */
20981         
20982          
20983        this.xtype = 'NavSimplebar';
20984         
20985         for(var i=0;i< children.length;i++) {
20986             
20987             this.buttons.add(this.addxtypeChild(children[i]));
20988             
20989         }
20990         
20991         editor.on('editorevent', this.updateToolbar, this);
20992     },
20993     onBtnClick : function(id)
20994     {
20995        this.editorcore.relayCmd(id);
20996        this.editorcore.focus();
20997     },
20998     
20999     /**
21000      * Protected method that will not generally be called directly. It triggers
21001      * a toolbar update by reading the markup state of the current selection in the editor.
21002      */
21003     updateToolbar: function(){
21004
21005         if(!this.editorcore.activated){
21006             this.editor.onFirstFocus(); // is this neeed?
21007             return;
21008         }
21009
21010         var btns = this.buttons; 
21011         var doc = this.editorcore.doc;
21012         btns.get('bold').setActive(doc.queryCommandState('bold'));
21013         btns.get('italic').setActive(doc.queryCommandState('italic'));
21014         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21015         
21016         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21017         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21018         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21019         
21020         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21021         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21022          /*
21023         
21024         var ans = this.editorcore.getAllAncestors();
21025         if (this.formatCombo) {
21026             
21027             
21028             var store = this.formatCombo.store;
21029             this.formatCombo.setValue("");
21030             for (var i =0; i < ans.length;i++) {
21031                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21032                     // select it..
21033                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21034                     break;
21035                 }
21036             }
21037         }
21038         
21039         
21040         
21041         // hides menus... - so this cant be on a menu...
21042         Roo.bootstrap.MenuMgr.hideAll();
21043         */
21044         Roo.bootstrap.MenuMgr.hideAll();
21045         //this.editorsyncValue();
21046     },
21047     onFirstFocus: function() {
21048         this.buttons.each(function(item){
21049            item.enable();
21050         });
21051     },
21052     toggleSourceEdit : function(sourceEditMode){
21053         
21054           
21055         if(sourceEditMode){
21056             Roo.log("disabling buttons");
21057            this.buttons.each( function(item){
21058                 if(item.cmd != 'pencil'){
21059                     item.disable();
21060                 }
21061             });
21062           
21063         }else{
21064             Roo.log("enabling buttons");
21065             if(this.editorcore.initialized){
21066                 this.buttons.each( function(item){
21067                     item.enable();
21068                 });
21069             }
21070             
21071         }
21072         Roo.log("calling toggole on editor");
21073         // tell the editor that it's been pressed..
21074         this.editor.toggleSourceEdit(sourceEditMode);
21075        
21076     }
21077 });
21078
21079
21080
21081
21082
21083 /**
21084  * @class Roo.bootstrap.Table.AbstractSelectionModel
21085  * @extends Roo.util.Observable
21086  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21087  * implemented by descendant classes.  This class should not be directly instantiated.
21088  * @constructor
21089  */
21090 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21091     this.locked = false;
21092     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21093 };
21094
21095
21096 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21097     /** @ignore Called by the grid automatically. Do not call directly. */
21098     init : function(grid){
21099         this.grid = grid;
21100         this.initEvents();
21101     },
21102
21103     /**
21104      * Locks the selections.
21105      */
21106     lock : function(){
21107         this.locked = true;
21108     },
21109
21110     /**
21111      * Unlocks the selections.
21112      */
21113     unlock : function(){
21114         this.locked = false;
21115     },
21116
21117     /**
21118      * Returns true if the selections are locked.
21119      * @return {Boolean}
21120      */
21121     isLocked : function(){
21122         return this.locked;
21123     }
21124 });
21125 /**
21126  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21127  * @class Roo.bootstrap.Table.RowSelectionModel
21128  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21129  * It supports multiple selections and keyboard selection/navigation. 
21130  * @constructor
21131  * @param {Object} config
21132  */
21133
21134 Roo.bootstrap.Table.RowSelectionModel = function(config){
21135     Roo.apply(this, config);
21136     this.selections = new Roo.util.MixedCollection(false, function(o){
21137         return o.id;
21138     });
21139
21140     this.last = false;
21141     this.lastActive = false;
21142
21143     this.addEvents({
21144         /**
21145              * @event selectionchange
21146              * Fires when the selection changes
21147              * @param {SelectionModel} this
21148              */
21149             "selectionchange" : true,
21150         /**
21151              * @event afterselectionchange
21152              * Fires after the selection changes (eg. by key press or clicking)
21153              * @param {SelectionModel} this
21154              */
21155             "afterselectionchange" : true,
21156         /**
21157              * @event beforerowselect
21158              * Fires when a row is selected being selected, return false to cancel.
21159              * @param {SelectionModel} this
21160              * @param {Number} rowIndex The selected index
21161              * @param {Boolean} keepExisting False if other selections will be cleared
21162              */
21163             "beforerowselect" : true,
21164         /**
21165              * @event rowselect
21166              * Fires when a row is selected.
21167              * @param {SelectionModel} this
21168              * @param {Number} rowIndex The selected index
21169              * @param {Roo.data.Record} r The record
21170              */
21171             "rowselect" : true,
21172         /**
21173              * @event rowdeselect
21174              * Fires when a row is deselected.
21175              * @param {SelectionModel} this
21176              * @param {Number} rowIndex The selected index
21177              */
21178         "rowdeselect" : true
21179     });
21180     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21181     this.locked = false;
21182 };
21183
21184 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21185     /**
21186      * @cfg {Boolean} singleSelect
21187      * True to allow selection of only one row at a time (defaults to false)
21188      */
21189     singleSelect : false,
21190
21191     // private
21192     initEvents : function(){
21193
21194         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21195             this.grid.on("mousedown", this.handleMouseDown, this);
21196         }else{ // allow click to work like normal
21197             this.grid.on("rowclick", this.handleDragableRowClick, this);
21198         }
21199
21200         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21201             "up" : function(e){
21202                 if(!e.shiftKey){
21203                     this.selectPrevious(e.shiftKey);
21204                 }else if(this.last !== false && this.lastActive !== false){
21205                     var last = this.last;
21206                     this.selectRange(this.last,  this.lastActive-1);
21207                     this.grid.getView().focusRow(this.lastActive);
21208                     if(last !== false){
21209                         this.last = last;
21210                     }
21211                 }else{
21212                     this.selectFirstRow();
21213                 }
21214                 this.fireEvent("afterselectionchange", this);
21215             },
21216             "down" : function(e){
21217                 if(!e.shiftKey){
21218                     this.selectNext(e.shiftKey);
21219                 }else if(this.last !== false && this.lastActive !== false){
21220                     var last = this.last;
21221                     this.selectRange(this.last,  this.lastActive+1);
21222                     this.grid.getView().focusRow(this.lastActive);
21223                     if(last !== false){
21224                         this.last = last;
21225                     }
21226                 }else{
21227                     this.selectFirstRow();
21228                 }
21229                 this.fireEvent("afterselectionchange", this);
21230             },
21231             scope: this
21232         });
21233
21234         var view = this.grid.view;
21235         view.on("refresh", this.onRefresh, this);
21236         view.on("rowupdated", this.onRowUpdated, this);
21237         view.on("rowremoved", this.onRemove, this);
21238     },
21239
21240     // private
21241     onRefresh : function(){
21242         var ds = this.grid.dataSource, i, v = this.grid.view;
21243         var s = this.selections;
21244         s.each(function(r){
21245             if((i = ds.indexOfId(r.id)) != -1){
21246                 v.onRowSelect(i);
21247             }else{
21248                 s.remove(r);
21249             }
21250         });
21251     },
21252
21253     // private
21254     onRemove : function(v, index, r){
21255         this.selections.remove(r);
21256     },
21257
21258     // private
21259     onRowUpdated : function(v, index, r){
21260         if(this.isSelected(r)){
21261             v.onRowSelect(index);
21262         }
21263     },
21264
21265     /**
21266      * Select records.
21267      * @param {Array} records The records to select
21268      * @param {Boolean} keepExisting (optional) True to keep existing selections
21269      */
21270     selectRecords : function(records, keepExisting){
21271         if(!keepExisting){
21272             this.clearSelections();
21273         }
21274         var ds = this.grid.dataSource;
21275         for(var i = 0, len = records.length; i < len; i++){
21276             this.selectRow(ds.indexOf(records[i]), true);
21277         }
21278     },
21279
21280     /**
21281      * Gets the number of selected rows.
21282      * @return {Number}
21283      */
21284     getCount : function(){
21285         return this.selections.length;
21286     },
21287
21288     /**
21289      * Selects the first row in the grid.
21290      */
21291     selectFirstRow : function(){
21292         this.selectRow(0);
21293     },
21294
21295     /**
21296      * Select the last row.
21297      * @param {Boolean} keepExisting (optional) True to keep existing selections
21298      */
21299     selectLastRow : function(keepExisting){
21300         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21301     },
21302
21303     /**
21304      * Selects the row immediately following the last selected row.
21305      * @param {Boolean} keepExisting (optional) True to keep existing selections
21306      */
21307     selectNext : function(keepExisting){
21308         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21309             this.selectRow(this.last+1, keepExisting);
21310             this.grid.getView().focusRow(this.last);
21311         }
21312     },
21313
21314     /**
21315      * Selects the row that precedes the last selected row.
21316      * @param {Boolean} keepExisting (optional) True to keep existing selections
21317      */
21318     selectPrevious : function(keepExisting){
21319         if(this.last){
21320             this.selectRow(this.last-1, keepExisting);
21321             this.grid.getView().focusRow(this.last);
21322         }
21323     },
21324
21325     /**
21326      * Returns the selected records
21327      * @return {Array} Array of selected records
21328      */
21329     getSelections : function(){
21330         return [].concat(this.selections.items);
21331     },
21332
21333     /**
21334      * Returns the first selected record.
21335      * @return {Record}
21336      */
21337     getSelected : function(){
21338         return this.selections.itemAt(0);
21339     },
21340
21341
21342     /**
21343      * Clears all selections.
21344      */
21345     clearSelections : function(fast){
21346         if(this.locked) return;
21347         if(fast !== true){
21348             var ds = this.grid.dataSource;
21349             var s = this.selections;
21350             s.each(function(r){
21351                 this.deselectRow(ds.indexOfId(r.id));
21352             }, this);
21353             s.clear();
21354         }else{
21355             this.selections.clear();
21356         }
21357         this.last = false;
21358     },
21359
21360
21361     /**
21362      * Selects all rows.
21363      */
21364     selectAll : function(){
21365         if(this.locked) return;
21366         this.selections.clear();
21367         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21368             this.selectRow(i, true);
21369         }
21370     },
21371
21372     /**
21373      * Returns True if there is a selection.
21374      * @return {Boolean}
21375      */
21376     hasSelection : function(){
21377         return this.selections.length > 0;
21378     },
21379
21380     /**
21381      * Returns True if the specified row is selected.
21382      * @param {Number/Record} record The record or index of the record to check
21383      * @return {Boolean}
21384      */
21385     isSelected : function(index){
21386         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21387         return (r && this.selections.key(r.id) ? true : false);
21388     },
21389
21390     /**
21391      * Returns True if the specified record id is selected.
21392      * @param {String} id The id of record to check
21393      * @return {Boolean}
21394      */
21395     isIdSelected : function(id){
21396         return (this.selections.key(id) ? true : false);
21397     },
21398
21399     // private
21400     handleMouseDown : function(e, t){
21401         var view = this.grid.getView(), rowIndex;
21402         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21403             return;
21404         };
21405         if(e.shiftKey && this.last !== false){
21406             var last = this.last;
21407             this.selectRange(last, rowIndex, e.ctrlKey);
21408             this.last = last; // reset the last
21409             view.focusRow(rowIndex);
21410         }else{
21411             var isSelected = this.isSelected(rowIndex);
21412             if(e.button !== 0 && isSelected){
21413                 view.focusRow(rowIndex);
21414             }else if(e.ctrlKey && isSelected){
21415                 this.deselectRow(rowIndex);
21416             }else if(!isSelected){
21417                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21418                 view.focusRow(rowIndex);
21419             }
21420         }
21421         this.fireEvent("afterselectionchange", this);
21422     },
21423     // private
21424     handleDragableRowClick :  function(grid, rowIndex, e) 
21425     {
21426         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21427             this.selectRow(rowIndex, false);
21428             grid.view.focusRow(rowIndex);
21429              this.fireEvent("afterselectionchange", this);
21430         }
21431     },
21432     
21433     /**
21434      * Selects multiple rows.
21435      * @param {Array} rows Array of the indexes of the row to select
21436      * @param {Boolean} keepExisting (optional) True to keep existing selections
21437      */
21438     selectRows : function(rows, keepExisting){
21439         if(!keepExisting){
21440             this.clearSelections();
21441         }
21442         for(var i = 0, len = rows.length; i < len; i++){
21443             this.selectRow(rows[i], true);
21444         }
21445     },
21446
21447     /**
21448      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21449      * @param {Number} startRow The index of the first row in the range
21450      * @param {Number} endRow The index of the last row in the range
21451      * @param {Boolean} keepExisting (optional) True to retain existing selections
21452      */
21453     selectRange : function(startRow, endRow, keepExisting){
21454         if(this.locked) return;
21455         if(!keepExisting){
21456             this.clearSelections();
21457         }
21458         if(startRow <= endRow){
21459             for(var i = startRow; i <= endRow; i++){
21460                 this.selectRow(i, true);
21461             }
21462         }else{
21463             for(var i = startRow; i >= endRow; i--){
21464                 this.selectRow(i, true);
21465             }
21466         }
21467     },
21468
21469     /**
21470      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21471      * @param {Number} startRow The index of the first row in the range
21472      * @param {Number} endRow The index of the last row in the range
21473      */
21474     deselectRange : function(startRow, endRow, preventViewNotify){
21475         if(this.locked) return;
21476         for(var i = startRow; i <= endRow; i++){
21477             this.deselectRow(i, preventViewNotify);
21478         }
21479     },
21480
21481     /**
21482      * Selects a row.
21483      * @param {Number} row The index of the row to select
21484      * @param {Boolean} keepExisting (optional) True to keep existing selections
21485      */
21486     selectRow : function(index, keepExisting, preventViewNotify){
21487         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21488         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21489             if(!keepExisting || this.singleSelect){
21490                 this.clearSelections();
21491             }
21492             var r = this.grid.dataSource.getAt(index);
21493             this.selections.add(r);
21494             this.last = this.lastActive = index;
21495             if(!preventViewNotify){
21496                 this.grid.getView().onRowSelect(index);
21497             }
21498             this.fireEvent("rowselect", this, index, r);
21499             this.fireEvent("selectionchange", this);
21500         }
21501     },
21502
21503     /**
21504      * Deselects a row.
21505      * @param {Number} row The index of the row to deselect
21506      */
21507     deselectRow : function(index, preventViewNotify){
21508         if(this.locked) return;
21509         if(this.last == index){
21510             this.last = false;
21511         }
21512         if(this.lastActive == index){
21513             this.lastActive = false;
21514         }
21515         var r = this.grid.dataSource.getAt(index);
21516         this.selections.remove(r);
21517         if(!preventViewNotify){
21518             this.grid.getView().onRowDeselect(index);
21519         }
21520         this.fireEvent("rowdeselect", this, index);
21521         this.fireEvent("selectionchange", this);
21522     },
21523
21524     // private
21525     restoreLast : function(){
21526         if(this._last){
21527             this.last = this._last;
21528         }
21529     },
21530
21531     // private
21532     acceptsNav : function(row, col, cm){
21533         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21534     },
21535
21536     // private
21537     onEditorKey : function(field, e){
21538         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21539         if(k == e.TAB){
21540             e.stopEvent();
21541             ed.completeEdit();
21542             if(e.shiftKey){
21543                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21544             }else{
21545                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21546             }
21547         }else if(k == e.ENTER && !e.ctrlKey){
21548             e.stopEvent();
21549             ed.completeEdit();
21550             if(e.shiftKey){
21551                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21552             }else{
21553                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21554             }
21555         }else if(k == e.ESC){
21556             ed.cancelEdit();
21557         }
21558         if(newCell){
21559             g.startEditing(newCell[0], newCell[1]);
21560         }
21561     }
21562 });/*
21563  * Based on:
21564  * Ext JS Library 1.1.1
21565  * Copyright(c) 2006-2007, Ext JS, LLC.
21566  *
21567  * Originally Released Under LGPL - original licence link has changed is not relivant.
21568  *
21569  * Fork - LGPL
21570  * <script type="text/javascript">
21571  */
21572  
21573 /**
21574  * @class Roo.bootstrap.PagingToolbar
21575  * @extends Roo.Row
21576  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21577  * @constructor
21578  * Create a new PagingToolbar
21579  * @param {Object} config The config object
21580  */
21581 Roo.bootstrap.PagingToolbar = function(config)
21582 {
21583     // old args format still supported... - xtype is prefered..
21584         // created from xtype...
21585     var ds = config.dataSource;
21586     this.toolbarItems = [];
21587     if (config.items) {
21588         this.toolbarItems = config.items;
21589 //        config.items = [];
21590     }
21591     
21592     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21593     this.ds = ds;
21594     this.cursor = 0;
21595     if (ds) { 
21596         this.bind(ds);
21597     }
21598     
21599     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21600     
21601 };
21602
21603 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21604     /**
21605      * @cfg {Roo.data.Store} dataSource
21606      * The underlying data store providing the paged data
21607      */
21608     /**
21609      * @cfg {String/HTMLElement/Element} container
21610      * container The id or element that will contain the toolbar
21611      */
21612     /**
21613      * @cfg {Boolean} displayInfo
21614      * True to display the displayMsg (defaults to false)
21615      */
21616     /**
21617      * @cfg {Number} pageSize
21618      * The number of records to display per page (defaults to 20)
21619      */
21620     pageSize: 20,
21621     /**
21622      * @cfg {String} displayMsg
21623      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21624      */
21625     displayMsg : 'Displaying {0} - {1} of {2}',
21626     /**
21627      * @cfg {String} emptyMsg
21628      * The message to display when no records are found (defaults to "No data to display")
21629      */
21630     emptyMsg : 'No data to display',
21631     /**
21632      * Customizable piece of the default paging text (defaults to "Page")
21633      * @type String
21634      */
21635     beforePageText : "Page",
21636     /**
21637      * Customizable piece of the default paging text (defaults to "of %0")
21638      * @type String
21639      */
21640     afterPageText : "of {0}",
21641     /**
21642      * Customizable piece of the default paging text (defaults to "First Page")
21643      * @type String
21644      */
21645     firstText : "First Page",
21646     /**
21647      * Customizable piece of the default paging text (defaults to "Previous Page")
21648      * @type String
21649      */
21650     prevText : "Previous Page",
21651     /**
21652      * Customizable piece of the default paging text (defaults to "Next Page")
21653      * @type String
21654      */
21655     nextText : "Next Page",
21656     /**
21657      * Customizable piece of the default paging text (defaults to "Last Page")
21658      * @type String
21659      */
21660     lastText : "Last Page",
21661     /**
21662      * Customizable piece of the default paging text (defaults to "Refresh")
21663      * @type String
21664      */
21665     refreshText : "Refresh",
21666
21667     buttons : false,
21668     // private
21669     onRender : function(ct, position) 
21670     {
21671         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21672         this.navgroup.parentId = this.id;
21673         this.navgroup.onRender(this.el, null);
21674         // add the buttons to the navgroup
21675         
21676         if(this.displayInfo){
21677             Roo.log(this.el.select('ul.navbar-nav',true).first());
21678             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21679             this.displayEl = this.el.select('.x-paging-info', true).first();
21680 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21681 //            this.displayEl = navel.el.select('span',true).first();
21682         }
21683         
21684         var _this = this;
21685         
21686         if(this.buttons){
21687             Roo.each(_this.buttons, function(e){
21688                Roo.factory(e).onRender(_this.el, null);
21689             });
21690         }
21691             
21692         Roo.each(_this.toolbarItems, function(e) {
21693             _this.navgroup.addItem(e);
21694         });
21695         
21696         
21697         this.first = this.navgroup.addItem({
21698             tooltip: this.firstText,
21699             cls: "prev",
21700             icon : 'fa fa-backward',
21701             disabled: true,
21702             preventDefault: true,
21703             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21704         });
21705         
21706         this.prev =  this.navgroup.addItem({
21707             tooltip: this.prevText,
21708             cls: "prev",
21709             icon : 'fa fa-step-backward',
21710             disabled: true,
21711             preventDefault: true,
21712             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21713         });
21714     //this.addSeparator();
21715         
21716         
21717         var field = this.navgroup.addItem( {
21718             tagtype : 'span',
21719             cls : 'x-paging-position',
21720             
21721             html : this.beforePageText  +
21722                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21723                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21724          } ); //?? escaped?
21725         
21726         this.field = field.el.select('input', true).first();
21727         this.field.on("keydown", this.onPagingKeydown, this);
21728         this.field.on("focus", function(){this.dom.select();});
21729     
21730     
21731         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21732         //this.field.setHeight(18);
21733         //this.addSeparator();
21734         this.next = this.navgroup.addItem({
21735             tooltip: this.nextText,
21736             cls: "next",
21737             html : ' <i class="fa fa-step-forward">',
21738             disabled: true,
21739             preventDefault: true,
21740             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21741         });
21742         this.last = this.navgroup.addItem({
21743             tooltip: this.lastText,
21744             icon : 'fa fa-forward',
21745             cls: "next",
21746             disabled: true,
21747             preventDefault: true,
21748             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21749         });
21750     //this.addSeparator();
21751         this.loading = this.navgroup.addItem({
21752             tooltip: this.refreshText,
21753             icon: 'fa fa-refresh',
21754             preventDefault: true,
21755             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21756         });
21757
21758     },
21759
21760     // private
21761     updateInfo : function(){
21762         if(this.displayEl){
21763             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21764             var msg = count == 0 ?
21765                 this.emptyMsg :
21766                 String.format(
21767                     this.displayMsg,
21768                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21769                 );
21770             this.displayEl.update(msg);
21771         }
21772     },
21773
21774     // private
21775     onLoad : function(ds, r, o){
21776        this.cursor = o.params ? o.params.start : 0;
21777        var d = this.getPageData(),
21778             ap = d.activePage,
21779             ps = d.pages;
21780         
21781        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21782        this.field.dom.value = ap;
21783        this.first.setDisabled(ap == 1);
21784        this.prev.setDisabled(ap == 1);
21785        this.next.setDisabled(ap == ps);
21786        this.last.setDisabled(ap == ps);
21787        this.loading.enable();
21788        this.updateInfo();
21789     },
21790
21791     // private
21792     getPageData : function(){
21793         var total = this.ds.getTotalCount();
21794         return {
21795             total : total,
21796             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21797             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21798         };
21799     },
21800
21801     // private
21802     onLoadError : function(){
21803         this.loading.enable();
21804     },
21805
21806     // private
21807     onPagingKeydown : function(e){
21808         var k = e.getKey();
21809         var d = this.getPageData();
21810         if(k == e.RETURN){
21811             var v = this.field.dom.value, pageNum;
21812             if(!v || isNaN(pageNum = parseInt(v, 10))){
21813                 this.field.dom.value = d.activePage;
21814                 return;
21815             }
21816             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21817             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21818             e.stopEvent();
21819         }
21820         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))
21821         {
21822           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21823           this.field.dom.value = pageNum;
21824           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21825           e.stopEvent();
21826         }
21827         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21828         {
21829           var v = this.field.dom.value, pageNum; 
21830           var increment = (e.shiftKey) ? 10 : 1;
21831           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21832             increment *= -1;
21833           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21834             this.field.dom.value = d.activePage;
21835             return;
21836           }
21837           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21838           {
21839             this.field.dom.value = parseInt(v, 10) + increment;
21840             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21841             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21842           }
21843           e.stopEvent();
21844         }
21845     },
21846
21847     // private
21848     beforeLoad : function(){
21849         if(this.loading){
21850             this.loading.disable();
21851         }
21852     },
21853
21854     // private
21855     onClick : function(which){
21856         
21857         var ds = this.ds;
21858         if (!ds) {
21859             return;
21860         }
21861         
21862         switch(which){
21863             case "first":
21864                 ds.load({params:{start: 0, limit: this.pageSize}});
21865             break;
21866             case "prev":
21867                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21868             break;
21869             case "next":
21870                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21871             break;
21872             case "last":
21873                 var total = ds.getTotalCount();
21874                 var extra = total % this.pageSize;
21875                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21876                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21877             break;
21878             case "refresh":
21879                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21880             break;
21881         }
21882     },
21883
21884     /**
21885      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21886      * @param {Roo.data.Store} store The data store to unbind
21887      */
21888     unbind : function(ds){
21889         ds.un("beforeload", this.beforeLoad, this);
21890         ds.un("load", this.onLoad, this);
21891         ds.un("loadexception", this.onLoadError, this);
21892         ds.un("remove", this.updateInfo, this);
21893         ds.un("add", this.updateInfo, this);
21894         this.ds = undefined;
21895     },
21896
21897     /**
21898      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21899      * @param {Roo.data.Store} store The data store to bind
21900      */
21901     bind : function(ds){
21902         ds.on("beforeload", this.beforeLoad, this);
21903         ds.on("load", this.onLoad, this);
21904         ds.on("loadexception", this.onLoadError, this);
21905         ds.on("remove", this.updateInfo, this);
21906         ds.on("add", this.updateInfo, this);
21907         this.ds = ds;
21908     }
21909 });/*
21910  * - LGPL
21911  *
21912  * element
21913  * 
21914  */
21915
21916 /**
21917  * @class Roo.bootstrap.MessageBar
21918  * @extends Roo.bootstrap.Component
21919  * Bootstrap MessageBar class
21920  * @cfg {String} html contents of the MessageBar
21921  * @cfg {String} weight (info | success | warning | danger) default info
21922  * @cfg {String} beforeClass insert the bar before the given class
21923  * @cfg {Boolean} closable (true | false) default false
21924  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21925  * 
21926  * @constructor
21927  * Create a new Element
21928  * @param {Object} config The config object
21929  */
21930
21931 Roo.bootstrap.MessageBar = function(config){
21932     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21933 };
21934
21935 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21936     
21937     html: '',
21938     weight: 'info',
21939     closable: false,
21940     fixed: false,
21941     beforeClass: 'bootstrap-sticky-wrap',
21942     
21943     getAutoCreate : function(){
21944         
21945         var cfg = {
21946             tag: 'div',
21947             cls: 'alert alert-dismissable alert-' + this.weight,
21948             cn: [
21949                 {
21950                     tag: 'span',
21951                     cls: 'message',
21952                     html: this.html || ''
21953                 }
21954             ]
21955         }
21956         
21957         if(this.fixed){
21958             cfg.cls += ' alert-messages-fixed';
21959         }
21960         
21961         if(this.closable){
21962             cfg.cn.push({
21963                 tag: 'button',
21964                 cls: 'close',
21965                 html: 'x'
21966             });
21967         }
21968         
21969         return cfg;
21970     },
21971     
21972     onRender : function(ct, position)
21973     {
21974         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21975         
21976         if(!this.el){
21977             var cfg = Roo.apply({},  this.getAutoCreate());
21978             cfg.id = Roo.id();
21979             
21980             if (this.cls) {
21981                 cfg.cls += ' ' + this.cls;
21982             }
21983             if (this.style) {
21984                 cfg.style = this.style;
21985             }
21986             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21987             
21988             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21989         }
21990         
21991         this.el.select('>button.close').on('click', this.hide, this);
21992         
21993     },
21994     
21995     show : function()
21996     {
21997         if (!this.rendered) {
21998             this.render();
21999         }
22000         
22001         this.el.show();
22002         
22003         this.fireEvent('show', this);
22004         
22005     },
22006     
22007     hide : function()
22008     {
22009         if (!this.rendered) {
22010             this.render();
22011         }
22012         
22013         this.el.hide();
22014         
22015         this.fireEvent('hide', this);
22016     },
22017     
22018     update : function()
22019     {
22020 //        var e = this.el.dom.firstChild;
22021 //        
22022 //        if(this.closable){
22023 //            e = e.nextSibling;
22024 //        }
22025 //        
22026 //        e.data = this.html || '';
22027
22028         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22029     }
22030    
22031 });
22032
22033  
22034
22035      /*
22036  * - LGPL
22037  *
22038  * Graph
22039  * 
22040  */
22041
22042
22043 /**
22044  * @class Roo.bootstrap.Graph
22045  * @extends Roo.bootstrap.Component
22046  * Bootstrap Graph class
22047 > Prameters
22048  -sm {number} sm 4
22049  -md {number} md 5
22050  @cfg {String} graphtype  bar | vbar | pie
22051  @cfg {number} g_x coodinator | centre x (pie)
22052  @cfg {number} g_y coodinator | centre y (pie)
22053  @cfg {number} g_r radius (pie)
22054  @cfg {number} g_height height of the chart (respected by all elements in the set)
22055  @cfg {number} g_width width of the chart (respected by all elements in the set)
22056  @cfg {Object} title The title of the chart
22057     
22058  -{Array}  values
22059  -opts (object) options for the chart 
22060      o {
22061      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22062      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22063      o vgutter (number)
22064      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.
22065      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22066      o to
22067      o stretch (boolean)
22068      o }
22069  -opts (object) options for the pie
22070      o{
22071      o cut
22072      o startAngle (number)
22073      o endAngle (number)
22074      } 
22075  *
22076  * @constructor
22077  * Create a new Input
22078  * @param {Object} config The config object
22079  */
22080
22081 Roo.bootstrap.Graph = function(config){
22082     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22083     
22084     this.addEvents({
22085         // img events
22086         /**
22087          * @event click
22088          * The img click event for the img.
22089          * @param {Roo.EventObject} e
22090          */
22091         "click" : true
22092     });
22093 };
22094
22095 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22096     
22097     sm: 4,
22098     md: 5,
22099     graphtype: 'bar',
22100     g_height: 250,
22101     g_width: 400,
22102     g_x: 50,
22103     g_y: 50,
22104     g_r: 30,
22105     opts:{
22106         //g_colors: this.colors,
22107         g_type: 'soft',
22108         g_gutter: '20%'
22109
22110     },
22111     title : false,
22112
22113     getAutoCreate : function(){
22114         
22115         var cfg = {
22116             tag: 'div',
22117             html : null
22118         }
22119         
22120         
22121         return  cfg;
22122     },
22123
22124     onRender : function(ct,position){
22125         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22126         this.raphael = Raphael(this.el.dom);
22127         
22128                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22129                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22130                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22131                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22132                 /*
22133                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22134                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22135                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22136                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22137                 
22138                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22139                 r.barchart(330, 10, 300, 220, data1);
22140                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22141                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22142                 */
22143                 
22144                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22145                 // r.barchart(30, 30, 560, 250,  xdata, {
22146                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22147                 //     axis : "0 0 1 1",
22148                 //     axisxlabels :  xdata
22149                 //     //yvalues : cols,
22150                    
22151                 // });
22152 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22153 //        
22154 //        this.load(null,xdata,{
22155 //                axis : "0 0 1 1",
22156 //                axisxlabels :  xdata
22157 //                });
22158
22159     },
22160
22161     load : function(graphtype,xdata,opts){
22162         this.raphael.clear();
22163         if(!graphtype) {
22164             graphtype = this.graphtype;
22165         }
22166         if(!opts){
22167             opts = this.opts;
22168         }
22169         var r = this.raphael,
22170             fin = function () {
22171                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22172             },
22173             fout = function () {
22174                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22175             },
22176             pfin = function() {
22177                 this.sector.stop();
22178                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22179
22180                 if (this.label) {
22181                     this.label[0].stop();
22182                     this.label[0].attr({ r: 7.5 });
22183                     this.label[1].attr({ "font-weight": 800 });
22184                 }
22185             },
22186             pfout = function() {
22187                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22188
22189                 if (this.label) {
22190                     this.label[0].animate({ r: 5 }, 500, "bounce");
22191                     this.label[1].attr({ "font-weight": 400 });
22192                 }
22193             };
22194
22195         switch(graphtype){
22196             case 'bar':
22197                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22198                 break;
22199             case 'hbar':
22200                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22201                 break;
22202             case 'pie':
22203 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22204 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22205 //            
22206                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22207                 
22208                 break;
22209
22210         }
22211         
22212         if(this.title){
22213             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22214         }
22215         
22216     },
22217     
22218     setTitle: function(o)
22219     {
22220         this.title = o;
22221     },
22222     
22223     initEvents: function() {
22224         
22225         if(!this.href){
22226             this.el.on('click', this.onClick, this);
22227         }
22228     },
22229     
22230     onClick : function(e)
22231     {
22232         Roo.log('img onclick');
22233         this.fireEvent('click', this, e);
22234     }
22235    
22236 });
22237
22238  
22239 /*
22240  * - LGPL
22241  *
22242  * numberBox
22243  * 
22244  */
22245 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22246
22247 /**
22248  * @class Roo.bootstrap.dash.NumberBox
22249  * @extends Roo.bootstrap.Component
22250  * Bootstrap NumberBox class
22251  * @cfg {String} headline Box headline
22252  * @cfg {String} content Box content
22253  * @cfg {String} icon Box icon
22254  * @cfg {String} footer Footer text
22255  * @cfg {String} fhref Footer href
22256  * 
22257  * @constructor
22258  * Create a new NumberBox
22259  * @param {Object} config The config object
22260  */
22261
22262
22263 Roo.bootstrap.dash.NumberBox = function(config){
22264     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22265     
22266 };
22267
22268 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22269     
22270     headline : '',
22271     content : '',
22272     icon : '',
22273     footer : '',
22274     fhref : '',
22275     ficon : '',
22276     
22277     getAutoCreate : function(){
22278         
22279         var cfg = {
22280             tag : 'div',
22281             cls : 'small-box ',
22282             cn : [
22283                 {
22284                     tag : 'div',
22285                     cls : 'inner',
22286                     cn :[
22287                         {
22288                             tag : 'h3',
22289                             cls : 'roo-headline',
22290                             html : this.headline
22291                         },
22292                         {
22293                             tag : 'p',
22294                             cls : 'roo-content',
22295                             html : this.content
22296                         }
22297                     ]
22298                 }
22299             ]
22300         }
22301         
22302         if(this.icon){
22303             cfg.cn.push({
22304                 tag : 'div',
22305                 cls : 'icon',
22306                 cn :[
22307                     {
22308                         tag : 'i',
22309                         cls : 'ion ' + this.icon
22310                     }
22311                 ]
22312             });
22313         }
22314         
22315         if(this.footer){
22316             var footer = {
22317                 tag : 'a',
22318                 cls : 'small-box-footer',
22319                 href : this.fhref || '#',
22320                 html : this.footer
22321             };
22322             
22323             cfg.cn.push(footer);
22324             
22325         }
22326         
22327         return  cfg;
22328     },
22329
22330     onRender : function(ct,position){
22331         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22332
22333
22334        
22335                 
22336     },
22337
22338     setHeadline: function (value)
22339     {
22340         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22341     },
22342     
22343     setFooter: function (value, href)
22344     {
22345         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22346         
22347         if(href){
22348             this.el.select('a.small-box-footer',true).first().attr('href', href);
22349         }
22350         
22351     },
22352
22353     setContent: function (value)
22354     {
22355         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22356     },
22357
22358     initEvents: function() 
22359     {   
22360         
22361     }
22362     
22363 });
22364
22365  
22366 /*
22367  * - LGPL
22368  *
22369  * TabBox
22370  * 
22371  */
22372 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22373
22374 /**
22375  * @class Roo.bootstrap.dash.TabBox
22376  * @extends Roo.bootstrap.Component
22377  * Bootstrap TabBox class
22378  * @cfg {String} title Title of the TabBox
22379  * @cfg {String} icon Icon of the TabBox
22380  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22381  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22382  * 
22383  * @constructor
22384  * Create a new TabBox
22385  * @param {Object} config The config object
22386  */
22387
22388
22389 Roo.bootstrap.dash.TabBox = function(config){
22390     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22391     this.addEvents({
22392         // raw events
22393         /**
22394          * @event addpane
22395          * When a pane is added
22396          * @param {Roo.bootstrap.dash.TabPane} pane
22397          */
22398         "addpane" : true,
22399         /**
22400          * @event activatepane
22401          * When a pane is activated
22402          * @param {Roo.bootstrap.dash.TabPane} pane
22403          */
22404         "activatepane" : true
22405         
22406          
22407     });
22408     
22409     this.panes = [];
22410 };
22411
22412 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22413
22414     title : '',
22415     icon : false,
22416     showtabs : true,
22417     tabScrollable : false,
22418     
22419     getChildContainer : function()
22420     {
22421         return this.el.select('.tab-content', true).first();
22422     },
22423     
22424     getAutoCreate : function(){
22425         
22426         var header = {
22427             tag: 'li',
22428             cls: 'pull-left header',
22429             html: this.title,
22430             cn : []
22431         };
22432         
22433         if(this.icon){
22434             header.cn.push({
22435                 tag: 'i',
22436                 cls: 'fa ' + this.icon
22437             });
22438         }
22439         
22440         var h = {
22441             tag: 'ul',
22442             cls: 'nav nav-tabs pull-right',
22443             cn: [
22444                 header
22445             ]
22446         };
22447         
22448         if(this.tabScrollable){
22449             h = {
22450                 tag: 'div',
22451                 cls: 'tab-header',
22452                 cn: [
22453                     {
22454                         tag: 'ul',
22455                         cls: 'nav nav-tabs pull-right',
22456                         cn: [
22457                             header
22458                         ]
22459                     }
22460                 ]
22461             }
22462         }
22463         
22464         var cfg = {
22465             tag: 'div',
22466             cls: 'nav-tabs-custom',
22467             cn: [
22468                 h,
22469                 {
22470                     tag: 'div',
22471                     cls: 'tab-content no-padding',
22472                     cn: []
22473                 }
22474             ]
22475         }
22476
22477         return  cfg;
22478     },
22479     initEvents : function()
22480     {
22481         //Roo.log('add add pane handler');
22482         this.on('addpane', this.onAddPane, this);
22483     },
22484      /**
22485      * Updates the box title
22486      * @param {String} html to set the title to.
22487      */
22488     setTitle : function(value)
22489     {
22490         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22491     },
22492     onAddPane : function(pane)
22493     {
22494         this.panes.push(pane);
22495         //Roo.log('addpane');
22496         //Roo.log(pane);
22497         // tabs are rendere left to right..
22498         if(!this.showtabs){
22499             return;
22500         }
22501         
22502         var ctr = this.el.select('.nav-tabs', true).first();
22503          
22504          
22505         var existing = ctr.select('.nav-tab',true);
22506         var qty = existing.getCount();;
22507         
22508         
22509         var tab = ctr.createChild({
22510             tag : 'li',
22511             cls : 'nav-tab' + (qty ? '' : ' active'),
22512             cn : [
22513                 {
22514                     tag : 'a',
22515                     href:'#',
22516                     html : pane.title
22517                 }
22518             ]
22519         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22520         pane.tab = tab;
22521         
22522         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22523         if (!qty) {
22524             pane.el.addClass('active');
22525         }
22526         
22527                 
22528     },
22529     onTabClick : function(ev,un,ob,pane)
22530     {
22531         //Roo.log('tab - prev default');
22532         ev.preventDefault();
22533         
22534         
22535         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22536         pane.tab.addClass('active');
22537         //Roo.log(pane.title);
22538         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22539         // technically we should have a deactivate event.. but maybe add later.
22540         // and it should not de-activate the selected tab...
22541         this.fireEvent('activatepane', pane);
22542         pane.el.addClass('active');
22543         pane.fireEvent('activate');
22544         
22545         
22546     },
22547     
22548     getActivePane : function()
22549     {
22550         var r = false;
22551         Roo.each(this.panes, function(p) {
22552             if(p.el.hasClass('active')){
22553                 r = p;
22554                 return false;
22555             }
22556             
22557             return;
22558         });
22559         
22560         return r;
22561     }
22562     
22563     
22564 });
22565
22566  
22567 /*
22568  * - LGPL
22569  *
22570  * Tab pane
22571  * 
22572  */
22573 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22574 /**
22575  * @class Roo.bootstrap.TabPane
22576  * @extends Roo.bootstrap.Component
22577  * Bootstrap TabPane class
22578  * @cfg {Boolean} active (false | true) Default false
22579  * @cfg {String} title title of panel
22580
22581  * 
22582  * @constructor
22583  * Create a new TabPane
22584  * @param {Object} config The config object
22585  */
22586
22587 Roo.bootstrap.dash.TabPane = function(config){
22588     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22589     
22590     this.addEvents({
22591         // raw events
22592         /**
22593          * @event activate
22594          * When a pane is activated
22595          * @param {Roo.bootstrap.dash.TabPane} pane
22596          */
22597         "activate" : true
22598          
22599     });
22600 };
22601
22602 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22603     
22604     active : false,
22605     title : '',
22606     
22607     // the tabBox that this is attached to.
22608     tab : false,
22609      
22610     getAutoCreate : function() 
22611     {
22612         var cfg = {
22613             tag: 'div',
22614             cls: 'tab-pane'
22615         }
22616         
22617         if(this.active){
22618             cfg.cls += ' active';
22619         }
22620         
22621         return cfg;
22622     },
22623     initEvents  : function()
22624     {
22625         //Roo.log('trigger add pane handler');
22626         this.parent().fireEvent('addpane', this)
22627     },
22628     
22629      /**
22630      * Updates the tab title 
22631      * @param {String} html to set the title to.
22632      */
22633     setTitle: function(str)
22634     {
22635         if (!this.tab) {
22636             return;
22637         }
22638         this.title = str;
22639         this.tab.select('a', true).first().dom.innerHTML = str;
22640         
22641     }
22642     
22643     
22644     
22645 });
22646
22647  
22648
22649
22650  /*
22651  * - LGPL
22652  *
22653  * menu
22654  * 
22655  */
22656 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22657
22658 /**
22659  * @class Roo.bootstrap.menu.Menu
22660  * @extends Roo.bootstrap.Component
22661  * Bootstrap Menu class - container for Menu
22662  * @cfg {String} html Text of the menu
22663  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22664  * @cfg {String} icon Font awesome icon
22665  * @cfg {String} pos Menu align to (top | bottom) default bottom
22666  * 
22667  * 
22668  * @constructor
22669  * Create a new Menu
22670  * @param {Object} config The config object
22671  */
22672
22673
22674 Roo.bootstrap.menu.Menu = function(config){
22675     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22676     
22677     this.addEvents({
22678         /**
22679          * @event beforeshow
22680          * Fires before this menu is displayed
22681          * @param {Roo.bootstrap.menu.Menu} this
22682          */
22683         beforeshow : true,
22684         /**
22685          * @event beforehide
22686          * Fires before this menu is hidden
22687          * @param {Roo.bootstrap.menu.Menu} this
22688          */
22689         beforehide : true,
22690         /**
22691          * @event show
22692          * Fires after this menu is displayed
22693          * @param {Roo.bootstrap.menu.Menu} this
22694          */
22695         show : true,
22696         /**
22697          * @event hide
22698          * Fires after this menu is hidden
22699          * @param {Roo.bootstrap.menu.Menu} this
22700          */
22701         hide : true,
22702         /**
22703          * @event click
22704          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22705          * @param {Roo.bootstrap.menu.Menu} this
22706          * @param {Roo.EventObject} e
22707          */
22708         click : true
22709     });
22710     
22711 };
22712
22713 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22714     
22715     submenu : false,
22716     html : '',
22717     weight : 'default',
22718     icon : false,
22719     pos : 'bottom',
22720     
22721     
22722     getChildContainer : function() {
22723         if(this.isSubMenu){
22724             return this.el;
22725         }
22726         
22727         return this.el.select('ul.dropdown-menu', true).first();  
22728     },
22729     
22730     getAutoCreate : function()
22731     {
22732         var text = [
22733             {
22734                 tag : 'span',
22735                 cls : 'roo-menu-text',
22736                 html : this.html
22737             }
22738         ];
22739         
22740         if(this.icon){
22741             text.unshift({
22742                 tag : 'i',
22743                 cls : 'fa ' + this.icon
22744             })
22745         }
22746         
22747         
22748         var cfg = {
22749             tag : 'div',
22750             cls : 'btn-group',
22751             cn : [
22752                 {
22753                     tag : 'button',
22754                     cls : 'dropdown-button btn btn-' + this.weight,
22755                     cn : text
22756                 },
22757                 {
22758                     tag : 'button',
22759                     cls : 'dropdown-toggle btn btn-' + this.weight,
22760                     cn : [
22761                         {
22762                             tag : 'span',
22763                             cls : 'caret'
22764                         }
22765                     ]
22766                 },
22767                 {
22768                     tag : 'ul',
22769                     cls : 'dropdown-menu'
22770                 }
22771             ]
22772             
22773         };
22774         
22775         if(this.pos == 'top'){
22776             cfg.cls += ' dropup';
22777         }
22778         
22779         if(this.isSubMenu){
22780             cfg = {
22781                 tag : 'ul',
22782                 cls : 'dropdown-menu'
22783             }
22784         }
22785         
22786         return cfg;
22787     },
22788     
22789     onRender : function(ct, position)
22790     {
22791         this.isSubMenu = ct.hasClass('dropdown-submenu');
22792         
22793         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22794     },
22795     
22796     initEvents : function() 
22797     {
22798         if(this.isSubMenu){
22799             return;
22800         }
22801         
22802         this.hidden = true;
22803         
22804         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22805         this.triggerEl.on('click', this.onTriggerPress, this);
22806         
22807         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22808         this.buttonEl.on('click', this.onClick, this);
22809         
22810     },
22811     
22812     list : function()
22813     {
22814         if(this.isSubMenu){
22815             return this.el;
22816         }
22817         
22818         return this.el.select('ul.dropdown-menu', true).first();
22819     },
22820     
22821     onClick : function(e)
22822     {
22823         this.fireEvent("click", this, e);
22824     },
22825     
22826     onTriggerPress  : function(e)
22827     {   
22828         if (this.isVisible()) {
22829             this.hide();
22830         } else {
22831             this.show();
22832         }
22833     },
22834     
22835     isVisible : function(){
22836         return !this.hidden;
22837     },
22838     
22839     show : function()
22840     {
22841         this.fireEvent("beforeshow", this);
22842         
22843         this.hidden = false;
22844         this.el.addClass('open');
22845         
22846         Roo.get(document).on("mouseup", this.onMouseUp, this);
22847         
22848         this.fireEvent("show", this);
22849         
22850         
22851     },
22852     
22853     hide : function()
22854     {
22855         this.fireEvent("beforehide", this);
22856         
22857         this.hidden = true;
22858         this.el.removeClass('open');
22859         
22860         Roo.get(document).un("mouseup", this.onMouseUp);
22861         
22862         this.fireEvent("hide", this);
22863     },
22864     
22865     onMouseUp : function()
22866     {
22867         this.hide();
22868     }
22869     
22870 });
22871
22872  
22873  /*
22874  * - LGPL
22875  *
22876  * menu item
22877  * 
22878  */
22879 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22880
22881 /**
22882  * @class Roo.bootstrap.menu.Item
22883  * @extends Roo.bootstrap.Component
22884  * Bootstrap MenuItem class
22885  * @cfg {Boolean} submenu (true | false) default false
22886  * @cfg {String} html text of the item
22887  * @cfg {String} href the link
22888  * @cfg {Boolean} disable (true | false) default false
22889  * @cfg {Boolean} preventDefault (true | false) default true
22890  * @cfg {String} icon Font awesome icon
22891  * @cfg {String} pos Submenu align to (left | right) default right 
22892  * 
22893  * 
22894  * @constructor
22895  * Create a new Item
22896  * @param {Object} config The config object
22897  */
22898
22899
22900 Roo.bootstrap.menu.Item = function(config){
22901     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22902     this.addEvents({
22903         /**
22904          * @event mouseover
22905          * Fires when the mouse is hovering over this menu
22906          * @param {Roo.bootstrap.menu.Item} this
22907          * @param {Roo.EventObject} e
22908          */
22909         mouseover : true,
22910         /**
22911          * @event mouseout
22912          * Fires when the mouse exits this menu
22913          * @param {Roo.bootstrap.menu.Item} this
22914          * @param {Roo.EventObject} e
22915          */
22916         mouseout : true,
22917         // raw events
22918         /**
22919          * @event click
22920          * The raw click event for the entire grid.
22921          * @param {Roo.EventObject} e
22922          */
22923         click : true
22924     });
22925 };
22926
22927 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22928     
22929     submenu : false,
22930     href : '',
22931     html : '',
22932     preventDefault: true,
22933     disable : false,
22934     icon : false,
22935     pos : 'right',
22936     
22937     getAutoCreate : function()
22938     {
22939         var text = [
22940             {
22941                 tag : 'span',
22942                 cls : 'roo-menu-item-text',
22943                 html : this.html
22944             }
22945         ];
22946         
22947         if(this.icon){
22948             text.unshift({
22949                 tag : 'i',
22950                 cls : 'fa ' + this.icon
22951             })
22952         }
22953         
22954         var cfg = {
22955             tag : 'li',
22956             cn : [
22957                 {
22958                     tag : 'a',
22959                     href : this.href || '#',
22960                     cn : text
22961                 }
22962             ]
22963         };
22964         
22965         if(this.disable){
22966             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22967         }
22968         
22969         if(this.submenu){
22970             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22971             
22972             if(this.pos == 'left'){
22973                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22974             }
22975         }
22976         
22977         return cfg;
22978     },
22979     
22980     initEvents : function() 
22981     {
22982         this.el.on('mouseover', this.onMouseOver, this);
22983         this.el.on('mouseout', this.onMouseOut, this);
22984         
22985         this.el.select('a', true).first().on('click', this.onClick, this);
22986         
22987     },
22988     
22989     onClick : function(e)
22990     {
22991         if(this.preventDefault){
22992             e.preventDefault();
22993         }
22994         
22995         this.fireEvent("click", this, e);
22996     },
22997     
22998     onMouseOver : function(e)
22999     {
23000         if(this.submenu && this.pos == 'left'){
23001             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23002         }
23003         
23004         this.fireEvent("mouseover", this, e);
23005     },
23006     
23007     onMouseOut : function(e)
23008     {
23009         this.fireEvent("mouseout", this, e);
23010     }
23011 });
23012
23013  
23014
23015  /*
23016  * - LGPL
23017  *
23018  * menu separator
23019  * 
23020  */
23021 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23022
23023 /**
23024  * @class Roo.bootstrap.menu.Separator
23025  * @extends Roo.bootstrap.Component
23026  * Bootstrap Separator class
23027  * 
23028  * @constructor
23029  * Create a new Separator
23030  * @param {Object} config The config object
23031  */
23032
23033
23034 Roo.bootstrap.menu.Separator = function(config){
23035     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23036 };
23037
23038 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23039     
23040     getAutoCreate : function(){
23041         var cfg = {
23042             tag : 'li',
23043             cls: 'divider'
23044         };
23045         
23046         return cfg;
23047     }
23048    
23049 });
23050
23051  
23052
23053  /*
23054  * - LGPL
23055  *
23056  * Tooltip
23057  * 
23058  */
23059
23060 /**
23061  * @class Roo.bootstrap.Tooltip
23062  * Bootstrap Tooltip class
23063  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23064  * to determine which dom element triggers the tooltip.
23065  * 
23066  * It needs to add support for additional attributes like tooltip-position
23067  * 
23068  * @constructor
23069  * Create a new Toolti
23070  * @param {Object} config The config object
23071  */
23072
23073 Roo.bootstrap.Tooltip = function(config){
23074     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23075 };
23076
23077 Roo.apply(Roo.bootstrap.Tooltip, {
23078     /**
23079      * @function init initialize tooltip monitoring.
23080      * @static
23081      */
23082     currentEl : false,
23083     currentTip : false,
23084     currentRegion : false,
23085     
23086     //  init : delay?
23087     
23088     init : function()
23089     {
23090         Roo.get(document).on('mouseover', this.enter ,this);
23091         Roo.get(document).on('mouseout', this.leave, this);
23092          
23093         
23094         this.currentTip = new Roo.bootstrap.Tooltip();
23095     },
23096     
23097     enter : function(ev)
23098     {
23099         var dom = ev.getTarget();
23100         
23101         //Roo.log(['enter',dom]);
23102         var el = Roo.fly(dom);
23103         if (this.currentEl) {
23104             //Roo.log(dom);
23105             //Roo.log(this.currentEl);
23106             //Roo.log(this.currentEl.contains(dom));
23107             if (this.currentEl == el) {
23108                 return;
23109             }
23110             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23111                 return;
23112             }
23113
23114         }
23115         
23116         
23117         
23118         if (this.currentTip.el) {
23119             this.currentTip.el.hide(); // force hiding...
23120         }    
23121         //Roo.log(ev);
23122         var bindEl = el;
23123         
23124         // you can not look for children, as if el is the body.. then everythign is the child..
23125         if (!el.attr('tooltip')) { //
23126             if (!el.select("[tooltip]").elements.length) {
23127                 return;
23128             }
23129             // is the mouse over this child...?
23130             bindEl = el.select("[tooltip]").first();
23131             var xy = ev.getXY();
23132             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23133                 //Roo.log("not in region.");
23134                 return;
23135             }
23136             //Roo.log("child element over..");
23137             
23138         }
23139         this.currentEl = bindEl;
23140         this.currentTip.bind(bindEl);
23141         this.currentRegion = Roo.lib.Region.getRegion(dom);
23142         this.currentTip.enter();
23143         
23144     },
23145     leave : function(ev)
23146     {
23147         var dom = ev.getTarget();
23148         //Roo.log(['leave',dom]);
23149         if (!this.currentEl) {
23150             return;
23151         }
23152         
23153         
23154         if (dom != this.currentEl.dom) {
23155             return;
23156         }
23157         var xy = ev.getXY();
23158         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23159             return;
23160         }
23161         // only activate leave if mouse cursor is outside... bounding box..
23162         
23163         
23164         
23165         
23166         if (this.currentTip) {
23167             this.currentTip.leave();
23168         }
23169         //Roo.log('clear currentEl');
23170         this.currentEl = false;
23171         
23172         
23173     },
23174     alignment : {
23175         'left' : ['r-l', [-2,0], 'right'],
23176         'right' : ['l-r', [2,0], 'left'],
23177         'bottom' : ['t-b', [0,2], 'top'],
23178         'top' : [ 'b-t', [0,-2], 'bottom']
23179     }
23180     
23181 });
23182
23183
23184 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23185     
23186     
23187     bindEl : false,
23188     
23189     delay : null, // can be { show : 300 , hide: 500}
23190     
23191     timeout : null,
23192     
23193     hoverState : null, //???
23194     
23195     placement : 'bottom', 
23196     
23197     getAutoCreate : function(){
23198     
23199         var cfg = {
23200            cls : 'tooltip',
23201            role : 'tooltip',
23202            cn : [
23203                 {
23204                     cls : 'tooltip-arrow'
23205                 },
23206                 {
23207                     cls : 'tooltip-inner'
23208                 }
23209            ]
23210         };
23211         
23212         return cfg;
23213     },
23214     bind : function(el)
23215     {
23216         this.bindEl = el;
23217     },
23218       
23219     
23220     enter : function () {
23221        
23222         if (this.timeout != null) {
23223             clearTimeout(this.timeout);
23224         }
23225         
23226         this.hoverState = 'in';
23227          //Roo.log("enter - show");
23228         if (!this.delay || !this.delay.show) {
23229             this.show();
23230             return;
23231         }
23232         var _t = this;
23233         this.timeout = setTimeout(function () {
23234             if (_t.hoverState == 'in') {
23235                 _t.show();
23236             }
23237         }, this.delay.show);
23238     },
23239     leave : function()
23240     {
23241         clearTimeout(this.timeout);
23242     
23243         this.hoverState = 'out';
23244          if (!this.delay || !this.delay.hide) {
23245             this.hide();
23246             return;
23247         }
23248        
23249         var _t = this;
23250         this.timeout = setTimeout(function () {
23251             //Roo.log("leave - timeout");
23252             
23253             if (_t.hoverState == 'out') {
23254                 _t.hide();
23255                 Roo.bootstrap.Tooltip.currentEl = false;
23256             }
23257         }, delay);
23258     },
23259     
23260     show : function ()
23261     {
23262         if (!this.el) {
23263             this.render(document.body);
23264         }
23265         // set content.
23266         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23267         
23268         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23269         
23270         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23271         
23272         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23273         
23274         var placement = typeof this.placement == 'function' ?
23275             this.placement.call(this, this.el, on_el) :
23276             this.placement;
23277             
23278         var autoToken = /\s?auto?\s?/i;
23279         var autoPlace = autoToken.test(placement);
23280         if (autoPlace) {
23281             placement = placement.replace(autoToken, '') || 'top';
23282         }
23283         
23284         //this.el.detach()
23285         //this.el.setXY([0,0]);
23286         this.el.show();
23287         //this.el.dom.style.display='block';
23288         this.el.addClass(placement);
23289         
23290         //this.el.appendTo(on_el);
23291         
23292         var p = this.getPosition();
23293         var box = this.el.getBox();
23294         
23295         if (autoPlace) {
23296             // fixme..
23297         }
23298         var align = Roo.bootstrap.Tooltip.alignment[placement];
23299         this.el.alignTo(this.bindEl, align[0],align[1]);
23300         //var arrow = this.el.select('.arrow',true).first();
23301         //arrow.set(align[2], 
23302         
23303         this.el.addClass('in fade');
23304         this.hoverState = null;
23305         
23306         if (this.el.hasClass('fade')) {
23307             // fade it?
23308         }
23309         
23310     },
23311     hide : function()
23312     {
23313          
23314         if (!this.el) {
23315             return;
23316         }
23317         //this.el.setXY([0,0]);
23318         this.el.removeClass('in');
23319         //this.el.hide();
23320         
23321     }
23322     
23323 });
23324  
23325
23326  /*
23327  * - LGPL
23328  *
23329  * Location Picker
23330  * 
23331  */
23332
23333 /**
23334  * @class Roo.bootstrap.LocationPicker
23335  * @extends Roo.bootstrap.Component
23336  * Bootstrap LocationPicker class
23337  * @cfg {Number} latitude Position when init default 0
23338  * @cfg {Number} longitude Position when init default 0
23339  * @cfg {Number} zoom default 15
23340  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23341  * @cfg {Boolean} mapTypeControl default false
23342  * @cfg {Boolean} disableDoubleClickZoom default false
23343  * @cfg {Boolean} scrollwheel default true
23344  * @cfg {Boolean} streetViewControl default false
23345  * @cfg {Number} radius default 0
23346  * @cfg {String} locationName
23347  * @cfg {Boolean} draggable default true
23348  * @cfg {Boolean} enableAutocomplete default false
23349  * @cfg {Boolean} enableReverseGeocode default true
23350  * @cfg {String} markerTitle
23351  * 
23352  * @constructor
23353  * Create a new LocationPicker
23354  * @param {Object} config The config object
23355  */
23356
23357
23358 Roo.bootstrap.LocationPicker = function(config){
23359     
23360     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23361     
23362     this.addEvents({
23363         /**
23364          * @event initial
23365          * Fires when the picker initialized.
23366          * @param {Roo.bootstrap.LocationPicker} this
23367          * @param {Google Location} location
23368          */
23369         initial : true,
23370         /**
23371          * @event positionchanged
23372          * Fires when the picker position changed.
23373          * @param {Roo.bootstrap.LocationPicker} this
23374          * @param {Google Location} location
23375          */
23376         positionchanged : true,
23377         /**
23378          * @event resize
23379          * Fires when the map resize.
23380          * @param {Roo.bootstrap.LocationPicker} this
23381          */
23382         resize : true,
23383         /**
23384          * @event show
23385          * Fires when the map show.
23386          * @param {Roo.bootstrap.LocationPicker} this
23387          */
23388         show : true,
23389         /**
23390          * @event hide
23391          * Fires when the map hide.
23392          * @param {Roo.bootstrap.LocationPicker} this
23393          */
23394         hide : true,
23395         /**
23396          * @event mapClick
23397          * Fires when click the map.
23398          * @param {Roo.bootstrap.LocationPicker} this
23399          * @param {Map event} e
23400          */
23401         mapClick : true,
23402         /**
23403          * @event mapRightClick
23404          * Fires when right click the map.
23405          * @param {Roo.bootstrap.LocationPicker} this
23406          * @param {Map event} e
23407          */
23408         mapRightClick : true,
23409         /**
23410          * @event markerClick
23411          * Fires when click the marker.
23412          * @param {Roo.bootstrap.LocationPicker} this
23413          * @param {Map event} e
23414          */
23415         markerClick : true,
23416         /**
23417          * @event markerRightClick
23418          * Fires when right click the marker.
23419          * @param {Roo.bootstrap.LocationPicker} this
23420          * @param {Map event} e
23421          */
23422         markerRightClick : true,
23423         /**
23424          * @event OverlayViewDraw
23425          * Fires when OverlayView Draw
23426          * @param {Roo.bootstrap.LocationPicker} this
23427          */
23428         OverlayViewDraw : true,
23429         /**
23430          * @event OverlayViewOnAdd
23431          * Fires when OverlayView Draw
23432          * @param {Roo.bootstrap.LocationPicker} this
23433          */
23434         OverlayViewOnAdd : true,
23435         /**
23436          * @event OverlayViewOnRemove
23437          * Fires when OverlayView Draw
23438          * @param {Roo.bootstrap.LocationPicker} this
23439          */
23440         OverlayViewOnRemove : true,
23441         /**
23442          * @event OverlayViewShow
23443          * Fires when OverlayView Draw
23444          * @param {Roo.bootstrap.LocationPicker} this
23445          * @param {Pixel} cpx
23446          */
23447         OverlayViewShow : true,
23448         /**
23449          * @event OverlayViewHide
23450          * Fires when OverlayView Draw
23451          * @param {Roo.bootstrap.LocationPicker} this
23452          */
23453         OverlayViewHide : true
23454     });
23455         
23456 };
23457
23458 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23459     
23460     gMapContext: false,
23461     
23462     latitude: 0,
23463     longitude: 0,
23464     zoom: 15,
23465     mapTypeId: false,
23466     mapTypeControl: false,
23467     disableDoubleClickZoom: false,
23468     scrollwheel: true,
23469     streetViewControl: false,
23470     radius: 0,
23471     locationName: '',
23472     draggable: true,
23473     enableAutocomplete: false,
23474     enableReverseGeocode: true,
23475     markerTitle: '',
23476     
23477     getAutoCreate: function()
23478     {
23479
23480         var cfg = {
23481             tag: 'div',
23482             cls: 'roo-location-picker'
23483         };
23484         
23485         return cfg
23486     },
23487     
23488     initEvents: function(ct, position)
23489     {       
23490         if(!this.el.getWidth() || this.isApplied()){
23491             return;
23492         }
23493         
23494         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23495         
23496         this.initial();
23497     },
23498     
23499     initial: function()
23500     {
23501         if(!this.mapTypeId){
23502             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23503         }
23504         
23505         this.gMapContext = this.GMapContext();
23506         
23507         this.initOverlayView();
23508         
23509         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23510         
23511         var _this = this;
23512                 
23513         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23514             _this.setPosition(_this.gMapContext.marker.position);
23515         });
23516         
23517         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23518             _this.fireEvent('mapClick', this, event);
23519             
23520         });
23521
23522         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23523             _this.fireEvent('mapRightClick', this, event);
23524             
23525         });
23526         
23527         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23528             _this.fireEvent('markerClick', this, event);
23529             
23530         });
23531
23532         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23533             _this.fireEvent('markerRightClick', this, event);
23534             
23535         });
23536         
23537         this.setPosition(this.gMapContext.location);
23538         
23539         this.fireEvent('initial', this, this.gMapContext.location);
23540     },
23541     
23542     initOverlayView: function()
23543     {
23544         var _this = this;
23545         
23546         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23547             
23548             draw: function()
23549             {
23550                 _this.fireEvent('OverlayViewDraw', _this);
23551             },
23552             
23553             onAdd: function()
23554             {
23555                 _this.fireEvent('OverlayViewOnAdd', _this);
23556             },
23557             
23558             onRemove: function()
23559             {
23560                 _this.fireEvent('OverlayViewOnRemove', _this);
23561             },
23562             
23563             show: function(cpx)
23564             {
23565                 _this.fireEvent('OverlayViewShow', _this, cpx);
23566             },
23567             
23568             hide: function()
23569             {
23570                 _this.fireEvent('OverlayViewHide', _this);
23571             }
23572             
23573         });
23574     },
23575     
23576     fromLatLngToContainerPixel: function(event)
23577     {
23578         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23579     },
23580     
23581     isApplied: function() 
23582     {
23583         return this.getGmapContext() == false ? false : true;
23584     },
23585     
23586     getGmapContext: function() 
23587     {
23588         return this.gMapContext
23589     },
23590     
23591     GMapContext: function() 
23592     {
23593         var position = new google.maps.LatLng(this.latitude, this.longitude);
23594         
23595         var _map = new google.maps.Map(this.el.dom, {
23596             center: position,
23597             zoom: this.zoom,
23598             mapTypeId: this.mapTypeId,
23599             mapTypeControl: this.mapTypeControl,
23600             disableDoubleClickZoom: this.disableDoubleClickZoom,
23601             scrollwheel: this.scrollwheel,
23602             streetViewControl: this.streetViewControl,
23603             locationName: this.locationName,
23604             draggable: this.draggable,
23605             enableAutocomplete: this.enableAutocomplete,
23606             enableReverseGeocode: this.enableReverseGeocode
23607         });
23608         
23609         var _marker = new google.maps.Marker({
23610             position: position,
23611             map: _map,
23612             title: this.markerTitle,
23613             draggable: this.draggable
23614         });
23615         
23616         return {
23617             map: _map,
23618             marker: _marker,
23619             circle: null,
23620             location: position,
23621             radius: this.radius,
23622             locationName: this.locationName,
23623             addressComponents: {
23624                 formatted_address: null,
23625                 addressLine1: null,
23626                 addressLine2: null,
23627                 streetName: null,
23628                 streetNumber: null,
23629                 city: null,
23630                 district: null,
23631                 state: null,
23632                 stateOrProvince: null
23633             },
23634             settings: this,
23635             domContainer: this.el.dom,
23636             geodecoder: new google.maps.Geocoder()
23637         };
23638     },
23639     
23640     drawCircle: function(center, radius, options) 
23641     {
23642         if (this.gMapContext.circle != null) {
23643             this.gMapContext.circle.setMap(null);
23644         }
23645         if (radius > 0) {
23646             radius *= 1;
23647             options = Roo.apply({}, options, {
23648                 strokeColor: "#0000FF",
23649                 strokeOpacity: .35,
23650                 strokeWeight: 2,
23651                 fillColor: "#0000FF",
23652                 fillOpacity: .2
23653             });
23654             
23655             options.map = this.gMapContext.map;
23656             options.radius = radius;
23657             options.center = center;
23658             this.gMapContext.circle = new google.maps.Circle(options);
23659             return this.gMapContext.circle;
23660         }
23661         
23662         return null;
23663     },
23664     
23665     setPosition: function(location) 
23666     {
23667         this.gMapContext.location = location;
23668         this.gMapContext.marker.setPosition(location);
23669         this.gMapContext.map.panTo(location);
23670         this.drawCircle(location, this.gMapContext.radius, {});
23671         
23672         var _this = this;
23673         
23674         if (this.gMapContext.settings.enableReverseGeocode) {
23675             this.gMapContext.geodecoder.geocode({
23676                 latLng: this.gMapContext.location
23677             }, function(results, status) {
23678                 
23679                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23680                     _this.gMapContext.locationName = results[0].formatted_address;
23681                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23682                     
23683                     _this.fireEvent('positionchanged', this, location);
23684                 }
23685             });
23686             
23687             return;
23688         }
23689         
23690         this.fireEvent('positionchanged', this, location);
23691     },
23692     
23693     resize: function()
23694     {
23695         google.maps.event.trigger(this.gMapContext.map, "resize");
23696         
23697         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23698         
23699         this.fireEvent('resize', this);
23700     },
23701     
23702     setPositionByLatLng: function(latitude, longitude)
23703     {
23704         this.setPosition(new google.maps.LatLng(latitude, longitude));
23705     },
23706     
23707     getCurrentPosition: function() 
23708     {
23709         return {
23710             latitude: this.gMapContext.location.lat(),
23711             longitude: this.gMapContext.location.lng()
23712         };
23713     },
23714     
23715     getAddressName: function() 
23716     {
23717         return this.gMapContext.locationName;
23718     },
23719     
23720     getAddressComponents: function() 
23721     {
23722         return this.gMapContext.addressComponents;
23723     },
23724     
23725     address_component_from_google_geocode: function(address_components) 
23726     {
23727         var result = {};
23728         
23729         for (var i = 0; i < address_components.length; i++) {
23730             var component = address_components[i];
23731             if (component.types.indexOf("postal_code") >= 0) {
23732                 result.postalCode = component.short_name;
23733             } else if (component.types.indexOf("street_number") >= 0) {
23734                 result.streetNumber = component.short_name;
23735             } else if (component.types.indexOf("route") >= 0) {
23736                 result.streetName = component.short_name;
23737             } else if (component.types.indexOf("neighborhood") >= 0) {
23738                 result.city = component.short_name;
23739             } else if (component.types.indexOf("locality") >= 0) {
23740                 result.city = component.short_name;
23741             } else if (component.types.indexOf("sublocality") >= 0) {
23742                 result.district = component.short_name;
23743             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23744                 result.stateOrProvince = component.short_name;
23745             } else if (component.types.indexOf("country") >= 0) {
23746                 result.country = component.short_name;
23747             }
23748         }
23749         
23750         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23751         result.addressLine2 = "";
23752         return result;
23753     },
23754     
23755     setZoomLevel: function(zoom)
23756     {
23757         this.gMapContext.map.setZoom(zoom);
23758     },
23759     
23760     show: function()
23761     {
23762         if(!this.el){
23763             return;
23764         }
23765         
23766         this.el.show();
23767         
23768         this.resize();
23769         
23770         this.fireEvent('show', this);
23771     },
23772     
23773     hide: function()
23774     {
23775         if(!this.el){
23776             return;
23777         }
23778         
23779         this.el.hide();
23780         
23781         this.fireEvent('hide', this);
23782     }
23783     
23784 });
23785
23786 Roo.apply(Roo.bootstrap.LocationPicker, {
23787     
23788     OverlayView : function(map, options)
23789     {
23790         options = options || {};
23791         
23792         this.setMap(map);
23793     }
23794     
23795     
23796 });/*
23797  * - LGPL
23798  *
23799  * Alert
23800  * 
23801  */
23802
23803 /**
23804  * @class Roo.bootstrap.Alert
23805  * @extends Roo.bootstrap.Component
23806  * Bootstrap Alert class
23807  * @cfg {String} title The title of alert
23808  * @cfg {String} html The content of alert
23809  * @cfg {String} weight (  success | info | warning | danger )
23810  * @cfg {String} faicon font-awesomeicon
23811  * 
23812  * @constructor
23813  * Create a new alert
23814  * @param {Object} config The config object
23815  */
23816
23817
23818 Roo.bootstrap.Alert = function(config){
23819     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23820     
23821 };
23822
23823 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23824     
23825     title: '',
23826     html: '',
23827     weight: false,
23828     faicon: false,
23829     
23830     getAutoCreate : function()
23831     {
23832         
23833         var cfg = {
23834             tag : 'div',
23835             cls : 'alert',
23836             cn : [
23837                 {
23838                     tag : 'i',
23839                     cls : 'roo-alert-icon'
23840                     
23841                 },
23842                 {
23843                     tag : 'b',
23844                     cls : 'roo-alert-title',
23845                     html : this.title
23846                 },
23847                 {
23848                     tag : 'span',
23849                     cls : 'roo-alert-text',
23850                     html : this.html
23851                 }
23852             ]
23853         };
23854         
23855         if(this.faicon){
23856             cfg.cn[0].cls += ' fa ' + this.faicon;
23857         }
23858         
23859         if(this.weight){
23860             cfg.cls += ' alert-' + this.weight;
23861         }
23862         
23863         return cfg;
23864     },
23865     
23866     initEvents: function() 
23867     {
23868         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23869     },
23870     
23871     setTitle : function(str)
23872     {
23873         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23874     },
23875     
23876     setText : function(str)
23877     {
23878         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23879     },
23880     
23881     setWeight : function(weight)
23882     {
23883         if(this.weight){
23884             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23885         }
23886         
23887         this.weight = weight;
23888         
23889         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23890     },
23891     
23892     setIcon : function(icon)
23893     {
23894         if(this.faicon){
23895             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23896         }
23897         
23898         this.faicon = icon
23899         
23900         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23901     },
23902     
23903     hide: function() 
23904     {
23905         this.el.hide();   
23906     },
23907     
23908     show: function() 
23909     {  
23910         this.el.show();   
23911     }
23912     
23913 });
23914
23915